| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License") + you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.openmeetings.webservice; |
| |
| import static org.apache.openmeetings.db.dto.basic.ServiceResult.UNKNOWN; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.getDefaultGroup; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.getDefaultTimezone; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.isAllowRegisterSoap; |
| import static org.apache.openmeetings.webservice.Constants.TNS; |
| import static org.apache.openmeetings.webservice.Constants.USER_SERVICE_NAME; |
| import static org.apache.openmeetings.webservice.Constants.USER_SERVICE_PORT_NAME; |
| |
| import java.security.NoSuchAlgorithmException; |
| import java.util.List; |
| import java.util.Locale; |
| |
| import javax.jws.WebMethod; |
| import javax.jws.WebParam; |
| import javax.jws.WebService; |
| import javax.ws.rs.DELETE; |
| import javax.ws.rs.FormParam; |
| import javax.ws.rs.GET; |
| import javax.ws.rs.POST; |
| import javax.ws.rs.Path; |
| import javax.ws.rs.PathParam; |
| import javax.ws.rs.Produces; |
| import javax.ws.rs.QueryParam; |
| import javax.ws.rs.core.MediaType; |
| |
| import org.apache.cxf.feature.Features; |
| import org.apache.openmeetings.core.util.StrongPasswordValidator; |
| import org.apache.openmeetings.db.dao.server.SOAPLoginDao; |
| import org.apache.openmeetings.db.dao.user.GroupDao; |
| import org.apache.openmeetings.db.dao.user.IUserManager; |
| import org.apache.openmeetings.db.dto.basic.ServiceResult; |
| import org.apache.openmeetings.db.dto.basic.ServiceResult.Type; |
| import org.apache.openmeetings.db.dto.room.RoomOptionsDTO; |
| import org.apache.openmeetings.db.dto.user.ExternalUserDTO; |
| import org.apache.openmeetings.db.dto.user.UserDTO; |
| import org.apache.openmeetings.db.entity.server.RemoteSessionObject; |
| import org.apache.openmeetings.db.entity.server.Sessiondata; |
| import org.apache.openmeetings.db.entity.user.Address; |
| import org.apache.openmeetings.db.entity.user.User; |
| import org.apache.openmeetings.db.entity.user.User.Right; |
| import org.apache.openmeetings.db.mapper.UserMapper; |
| import org.apache.openmeetings.util.OmException; |
| import org.apache.openmeetings.webservice.error.InternalServiceException; |
| import org.apache.openmeetings.webservice.error.ServiceException; |
| import org.apache.openmeetings.webservice.schema.ServiceResultWrapper; |
| import org.apache.openmeetings.webservice.schema.UserDTOListWrapper; |
| import org.apache.openmeetings.webservice.schema.UserDTOWrapper; |
| import org.apache.wicket.util.string.Strings; |
| import org.apache.wicket.validation.IValidationError; |
| import org.apache.wicket.validation.IValidator; |
| import org.apache.wicket.validation.Validatable; |
| import org.apache.wicket.validation.ValidationError; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.stereotype.Service; |
| |
| import io.swagger.v3.oas.annotations.Operation; |
| import io.swagger.v3.oas.annotations.Parameter; |
| import io.swagger.v3.oas.annotations.media.Content; |
| import io.swagger.v3.oas.annotations.media.Schema; |
| import io.swagger.v3.oas.annotations.responses.ApiResponse; |
| import io.swagger.v3.oas.annotations.tags.Tag; |
| |
| /** |
| * |
| * The Service contains methods to login and create hash to directly enter |
| * conference rooms, recordings or the application in general |
| * |
| * @author sebawagner |
| * |
| */ |
| @Service("userWebService") |
| @WebService(serviceName = USER_SERVICE_NAME, targetNamespace = TNS, portName = USER_SERVICE_PORT_NAME) |
| @Features(features = "org.apache.cxf.ext.logging.LoggingFeature") |
| @Produces({MediaType.APPLICATION_JSON}) |
| @Tag(name = "UserService") |
| @Path("/user") |
| public class UserWebService extends BaseWebService { |
| private static final Logger log = LoggerFactory.getLogger(UserWebService.class); |
| |
| @Autowired |
| private IUserManager userManager; |
| @Autowired |
| private SOAPLoginDao soapDao; |
| @Autowired |
| private GroupDao groupDao; |
| @Autowired |
| private UserMapper uMapper; |
| |
| /** |
| * @param user - login or email of Openmeetings user with admin or SOAP-rights |
| * @param pass - password |
| * |
| * @return - {@link ServiceResult} with error code or SID and userId |
| */ |
| @WebMethod |
| @GET |
| @Path("/login") |
| @Operation( |
| description = "Login and create sessionId required for sub-sequent calls", |
| responses = { |
| @ApiResponse(responseCode = "200", description = "ServiceResult with error code or SID and userId", |
| content = @Content(schema = @Schema(implementation = ServiceResultWrapper.class))), |
| @ApiResponse(responseCode = "500", description = "Error of server error") |
| } |
| ) |
| public ServiceResult login( |
| @Parameter(required = true, description = "login or email of Openmeetings user with admin or SOAP-rights") @WebParam(name="user") @QueryParam("user") String user |
| , @Parameter(required = true, description = "password") @WebParam(name="pass") @QueryParam("pass") String pass) { |
| try { |
| log.debug("Login user"); |
| User u = userDao.login(user, pass); |
| if (u == null) { |
| return new ServiceResult("error.bad.credentials", Type.ERROR); |
| } |
| |
| Sessiondata sd = sessionDao.create(u.getId(), u.getLanguageId()); |
| log.debug("Login user: {}", u.getId()); |
| return new ServiceResult(sd.getSessionId(), Type.SUCCESS); |
| } catch (OmException oe) { |
| return oe.getKey() == null ? UNKNOWN : new ServiceResult(oe.getKey(), Type.ERROR); |
| } catch (Exception err) { |
| log.error("[login]", err); |
| return UNKNOWN; |
| } |
| } |
| |
| /** |
| * Lists all users in the system! |
| * |
| * @param sid |
| * The SID from getSession |
| * |
| * @return - list of users |
| * @throws {@link ServiceException} in case of any errors |
| */ |
| @WebMethod |
| @GET |
| @Path("/") |
| @Operation( |
| description = "Lists all users in the system!", |
| responses = { |
| @ApiResponse(responseCode = "200", description = "list of users", |
| content = @Content(schema = @Schema(implementation = UserDTOListWrapper.class))), |
| @ApiResponse(responseCode = "500", description = "Error in case of invalid credentials or server error") |
| } |
| ) |
| public List<UserDTO> get( |
| @Parameter(required = true, description = "The SID of the User. This SID must be marked as Loggedin") @WebParam(name="sid") @QueryParam("sid") String sid |
| ) throws ServiceException { |
| return performCall(sid, User.Right.SOAP, sd -> UserDTO.list(userDao.getAllUsers())); |
| } |
| |
| /** |
| * Adds a new User like through the Frontend, but also does activates the |
| * Account To do SSO see the methods to create a hash and use those ones! |
| * |
| * @param sid |
| * The SID from getSession |
| * @param user |
| * user object |
| * @param confirm |
| * whatever or not to send email, leave empty for auto-send |
| * |
| * @return - id of the user added or error code |
| * @throws {@link ServiceException} in case of any errors |
| */ |
| @WebMethod |
| @POST |
| @Path("/") |
| @Operation( |
| description = """ |
| Adds a new User like through the Frontend, but also does activates the |
| Account To do SSO see the methods to create a hash and use those ones!""", |
| responses = { |
| @ApiResponse(responseCode = "200", description = "list of users", |
| content = @Content(schema = @Schema(implementation = UserDTOWrapper.class))), |
| @ApiResponse(responseCode = "500", description = "Error in case of invalid credentials or server error") |
| } |
| ) |
| public UserDTO add( |
| @Parameter(required = true, description = "The SID of the User. This SID must be marked as Loggedin") @WebParam(name="sid") @QueryParam("sid") String sid |
| , @Parameter(required = true, description = "user object") @WebParam(name="user") @FormParam("user") UserDTO user |
| , @Parameter(required = true, description = "whatever or not to send email, leave empty for auto-send") @WebParam(name="confirm") @FormParam("confirm") Boolean confirm |
| ) throws ServiceException |
| { |
| return performCall(sid, User.Right.SOAP, sd -> { |
| if (!isAllowRegisterSoap()) { |
| throw new InternalServiceException("Soap register is denied in Settings"); |
| } |
| User testUser = userDao.getExternalUser(user.getExternalId(), user.getExternalType()); |
| |
| if (testUser != null) { |
| throw new InternalServiceException("User does already exist!"); |
| } |
| |
| String tz = user.getTimeZoneId(); |
| if (Strings.isEmpty(tz)) { |
| tz = getDefaultTimezone(); |
| } |
| if (user.getAddress() == null) { |
| user.setAddress(new Address()); |
| user.getAddress().setCountry(Locale.getDefault().getCountry()); |
| } |
| if (user.getLanguageId() == null) { |
| user.setLanguageId(1L); |
| } |
| User jsonUser = uMapper.get(user); |
| IValidator<String> passValidator = new StrongPasswordValidator(false, jsonUser); |
| Validatable<String> passVal = new Validatable<>(user.getPassword()); |
| passValidator.validate(passVal); |
| if (!passVal.isValid()) { |
| StringBuilder sb = new StringBuilder(); |
| for (IValidationError err : passVal.getErrors()) { |
| sb.append(((ValidationError)err).getMessage()).append(System.lineSeparator()); |
| } |
| log.debug("addNewUser::weak password '{}', msg: {}", user.getPassword(), sb); |
| throw new InternalServiceException(sb.toString()); |
| } |
| Object ouser; |
| try { |
| jsonUser.addGroup(groupDao.get(getDefaultGroup())); |
| ouser = userManager.registerUser(jsonUser, user.getPassword(), null); |
| } catch (NoSuchAlgorithmException | OmException e) { |
| throw new InternalServiceException("Unexpected error while creating user"); |
| } |
| |
| if (ouser == null) { |
| throw new InternalServiceException(UNKNOWN.getMessage()); |
| } else if (ouser instanceof String str) { |
| throw new InternalServiceException(str); |
| } |
| |
| User u = (User)ouser; |
| |
| u.getRights().add(Right.ROOM); |
| if (Strings.isEmpty(user.getExternalId()) && Strings.isEmpty(user.getExternalType())) { |
| // activate the User |
| u.getRights().add(Right.LOGIN); |
| u.getRights().add(Right.DASHBOARD); |
| } |
| |
| u = userDao.update(u, sd.getUserId()); |
| |
| return new UserDTO(u); |
| }); |
| } |
| |
| /** |
| * |
| * Delete a certain user by its id |
| * |
| * @param sid |
| * The SID from getSession |
| * @param id |
| * the openmeetings user id |
| * |
| * @return - id of the user deleted, error code otherwise |
| * @throws {@link ServiceException} in case of any errors |
| */ |
| @WebMethod |
| @DELETE |
| @Path("/{id}") |
| @Operation( |
| description = "Delete a certain user by its id", |
| responses = { |
| @ApiResponse(responseCode = "200", description = "id of the user deleted, error code otherwise", |
| content = @Content(schema = @Schema(implementation = ServiceResultWrapper.class))), |
| @ApiResponse(responseCode = "500", description = "Error in case of invalid credentials or server error") |
| } |
| ) |
| public ServiceResult delete( |
| @Parameter(required = true, description = "The SID of the User. This SID must be marked as Loggedin") @WebParam(name="sid") @QueryParam("sid") String sid |
| , @Parameter(required = true, description = "the openmeetings user id") @WebParam(name="id") @PathParam("id") long id |
| ) throws ServiceException |
| { |
| return performCall(sid, User.Right.ADMIN, sd -> { |
| userDao.delete(userDao.get(id), sd.getUserId()); |
| |
| return new ServiceResult("Deleted", Type.SUCCESS); |
| }); |
| } |
| |
| /** |
| * |
| * Delete a certain user by its external user id |
| * |
| * @param sid |
| * The SID from getSession |
| * @param externalId |
| * externalUserId |
| * @param externalType |
| * externalType |
| * |
| * @return - id of user deleted, or error code |
| * @throws {@link ServiceException} in case of any errors |
| */ |
| @DELETE |
| @Path("/{externaltype}/{externalid}") |
| @Operation( |
| description = "Delete a certain user by its external user id", |
| responses = { |
| @ApiResponse(responseCode = "200", description = "id of user deleted, or error code", |
| content = @Content(schema = @Schema(implementation = ServiceResultWrapper.class))), |
| @ApiResponse(responseCode = "500", description = "Error in case of invalid credentials or server error") |
| } |
| ) |
| public ServiceResult deleteExternal( |
| @Parameter(required = true, description = "The SID of the User. This SID must be marked as Loggedin") @WebParam(name="sid") @QueryParam("sid") String sid |
| , @Parameter(required = true, description = "externalUserId") @WebParam(name="externaltype") @PathParam("externaltype") String externalType |
| , @Parameter(required = true, description = "externalType") @WebParam(name="externalid") @PathParam("externalid") String externalId |
| ) throws ServiceException |
| { |
| return performCall(sid, User.Right.ADMIN, sd -> { |
| User user = userDao.getExternalUser(externalId, externalType); |
| |
| // Setting user deleted |
| userDao.delete(user, sd.getUserId()); |
| |
| return new ServiceResult("Deleted", Type.SUCCESS); |
| }); |
| } |
| |
| /** |
| * Sets the SessionObject for a certain SID, after setting this |
| * Session-Object you can use the SID + a RoomId to enter any Room. ... |
| * Session-Hashs are deleted 15 minutes after the creation if not used. |
| * |
| * @param sid |
| * The SID from getSession |
| * @param user |
| * user details to set |
| * @param options |
| * room options to set |
| * |
| * @return - secure hash or error code |
| * @throws {@link ServiceException} in case of any errors |
| */ |
| @WebMethod |
| @POST |
| @Path("/hash") |
| @Operation( |
| description = """ |
| Sets the SessionObject for a certain SID, after setting this |
| Session-Object you can use the SID + a RoomId to enter any Room. ... |
| Session-Hashs are deleted 15 minutes after the creation if not used.""", |
| responses = { |
| @ApiResponse(responseCode = "200", description = "secure hash or error code", |
| content = @Content(schema = @Schema(implementation = ServiceResultWrapper.class))), |
| @ApiResponse(responseCode = "500", description = "Error in case of invalid credentials or server error") |
| } |
| ) |
| public ServiceResult getRoomHash( |
| @Parameter(required = true, description = "The SID of the User. This SID must be marked as Loggedin") @WebParam(name="sid") @QueryParam("sid") String sid |
| , @Parameter(required = true, description = "user details to set") @WebParam(name="user") @FormParam("user") ExternalUserDTO user |
| , @Parameter(required = true, description = "room options to set") @WebParam(name="options") @FormParam("options") RoomOptionsDTO options |
| ) throws ServiceException |
| { |
| return performCall(sid, User.Right.SOAP, sd -> { |
| if (Strings.isEmpty(user.getExternalId()) || Strings.isEmpty(user.getExternalType())) { |
| return new ServiceResult("externalId and/or externalType are not set", Type.ERROR); |
| } |
| RemoteSessionObject remoteSessionObject = new RemoteSessionObject(user); |
| |
| log.debug(remoteSessionObject.toString()); |
| |
| String xmlString = remoteSessionObject.toString(); |
| |
| log.debug("jsonString {}", xmlString); |
| |
| String hash = soapDao.addSOAPLogin(sid, options); |
| |
| if (hash != null) { |
| if (options.isAllowSameURLMultipleTimes()) { |
| sd.setPermanent(true); |
| } |
| sd.setXml(xmlString); |
| sd = sessionDao.update(sd); |
| return new ServiceResult(hash, Type.SUCCESS); |
| } |
| return UNKNOWN; |
| }); |
| } |
| } |