From 93b5bec443d9b5b43f811c5b9d00b2b9e9c2f2ce Mon Sep 17 00:00:00 2001 From: pujavs Date: Fri, 10 Mar 2023 12:02:40 +0530 Subject: [PATCH 1/4] fix(config-api): customObjectClass changes --- .../docs/jans-config-api-swagger.yaml | 24 +- .../plugin/mgt/rest/UserResource.java | 739 +++++++++--------- .../plugin/mgt/service/UserMgmtService.java | 611 ++++++++------- 3 files changed, 712 insertions(+), 662 deletions(-) diff --git a/jans-config-api/docs/jans-config-api-swagger.yaml b/jans-config-api/docs/jans-config-api-swagger.yaml index a5e952e3f82..9114747ffea 100644 --- a/jans-config-api/docs/jans-config-api-swagger.yaml +++ b/jans-config-api/docs/jans-config-api-swagger.yaml @@ -7577,16 +7577,16 @@ components: type: boolean adminCanView: type: boolean - adminCanEdit: - type: boolean userCanAccess: type: boolean userCanView: type: boolean - whitePagesCanView: + adminCanEdit: type: boolean userCanEdit: type: boolean + whitePagesCanView: + type: boolean baseDn: type: string PatchRequest: @@ -8854,6 +8854,20 @@ components: - tls_client_auth - self_signed_tls_client_auth - none + allAuthenticationMethods: + uniqueItems: true + type: array + items: + type: string + enum: + - client_secret_basic + - client_secret_post + - client_secret_jwt + - private_key_jwt + - access_token + - tls_client_auth + - self_signed_tls_client_auth + - none baseDn: type: string inum: @@ -8942,6 +8956,10 @@ components: format: int32 minimumAcrLevelAutoresolve: type: boolean + additionalTokenEndpointAuthMethods: + type: array + items: + type: string minimumAcrPriorityList: type: array items: diff --git a/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/rest/UserResource.java b/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/rest/UserResource.java index 6ba10d39923..8a2395c4047 100644 --- a/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/rest/UserResource.java +++ b/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/rest/UserResource.java @@ -47,365 +47,384 @@ @ApplicationScoped public class UserResource extends BaseResource { - private static final String USER = "user"; - private static final String MAIL = "mail"; - private static final String DISPLAY_NAME = "displayName"; - private static final String JANS_STATUS = "jansStatus"; - private static final String GIVEN_NAME = "givenName"; - private static final String USER_PWD = "userPassword"; - private static final String INUM = "inum"; - private class UserPagedResult extends PagedResult{}; - - @Inject - Logger logger; - - @Inject - MgtUtil mgtUtil; - - @Inject - UserMgmtService userMgmtSrv; - - @Operation(summary = "Gets list of users", description = "Gets list of users", operationId = "get-user", tags = { - "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.USER_READ_ACCESS })) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = UserPagedResult.class), examples = @ExampleObject(name = "Response json example", value = "example/user/user-all.json"))), - @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "500", description = "InternalServerError") }) - @GET - @ProtectedApi(scopes = { ApiAccessConstants.USER_READ_ACCESS }) - public Response getUsers( - @Parameter(description = "Search size - max size of the results to return") @DefaultValue(ApiConstants.DEFAULT_LIST_SIZE) @QueryParam(value = ApiConstants.LIMIT) int limit, - @Parameter(description = "Search pattern") @DefaultValue("") @QueryParam(value = ApiConstants.PATTERN) String pattern, - @Parameter(description = "The 1-based index of the first query result") @DefaultValue(ApiConstants.DEFAULT_LIST_START_INDEX) @QueryParam(value = ApiConstants.START_INDEX) int startIndex, - @Parameter(description = "Attribute whose value will be used to order the returned response") @QueryParam(value = ApiConstants.SORT_BY) String sortBy, - @Parameter(description = "Order in which the sortBy param is applied. Allowed values are \"ascending\" and \"descending\"") @QueryParam(value = ApiConstants.SORT_ORDER) String sortOrder) - throws IllegalAccessException, InvocationTargetException { - if (logger.isDebugEnabled()) { - logger.debug("User search param - limit:{}, pattern:{}, startIndex:{}, sortBy:{}, sortOrder:{}", - escapeLog(limit), escapeLog(pattern), escapeLog(startIndex), escapeLog(sortBy), - escapeLog(sortOrder)); - } - SearchRequest searchReq = createSearchRequest(userMgmtSrv.getPeopleBaseDn(), pattern, sortBy, sortOrder, - startIndex, limit, null, userMgmtSrv.getUserExclusionAttributesAsString(), mgtUtil.getRecordMaxCount()); - - return Response.ok(this.doSearch(searchReq)).build(); - } - - @Operation(summary = "Get User by Inum", description = "Get User by Inum", operationId = "get-user-by-inum", tags = { - "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.USER_READ_ACCESS })) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class, description = "CustomUser identified by inum"), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), - @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "404", description = "Not Found"), - @ApiResponse(responseCode = "500", description = "InternalServerError") }) - @GET - @ProtectedApi(scopes = { ApiAccessConstants.USER_READ_ACCESS }) - @Path(ApiConstants.INUM_PATH) - public Response getUserByInum(@Parameter(description = "User identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) - throws IllegalAccessException, InvocationTargetException { - if (logger.isDebugEnabled()) { - logger.debug("User search by inum:{}", escapeLog(inum)); - } - User user = userMgmtSrv.getUserBasedOnInum(inum); - checkResourceNotNull(user, USER); - logger.debug("user:{}", user); - - // excludedAttributes - user = excludeUserAttributes(user); - logger.debug("user:{}", user); - - // get custom user - CustomUser customUser = getCustomUser(user); - logger.debug("customUser:{}", customUser); - - return Response.ok(customUser).build(); - } - - @Operation(summary = "Create new User", description = "Create new User", operationId = "post-user", tags = { - "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.USER_WRITE_ACCESS })) - @RequestBody(description = "User object", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user-post.json"))) - @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "Created", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class, description = "Created Object"), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), - @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "500", description = "InternalServerError") }) - @POST - @ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS }) - public Response createUser(@Valid CustomUser customUser) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - if (logger.isDebugEnabled()) { - logger.debug("User details to be added - customUser:{}", escapeLog(customUser)); - } - - // get User object - User user = setUserAttributes(customUser); - - // parse birthdate if present - userMgmtSrv.parseBirthDateAttribute(user); - logger.debug("Create user:{}", user); - - // checking mandatory attributes - checkMissingAttributes(user, null); - ignoreCustomObjectClassesForNonLDAP(user); - - user = userMgmtSrv.addUser(user, true); - logger.debug("User created {}", user); - - // excludedAttributes - user = excludeUserAttributes(user); - - // get custom user - customUser = getCustomUser(user); - logger.debug("newly created customUser:{}", customUser); - - return Response.status(Response.Status.CREATED).entity(customUser).build(); - } - - @Operation(summary = "Update User", description = "Update User", operationId = "put-user", tags = { - "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.USER_WRITE_ACCESS })) - @RequestBody(description = "User object", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user.json"))) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), - @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "404", description = "Not Found"), - @ApiResponse(responseCode = "500", description = "InternalServerError") }) - @PUT - @ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS }) - public Response updateUser(@Valid CustomUser customUser) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - if (logger.isDebugEnabled()) { - logger.debug("User details to be updated - customUser:{}", escapeLog(customUser)); - } - - // get User object - User user = setUserAttributes(customUser); - - // parse birthdate if present - userMgmtSrv.parseBirthDateAttribute(user); - logger.debug("Create user:{}", user); - - // checking mandatory attributes - List excludeAttributes = List.of(USER_PWD); - checkMissingAttributes(user, excludeAttributes); - ignoreCustomObjectClassesForNonLDAP(user); - try { - user = userMgmtSrv.updateUser(user); - logger.debug("Updated user:{}", user); - } catch (Exception ex) { - logger.error("Error while updating user", ex); - throwInternalServerException(ex); - } - - // excludedAttributes - user = excludeUserAttributes(user); - - // get custom user - customUser = getCustomUser(user); - logger.debug("updated customUser:{}", customUser); - - return Response.ok(customUser).build(); - - } - - @Operation(summary = "Patch user properties by Inum", description = "Patch user properties by Inum", operationId = "patch-user-by-inum", tags = { - "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.USER_WRITE_ACCESS })) - @RequestBody(description = "UserPatchRequest", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = UserPatchRequest.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user-patch.json"))) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class, description = "Patched CustomUser Object"), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), - @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "404", description = "Not Found"), - @ApiResponse(responseCode = "500", description = "InternalServerError") }) - @PATCH - @ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS }) - @Path(ApiConstants.INUM_PATH) - public Response patchUser(@Parameter(description = "User identifier") @PathParam(ApiConstants.INUM) @NotNull String inum, - @NotNull UserPatchRequest userPatchRequest) - throws IllegalAccessException, InvocationTargetException, JsonPatchException, IOException { - if (logger.isDebugEnabled()) { - logger.debug("User:{} to be patched with :{} ", escapeLog(inum), escapeLog(userPatchRequest)); - } - // check if user exists - User existingUser = userMgmtSrv.getUserBasedOnInum(inum); - - // parse birthdate if present - userMgmtSrv.parseBirthDateAttribute(existingUser); - checkResourceNotNull(existingUser, USER); - ignoreCustomObjectClassesForNonLDAP(existingUser); - - // patch user - existingUser = userMgmtSrv.patchUser(inum, userPatchRequest); - logger.debug("Patched user:{}", existingUser); - - // excludedAttributes - existingUser = excludeUserAttributes(existingUser); - - // get custom user - CustomUser customUser = getCustomUser(existingUser); - logger.debug("patched customUser:{}", customUser); - - return Response.ok(customUser).build(); - } - - @Operation(summary = "Delete User", description = "Delete User", operationId = "delete-user", tags = { - "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.USER_DELETE_ACCESS })) - @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "No Content"), - @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "404", description = "Not Found"), - @ApiResponse(responseCode = "500", description = "InternalServerError") }) - @DELETE - @Path(ApiConstants.INUM_PATH) - @ProtectedApi(scopes = { ApiAccessConstants.USER_DELETE_ACCESS }) - public Response deleteUser(@Parameter(description = "User identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) { - if (logger.isDebugEnabled()) { - logger.debug("User to be deleted - inum:{} ", escapeLog(inum)); - } - User user = userMgmtSrv.getUserBasedOnInum(inum); - checkResourceNotNull(user, USER); - userMgmtSrv.removeUser(user); - return Response.noContent().build(); - } - - private UserPagedResult doSearch(SearchRequest searchReq) throws IllegalAccessException, InvocationTargetException { - if (logger.isDebugEnabled()) { - logger.debug("User search params - searchReq:{} ", escapeLog(searchReq)); - } - - PagedResult pagedResult = userMgmtSrv.searchUsers(searchReq); - if (logger.isTraceEnabled()) { - logger.debug("PagedResult - pagedResult:{}", pagedResult); - } - - UserPagedResult pagedCustomUser = new UserPagedResult(); - if (pagedResult != null) { - logger.debug("Users fetched - pagedResult.getEntries():{}", pagedResult.getEntries()); - List users = pagedResult.getEntries(); - - // excludedAttributes - users = userMgmtSrv.excludeAttributes(users, searchReq.getExcludedAttributesStr()); - logger.debug("Users fetched - users:{}", users); - - // parse birthdate if present - users = users.stream().map(user -> userMgmtSrv.parseBirthDateAttribute(user)).collect(Collectors.toList()); - - // get customUser() - List customUsers = getCustomUserList(users); - pagedCustomUser.setStart(pagedResult.getStart()); - pagedCustomUser.setEntriesCount(pagedResult.getEntriesCount()); - pagedCustomUser.setTotalEntriesCount(pagedResult.getTotalEntriesCount()); - pagedCustomUser.setEntries(customUsers); - } - - logger.debug("User pagedCustomUser:{}", pagedCustomUser); - return pagedCustomUser; - - } - - private User excludeUserAttributes(User user) throws IllegalAccessException, InvocationTargetException { - return userMgmtSrv.excludeAttributes(user, userMgmtSrv.getUserExclusionAttributesAsString()); - } - - private void checkMissingAttributes(User user, List excludeAttributes) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - String missingAttributes = userMgmtSrv.checkMandatoryFields(user, excludeAttributes); - logger.debug("missingAttributes:{}", missingAttributes); - - if (StringHelper.isEmpty(missingAttributes)) { - return; - } - - throwMissingAttributeError(missingAttributes); - } - - private List getCustomUserList(List users) { - List customUserList = new ArrayList<>(); - if (users == null || users.isEmpty()) { - return customUserList; - } - - for (User user : users) { - CustomUser customUser = new CustomUser(); - setParentAttributes(customUser, user); - customUserList.add(customUser); - ignoreCustomObjectClassesForNonLDAP(customUser); - } - logger.debug("Custom Users - customUserList:{}", customUserList); - return customUserList; - } - - private CustomUser getCustomUser(User user) { - CustomUser customUser = new CustomUser(); - if (user == null) { - return customUser; - } - setParentAttributes(customUser, user); - logger.debug("Custom User - customUser:{}", customUser); - return customUser; - } - - public CustomUser setParentAttributes(CustomUser customUser, User user) { - customUser.setBaseDn(user.getBaseDn()); - customUser.setCreatedAt(user.getCreatedAt()); - customUser.setCustomAttributes(user.getCustomAttributes()); - customUser.setCustomObjectClasses(user.getCustomObjectClasses()); - customUser.setDn(user.getDn()); - customUser.setOxAuthPersistentJwt(user.getOxAuthPersistentJwt()); - customUser.setUpdatedAt(user.getUpdatedAt()); - customUser.setUserId(user.getUserId()); - - ignoreCustomObjectClassesForNonLDAP(customUser); - return setCustomUserAttributes(customUser, user); - } - - public CustomUser setCustomUserAttributes(CustomUser customUser, User user) { - customUser.setMail(user.getAttribute(MAIL)); - customUser.setDisplayName(user.getAttribute(DISPLAY_NAME)); - customUser.setJansStatus(user.getAttribute(JANS_STATUS)); - customUser.setGivenName(user.getAttribute(GIVEN_NAME)); - customUser.setUserPassword(user.getAttribute(USER_PWD)); - customUser.setInum(user.getAttribute(INUM)); - - customUser.removeAttribute(MAIL); - customUser.removeAttribute(DISPLAY_NAME); - customUser.removeAttribute(JANS_STATUS); - customUser.removeAttribute(GIVEN_NAME); - customUser.removeAttribute(USER_PWD); - customUser.removeAttribute(INUM); - - return customUser; - } - - private User setUserAttributes(CustomUser customUser) { - User user = new User(); - user.setBaseDn(customUser.getBaseDn()); - user.setCreatedAt(customUser.getCreatedAt()); - user.setCustomAttributes(customUser.getCustomAttributes()); - user.setCustomObjectClasses(customUser.getCustomObjectClasses()); - user.setDn(customUser.getDn()); - user.setOxAuthPersistentJwt(customUser.getOxAuthPersistentJwt()); - user.setUpdatedAt(customUser.getUpdatedAt()); - user.setUserId(customUser.getUserId()); - return setUserCustomAttributes(customUser, user); - } - - private User setUserCustomAttributes(CustomUser customUser, User user) { - user.setAttribute(MAIL, customUser.getMail(), false); - user.setAttribute(DISPLAY_NAME, customUser.getDisplayName(), false); - user.setAttribute(JANS_STATUS, customUser.getJansStatus(), false); - user.setAttribute(GIVEN_NAME, customUser.getGivenName(), false); - user.setAttribute(USER_PWD, customUser.getUserPassword(), false); - user.setAttribute(INUM, customUser.getInum(), false); - - logger.debug("Custom User - user:{}", user); - return user; - } - - private User ignoreCustomObjectClassesForNonLDAP(User user) { - return userMgmtSrv.ignoreCustomObjectClassesForNonLDAP(user); - } + private static final String USER = "user"; + private static final String MAIL = "mail"; + private static final String DISPLAY_NAME = "displayName"; + private static final String JANS_STATUS = "jansStatus"; + private static final String GIVEN_NAME = "givenName"; + private static final String USER_PWD = "userPassword"; + private static final String INUM = "inum"; + + private class UserPagedResult extends PagedResult { + }; + + @Inject + Logger logger; + + @Inject + MgtUtil mgtUtil; + + @Inject + UserMgmtService userMgmtSrv; + + @Operation(summary = "Gets list of users", description = "Gets list of users", operationId = "get-user", tags = { + "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { + ApiAccessConstants.USER_READ_ACCESS })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = UserPagedResult.class), examples = @ExampleObject(name = "Response json example", value = "example/user/user-all.json"))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @GET + @ProtectedApi(scopes = { ApiAccessConstants.USER_READ_ACCESS }) + public Response getUsers( + @Parameter(description = "Search size - max size of the results to return") @DefaultValue(ApiConstants.DEFAULT_LIST_SIZE) @QueryParam(value = ApiConstants.LIMIT) int limit, + @Parameter(description = "Search pattern") @DefaultValue("") @QueryParam(value = ApiConstants.PATTERN) String pattern, + @Parameter(description = "The 1-based index of the first query result") @DefaultValue(ApiConstants.DEFAULT_LIST_START_INDEX) @QueryParam(value = ApiConstants.START_INDEX) int startIndex, + @Parameter(description = "Attribute whose value will be used to order the returned response") @QueryParam(value = ApiConstants.SORT_BY) String sortBy, + @Parameter(description = "Order in which the sortBy param is applied. Allowed values are \"ascending\" and \"descending\"") @QueryParam(value = ApiConstants.SORT_ORDER) String sortOrder) + throws IllegalAccessException, InvocationTargetException { + if (logger.isDebugEnabled()) { + logger.debug("User search param - limit:{}, pattern:{}, startIndex:{}, sortBy:{}, sortOrder:{}", + escapeLog(limit), escapeLog(pattern), escapeLog(startIndex), escapeLog(sortBy), + escapeLog(sortOrder)); + } + SearchRequest searchReq = createSearchRequest(userMgmtSrv.getPeopleBaseDn(), pattern, sortBy, sortOrder, + startIndex, limit, null, userMgmtSrv.getUserExclusionAttributesAsString(), mgtUtil.getRecordMaxCount()); + + return Response.ok(this.doSearch(searchReq)).build(); + } + + @Operation(summary = "Get User by Inum", description = "Get User by Inum", operationId = "get-user-by-inum", tags = { + "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { + ApiAccessConstants.USER_READ_ACCESS })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class, description = "CustomUser identified by inum"), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "404", description = "Not Found"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @GET + @ProtectedApi(scopes = { ApiAccessConstants.USER_READ_ACCESS }) + @Path(ApiConstants.INUM_PATH) + public Response getUserByInum( + @Parameter(description = "User identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) + throws IllegalAccessException, InvocationTargetException { + if (logger.isDebugEnabled()) { + logger.debug("User search by inum:{}", escapeLog(inum)); + } + User user = userMgmtSrv.getUserBasedOnInum(inum); + checkResourceNotNull(user, USER); + logger.debug("user:{}", user); + + // excludedAttributes + user = excludeUserAttributes(user); + logger.debug("user:{}", user); + + // get custom user + CustomUser customUser = getCustomUser(user); + logger.debug("customUser:{}", customUser); + + return Response.ok(customUser).build(); + } + + @Operation(summary = "Create new User", description = "Create new User", operationId = "post-user", tags = { + "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { + ApiAccessConstants.USER_WRITE_ACCESS })) + @RequestBody(description = "User object", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user-post.json"))) + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "Created", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class, description = "Created Object"), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @POST + @ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS }) + public Response createUser(@Valid CustomUser customUser) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + if (logger.isDebugEnabled()) { + logger.debug("User details to be added - customUser:{}", escapeLog(customUser)); + } + + // get User object + User user = setUserAttributes(customUser); + + // parse birthdate if present + userMgmtSrv.parseBirthDateAttribute(user); + logger.debug("Create user:{}", user); + + // checking mandatory attributes + checkMissingAttributes(user, null); + validateCustomObjectClasses(user); + + user = userMgmtSrv.addUser(user, true); + logger.debug("User created {}", user); + + // excludedAttributes + user = excludeUserAttributes(user); + + // get custom user + customUser = getCustomUser(user); + logger.debug("newly created customUser:{}", customUser); + + return Response.status(Response.Status.CREATED).entity(customUser).build(); + } + + @Operation(summary = "Update User", description = "Update User", operationId = "put-user", tags = { + "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { + ApiAccessConstants.USER_WRITE_ACCESS })) + @RequestBody(description = "User object", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user.json"))) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "404", description = "Not Found"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @PUT + @ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS }) + public Response updateUser(@Valid CustomUser customUser) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + if (logger.isDebugEnabled()) { + logger.debug("User details to be updated - customUser:{}", escapeLog(customUser)); + } + + // get User object + User user = setUserAttributes(customUser); + + // parse birthdate if present + userMgmtSrv.parseBirthDateAttribute(user); + logger.debug("Create user:{}", user); + + // checking mandatory attributes + List excludeAttributes = List.of(USER_PWD); + checkMissingAttributes(user, excludeAttributes); + validateCustomObjectClasses(user); + try { + user = userMgmtSrv.updateUser(user); + logger.debug("Updated user:{}", user); + } catch (Exception ex) { + logger.error("Error while updating user", ex); + throwInternalServerException(ex); + } + + // excludedAttributes + user = excludeUserAttributes(user); + + // get custom user + customUser = getCustomUser(user); + logger.debug("updated customUser:{}", customUser); + + return Response.ok(customUser).build(); + + } + + @Operation(summary = "Patch user properties by Inum", description = "Patch user properties by Inum", operationId = "patch-user-by-inum", tags = { + "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { + ApiAccessConstants.USER_WRITE_ACCESS })) + @RequestBody(description = "UserPatchRequest", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = UserPatchRequest.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user-patch.json"))) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class, description = "Patched CustomUser Object"), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "404", description = "Not Found"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @PATCH + @ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS }) + @Path(ApiConstants.INUM_PATH) + public Response patchUser( + @Parameter(description = "User identifier") @PathParam(ApiConstants.INUM) @NotNull String inum, + @NotNull UserPatchRequest userPatchRequest) + throws IllegalAccessException, InvocationTargetException, JsonPatchException, IOException { + if (logger.isDebugEnabled()) { + logger.debug("User:{} to be patched with :{} ", escapeLog(inum), escapeLog(userPatchRequest)); + } + // check if user exists + User existingUser = userMgmtSrv.getUserBasedOnInum(inum); + + // parse birthdate if present + userMgmtSrv.parseBirthDateAttribute(existingUser); + checkResourceNotNull(existingUser, USER); + validateCustomObjectClasses(existingUser); + + // patch user + existingUser = userMgmtSrv.patchUser(inum, userPatchRequest); + logger.debug("Patched user:{}", existingUser); + + // excludedAttributes + existingUser = excludeUserAttributes(existingUser); + + // get custom user + CustomUser customUser = getCustomUser(existingUser); + logger.debug("patched customUser:{}", customUser); + + return Response.ok(customUser).build(); + } + + @Operation(summary = "Delete User", description = "Delete User", operationId = "delete-user", tags = { + "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { + ApiAccessConstants.USER_DELETE_ACCESS })) + @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "No Content"), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "404", description = "Not Found"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @DELETE + @Path(ApiConstants.INUM_PATH) + @ProtectedApi(scopes = { ApiAccessConstants.USER_DELETE_ACCESS }) + public Response deleteUser( + @Parameter(description = "User identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) { + if (logger.isDebugEnabled()) { + logger.debug("User to be deleted - inum:{} ", escapeLog(inum)); + } + User user = userMgmtSrv.getUserBasedOnInum(inum); + checkResourceNotNull(user, USER); + userMgmtSrv.removeUser(user); + return Response.noContent().build(); + } + + private UserPagedResult doSearch(SearchRequest searchReq) throws IllegalAccessException, InvocationTargetException { + if (logger.isDebugEnabled()) { + logger.debug("User search params - searchReq:{} ", escapeLog(searchReq)); + } + + PagedResult pagedResult = userMgmtSrv.searchUsers(searchReq); + if (logger.isTraceEnabled()) { + logger.debug("PagedResult - pagedResult:{}", pagedResult); + } + + UserPagedResult pagedCustomUser = new UserPagedResult(); + if (pagedResult != null) { + logger.debug("Users fetched - pagedResult.getEntries():{}", pagedResult.getEntries()); + List users = pagedResult.getEntries(); + + // excludedAttributes + users = userMgmtSrv.excludeAttributes(users, searchReq.getExcludedAttributesStr()); + logger.debug("Users fetched - users:{}", users); + + // parse birthdate if present + users = users.stream().map(user -> userMgmtSrv.parseBirthDateAttribute(user)).collect(Collectors.toList()); + + // get customUser() + List customUsers = getCustomUserList(users); + pagedCustomUser.setStart(pagedResult.getStart()); + pagedCustomUser.setEntriesCount(pagedResult.getEntriesCount()); + pagedCustomUser.setTotalEntriesCount(pagedResult.getTotalEntriesCount()); + pagedCustomUser.setEntries(customUsers); + } + + logger.debug("User pagedCustomUser:{}", pagedCustomUser); + return pagedCustomUser; + + } + + private User excludeUserAttributes(User user) throws IllegalAccessException, InvocationTargetException { + return userMgmtSrv.excludeAttributes(user, userMgmtSrv.getUserExclusionAttributesAsString()); + } + + private void checkMissingAttributes(User user, List excludeAttributes) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + String missingAttributes = userMgmtSrv.checkMandatoryFields(user, excludeAttributes); + logger.debug("missingAttributes:{}", missingAttributes); + + if (StringHelper.isEmpty(missingAttributes)) { + return; + } + + throwMissingAttributeError(missingAttributes); + } + + private List getCustomUserList(List users) { + List customUserList = new ArrayList<>(); + if (users == null || users.isEmpty()) { + return customUserList; + } + + for (User user : users) { + CustomUser customUser = new CustomUser(); + setParentAttributes(customUser, user); + customUserList.add(customUser); + validateCustomObjectClasses(customUser); + } + logger.debug("Custom Users - customUserList:{}", customUserList); + return customUserList; + } + + private CustomUser getCustomUser(User user) { + CustomUser customUser = new CustomUser(); + if (user == null) { + return customUser; + } + setParentAttributes(customUser, user); + logger.debug("Custom User - customUser:{}", customUser); + return customUser; + } + + public CustomUser setParentAttributes(CustomUser customUser, User user) { + customUser.setBaseDn(user.getBaseDn()); + customUser.setCreatedAt(user.getCreatedAt()); + customUser.setCustomAttributes(user.getCustomAttributes()); + customUser.setCustomObjectClasses(user.getCustomObjectClasses()); + customUser.setDn(user.getDn()); + customUser.setOxAuthPersistentJwt(user.getOxAuthPersistentJwt()); + customUser.setUpdatedAt(user.getUpdatedAt()); + customUser.setUserId(user.getUserId()); + + validateCustomObjectClasses(customUser); + return setCustomUserAttributes(customUser, user); + } + + public CustomUser setCustomUserAttributes(CustomUser customUser, User user) { + customUser.setMail(user.getAttribute(MAIL)); + customUser.setDisplayName(user.getAttribute(DISPLAY_NAME)); + customUser.setJansStatus(user.getAttribute(JANS_STATUS)); + customUser.setGivenName(user.getAttribute(GIVEN_NAME)); + customUser.setUserPassword(user.getAttribute(USER_PWD)); + customUser.setInum(user.getAttribute(INUM)); + + customUser.removeAttribute(MAIL); + customUser.removeAttribute(DISPLAY_NAME); + customUser.removeAttribute(JANS_STATUS); + customUser.removeAttribute(GIVEN_NAME); + customUser.removeAttribute(USER_PWD); + customUser.removeAttribute(INUM); + + return customUser; + } + + private User setUserAttributes(CustomUser customUser) { + User user = new User(); + user.setBaseDn(customUser.getBaseDn()); + user.setCreatedAt(customUser.getCreatedAt()); + user.setCustomAttributes(customUser.getCustomAttributes()); + user.setCustomObjectClasses(customUser.getCustomObjectClasses()); + user.setDn(customUser.getDn()); + user.setOxAuthPersistentJwt(customUser.getOxAuthPersistentJwt()); + user.setUpdatedAt(customUser.getUpdatedAt()); + user.setUserId(customUser.getUserId()); + return setUserCustomAttributes(customUser, user); + } + + private User setUserCustomAttributes(CustomUser customUser, User user) { + user.setAttribute(MAIL, customUser.getMail(), false); + user.setAttribute(DISPLAY_NAME, customUser.getDisplayName(), false); + user.setAttribute(JANS_STATUS, customUser.getJansStatus(), false); + user.setAttribute(GIVEN_NAME, customUser.getGivenName(), false); + user.setAttribute(USER_PWD, customUser.getUserPassword(), false); + user.setAttribute(INUM, customUser.getInum(), false); + + logger.debug("Custom User - user:{}", user); + return user; + } + + private User validateCustomObjectClasses(User user) { + logger.info("validate User CustomObjectClasses - User user:{}", user); + if (user == null || (user.getCustomObjectClasses() == null || user.getCustomObjectClasses().length == 0)) { + return user; + } + logger.trace("user.getCustomObjectClasses():{}, userMgmtSrv.getPersistenceType():{}, userMgmtSrv.isLDAP():?{}", + user.getCustomObjectClasses(), userMgmtSrv.getPersistenceType(), userMgmtSrv.isLDAP()); + + if (user.getCustomObjectClasses().length > 0 && !userMgmtSrv.isLDAP()) { + + throwBadRequestException( + "'customObjectClasses' attribute supported only for 'LDAP'. Current Persistence type is '" + + userMgmtSrv.getPersistenceType() + "'. Kindly remove it."); + } + + return userMgmtSrv.ignoreCustomObjectClassesForNonLDAP(user); + } } \ No newline at end of file diff --git a/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/service/UserMgmtService.java b/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/service/UserMgmtService.java index 39248893166..1fc7461ad6e 100644 --- a/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/service/UserMgmtService.java +++ b/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/service/UserMgmtService.java @@ -37,303 +37,316 @@ @Named("userMgmtSrv") public class UserMgmtService { - @Inject - private Logger logger; - - @Inject - private StaticConfiguration staticConfiguration; - - @Inject - private AppConfiguration appConfiguration; - - @Inject - ConfigurationService configurationService; - - @Inject - PersistenceEntryManager persistenceEntryManager; - - @Inject - AuthUtil authUtil; - - @Inject - MgtUtil mgtUtil; - - @Inject - ConfigUserService userService; - - private static final String BIRTH_DATE = "birthdate"; - - public String getPeopleBaseDn() { - return userService.getPeopleBaseDn(); - } - - public PagedResult searchUsers(SearchRequest searchRequest) { - if (logger.isDebugEnabled()) { - logger.debug("Search Users with searchRequest:{}, getPeopleBaseDn():{}", escapeLog(searchRequest), - getPeopleBaseDn()); - } - Filter searchFilter = null; - List filters = new ArrayList<>(); - if (searchRequest.getFilterAssertionValue() != null && !searchRequest.getFilterAssertionValue().isEmpty()) { - - for (String assertionValue : searchRequest.getFilterAssertionValue()) { - String[] targetArray = new String[] { assertionValue }; - Filter displayNameFilter = Filter.createSubstringFilter(AttributeConstants.DISPLAY_NAME, null, - targetArray, null); - Filter descriptionFilter = Filter.createSubstringFilter(AttributeConstants.DESCRIPTION, null, - targetArray, null); - Filter uidFilter = Filter.createSubstringFilter("uid", null, targetArray, null); - Filter inumFilter = Filter.createSubstringFilter(AttributeConstants.INUM, null, targetArray, null); - filters.add(Filter.createORFilter(displayNameFilter, descriptionFilter, uidFilter, inumFilter)); - } - searchFilter = Filter.createORFilter(filters); - } - logger.debug("Users searchFilter:{}", searchFilter); - return persistenceEntryManager.findPagedEntries(userService.getPeopleBaseDn(), User.class, searchFilter, null, - searchRequest.getSortBy(), SortOrder.getByValue(searchRequest.getSortOrder()), - searchRequest.getStartIndex(), searchRequest.getCount(), searchRequest.getMaxCount()); - - } - - public void removeUser(User user) { - persistenceEntryManager.removeRecursively(user.getDn(), User.class); - } - - public User patchUser(String inum, UserPatchRequest userPatchRequest) throws JsonPatchException, IOException { - if (logger.isDebugEnabled()) { - logger.debug("Details to patch user inum:{}, UserPatchRequest:{} ", escapeLog(inum), - escapeLog(userPatchRequest)); - } - if (StringHelper.isEmpty(inum)) { - return null; - } - - User user = userService.getUserByInum(inum); - if (user == null) { - return null; - } - - logger.debug("User to be patched- user:{}", user); - // apply direct patch for basic attributes - if (StringUtils.isNotEmpty(userPatchRequest.getJsonPatchString())) { - logger.debug("Patch basic attributes"); - user = Jackson.applyPatch(userPatchRequest.getJsonPatchString(), user); - logger.debug("User after patching basic attributes - user:{}", user); - } - - // patch for customAttributes - if (userPatchRequest.getCustomAttributes() != null && !userPatchRequest.getCustomAttributes().isEmpty()) { - updateCustomAttributes(user, userPatchRequest.getCustomAttributes()); - } - - logger.debug("User before patch user:{}", user); - - // persist user - ignoreCustomObjectClassesForNonLDAP(user); - user = userService.updateUser(user); - logger.debug("User after patch user:{}", user); - return user; - - } - - public User getUserBasedOnInum(String inum) { - User result = null; - try { - result = userService.getUserByInum(inum); - } catch (Exception ex) { - logger.error("Failed to load user entry", ex); - } - return result; - } - - private User updateCustomAttributes(User user, List customAttributes) { - logger.debug("Custom Attributes to update for - user:{}, customAttributes:{} ", user, customAttributes); - - if (customAttributes == null || customAttributes.isEmpty()) { - return user; - } - - for (CustomObjectAttribute attribute : customAttributes) { - CustomObjectAttribute existingAttribute = userService.getCustomAttribute(user, attribute.getName()); - logger.debug("Existing CustomAttributes with existingAttribute:{} ", existingAttribute); - - // add - if (existingAttribute == null) { - boolean result = userService.addUserAttribute(user, attribute.getName(), attribute.getValues(), - attribute.isMultiValued()); - logger.debug("Result of adding CustomAttributes attribute:{} , result:{} ", attribute, result); - } - // remove attribute - else if (attribute.getValue() == null || attribute.getValues() == null) { - - user.removeAttribute(attribute.getName()); - } - // replace attribute - else { - existingAttribute.setMultiValued(attribute.isMultiValued()); - existingAttribute.setValues(attribute.getValues()); - } - // Final attribute - logger.debug("Finally user CustomAttributes user.getCustomAttributes:{} ", user.getCustomAttributes()); - - } - - return user; - } - - public List excludeAttributes(List users, String commaSeparatedString) - throws IllegalAccessException, InvocationTargetException { - logger.debug("Attributes:{} to be excluded from users:{} ", commaSeparatedString, users); - - if (users == null || users.isEmpty() || StringUtils.isEmpty(commaSeparatedString)) { - return users; - } - - for (User user : users) { - excludeAttributes(user, commaSeparatedString); - } - logger.debug("Users:{} after excluding attribute:{} ", users, commaSeparatedString); - - return users; - } - - public User excludeAttributes(User user, String commaSeparatedString) - throws IllegalAccessException, InvocationTargetException { - logger.debug("Attributes:{} to be excluded from user:{} ", commaSeparatedString, user); - - if (user == null || StringUtils.isEmpty(commaSeparatedString)) { - return user; - } - - List excludedAttributes = Arrays.asList(commaSeparatedString.split(",")); - logger.debug("Attributes List:{} to be excluded ", excludedAttributes); - - List allFields = authUtil.getAllFields(user.getClass()); - logger.debug("All user fields :{} ", allFields); - - HashMap map = new HashMap<>(); - for (String attribute : excludedAttributes) { - logger.debug("User class allFields:{} conatins attribute:{} ? :{} ", allFields, attribute, - authUtil.containsField(allFields, attribute)); - if (authUtil.containsField(allFields, attribute)) { - logger.debug("User class contains attribute:{} ! ", attribute); - map.put(attribute, null); - } else { - logger.debug("Removing custom attribute:{} from user:{} ", attribute, user); - user.removeAttribute(attribute); - } - } - - logger.debug("Attributes map:{} to be excluded ", map); - if (!map.isEmpty()) { - logger.debug("Removing simple attributes:{} from user object ", map); - BeanUtilsBean.getInstance().getConvertUtils().register(false, false, 0); - BeanUtils.populate(user, map); - } - - return user; - } - - public String getUserExclusionAttributesAsString() { - return authUtil.getUserExclusionAttributesAsString(); - } - - public String checkMandatoryFields(User user, List excludeAttributes) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - List mandatoryAttributes = authUtil.getUserMandatoryAttributes(); - logger.debug("mandatoryAttributess :{}, excludeAttributes:{} ", mandatoryAttributes, excludeAttributes); - - StringBuilder missingAttributes = new StringBuilder(); - - if (mandatoryAttributes == null || mandatoryAttributes.isEmpty()) { - return missingAttributes.toString(); - } - - List allFields = authUtil.getAllFields(user.getClass()); - logger.debug("All user fields :{} ", allFields); - - Object attributeValue = null; - for (String attribute : mandatoryAttributes) { - logger.debug("User class allFields:{} conatins attribute:{} ? :{} ", allFields, attribute, - authUtil.containsField(allFields, attribute)); - - // check if to be excluded - if (isExcludedAttribute(excludeAttributes, attribute)) { - logger.debug("Not checking if the attribute:{} is missing as it's in excludeAttributes:{}", attribute, - excludeAttributes); - continue; - } - - if (authUtil.containsField(allFields, attribute)) { - logger.debug("Checking if attribute:{} is simple attribute", attribute); - attributeValue = BeanUtils.getProperty(user, attribute); - logger.debug("User basic attribute:{} - attributeValue:{} ", attribute, attributeValue); - } else { - logger.debug("Checking if attribute:{} is custom attribute", attribute); - attributeValue = user.getAttribute(attribute); - logger.debug("User custom attribute:{} - attributeValue:{} ", attribute, attributeValue); - } - - if (attributeValue == null) { - missingAttributes.append(attribute).append(","); - } - } - logger.debug("Checking mandatory missingAttributes:{} ", missingAttributes); - if (missingAttributes.length() > 0) { - missingAttributes.replace(missingAttributes.lastIndexOf(","), missingAttributes.length(), ""); - } - - logger.debug("Returning missingAttributes:{} ", missingAttributes); - return missingAttributes.toString(); - } - - private boolean isExcludedAttribute(List excludeAttributes, String attribute) { - logger.debug(" Is attribute:{} in excludeAttributeList:{} ", attribute, excludeAttributes); - - if (excludeAttributes == null || excludeAttributes.isEmpty()) { - return false; - } - - return excludeAttributes.stream().anyMatch(e -> e.equals(attribute)); - } - - public User parseBirthDateAttribute(User user) { - if (user.getAttributeObjectValues(BIRTH_DATE) != null) { - - Optional optionalBithdate = user.getAttributeObjectValues(BIRTH_DATE).stream().findFirst(); - - if (!optionalBithdate.isPresent()) { - return user; - } - - Date date = mgtUtil.parseStringToDateObj(optionalBithdate.get().toString()); - // parse date with persistenceEntryManager.decodeTime if it is null - if (date == null) { - date = persistenceEntryManager.decodeTime(null, optionalBithdate.get().toString()); - } - user.getCustomAttributes().remove(new CustomObjectAttribute(BIRTH_DATE)); - user.getCustomAttributes().add(new CustomObjectAttribute(BIRTH_DATE, date)); - } - return user; - } - - public User ignoreCustomObjectClassesForNonLDAP(User user) { - String persistenceType = configurationService.getPersistenceType(); - logger.debug("persistenceType: {}", persistenceType); - if (!PersistenceEntryManager.PERSITENCE_TYPES.ldap.name().equals(persistenceType)) { - logger.debug( - "Setting CustomObjectClasses :{} to null as its used only for LDAP and current persistenceType is {} ", - user.getCustomObjectClasses(), persistenceType); - user.setCustomObjectClasses(null); - } - return user; - } - - public User addUser(User user, boolean active) { - return userService.addUser(user, active); - } - - public User updateUser(User user) { - return userService.updateUser(user); - } + @Inject + private Logger logger; + + @Inject + private StaticConfiguration staticConfiguration; + + @Inject + private AppConfiguration appConfiguration; + + @Inject + ConfigurationService configurationService; + + @Inject + PersistenceEntryManager persistenceEntryManager; + + @Inject + AuthUtil authUtil; + + @Inject + MgtUtil mgtUtil; + + @Inject + ConfigUserService userService; + + private static final String BIRTH_DATE = "birthdate"; + + public String getPeopleBaseDn() { + return userService.getPeopleBaseDn(); + } + + public PagedResult searchUsers(SearchRequest searchRequest) { + if (logger.isDebugEnabled()) { + logger.debug("Search Users with searchRequest:{}, getPeopleBaseDn():{}", escapeLog(searchRequest), + getPeopleBaseDn()); + } + Filter searchFilter = null; + List filters = new ArrayList<>(); + if (searchRequest.getFilterAssertionValue() != null && !searchRequest.getFilterAssertionValue().isEmpty()) { + + for (String assertionValue : searchRequest.getFilterAssertionValue()) { + String[] targetArray = new String[] { assertionValue }; + Filter displayNameFilter = Filter.createSubstringFilter(AttributeConstants.DISPLAY_NAME, null, + targetArray, null); + Filter descriptionFilter = Filter.createSubstringFilter(AttributeConstants.DESCRIPTION, null, + targetArray, null); + Filter uidFilter = Filter.createSubstringFilter("uid", null, targetArray, null); + Filter inumFilter = Filter.createSubstringFilter(AttributeConstants.INUM, null, targetArray, null); + filters.add(Filter.createORFilter(displayNameFilter, descriptionFilter, uidFilter, inumFilter)); + } + searchFilter = Filter.createORFilter(filters); + } + logger.debug("Users searchFilter:{}", searchFilter); + return persistenceEntryManager.findPagedEntries(userService.getPeopleBaseDn(), User.class, searchFilter, null, + searchRequest.getSortBy(), SortOrder.getByValue(searchRequest.getSortOrder()), + searchRequest.getStartIndex(), searchRequest.getCount(), searchRequest.getMaxCount()); + + } + + public void removeUser(User user) { + persistenceEntryManager.removeRecursively(user.getDn(), User.class); + } + + public User patchUser(String inum, UserPatchRequest userPatchRequest) throws JsonPatchException, IOException { + if (logger.isDebugEnabled()) { + logger.debug("Details to patch user inum:{}, UserPatchRequest:{} ", escapeLog(inum), + escapeLog(userPatchRequest)); + } + if (StringHelper.isEmpty(inum)) { + return null; + } + + User user = userService.getUserByInum(inum); + if (user == null) { + return null; + } + + logger.debug("User to be patched- user:{}", user); + // apply direct patch for basic attributes + if (StringUtils.isNotEmpty(userPatchRequest.getJsonPatchString())) { + logger.debug("Patch basic attributes"); + user = Jackson.applyPatch(userPatchRequest.getJsonPatchString(), user); + logger.debug("User after patching basic attributes - user:{}", user); + } + + // patch for customAttributes + if (userPatchRequest.getCustomAttributes() != null && !userPatchRequest.getCustomAttributes().isEmpty()) { + updateCustomAttributes(user, userPatchRequest.getCustomAttributes()); + } + + logger.debug("User before patch user:{}", user); + + // persist user + ignoreCustomObjectClassesForNonLDAP(user); + user = userService.updateUser(user); + logger.debug("User after patch user:{}", user); + return user; + + } + + public User getUserBasedOnInum(String inum) { + User result = null; + try { + result = userService.getUserByInum(inum); + } catch (Exception ex) { + logger.error("Failed to load user entry", ex); + } + return result; + } + + private User updateCustomAttributes(User user, List customAttributes) { + logger.debug("Custom Attributes to update for - user:{}, customAttributes:{} ", user, customAttributes); + + if (customAttributes == null || customAttributes.isEmpty()) { + return user; + } + + for (CustomObjectAttribute attribute : customAttributes) { + CustomObjectAttribute existingAttribute = userService.getCustomAttribute(user, attribute.getName()); + logger.debug("Existing CustomAttributes with existingAttribute:{} ", existingAttribute); + + // add + if (existingAttribute == null) { + boolean result = userService.addUserAttribute(user, attribute.getName(), attribute.getValues(), + attribute.isMultiValued()); + logger.debug("Result of adding CustomAttributes attribute:{} , result:{} ", attribute, result); + } + // remove attribute + else if (attribute.getValue() == null || attribute.getValues() == null) { + + user.removeAttribute(attribute.getName()); + } + // replace attribute + else { + existingAttribute.setMultiValued(attribute.isMultiValued()); + existingAttribute.setValues(attribute.getValues()); + } + // Final attribute + logger.debug("Finally user CustomAttributes user.getCustomAttributes:{} ", user.getCustomAttributes()); + + } + + return user; + } + + public List excludeAttributes(List users, String commaSeparatedString) + throws IllegalAccessException, InvocationTargetException { + logger.debug("Attributes:{} to be excluded from users:{} ", commaSeparatedString, users); + + if (users == null || users.isEmpty() || StringUtils.isEmpty(commaSeparatedString)) { + return users; + } + + for (User user : users) { + excludeAttributes(user, commaSeparatedString); + } + logger.debug("Users:{} after excluding attribute:{} ", users, commaSeparatedString); + + return users; + } + + public User excludeAttributes(User user, String commaSeparatedString) + throws IllegalAccessException, InvocationTargetException { + logger.debug("Attributes:{} to be excluded from user:{} ", commaSeparatedString, user); + + if (user == null || StringUtils.isEmpty(commaSeparatedString)) { + return user; + } + + List excludedAttributes = Arrays.asList(commaSeparatedString.split(",")); + logger.debug("Attributes List:{} to be excluded ", excludedAttributes); + + List allFields = authUtil.getAllFields(user.getClass()); + logger.debug("All user fields :{} ", allFields); + + HashMap map = new HashMap<>(); + for (String attribute : excludedAttributes) { + logger.debug("User class allFields:{} conatins attribute:{} ? :{} ", allFields, attribute, + authUtil.containsField(allFields, attribute)); + if (authUtil.containsField(allFields, attribute)) { + logger.debug("User class contains attribute:{} ! ", attribute); + map.put(attribute, null); + } else { + logger.debug("Removing custom attribute:{} from user:{} ", attribute, user); + user.removeAttribute(attribute); + } + } + + logger.debug("Attributes map:{} to be excluded ", map); + if (!map.isEmpty()) { + logger.debug("Removing simple attributes:{} from user object ", map); + BeanUtilsBean.getInstance().getConvertUtils().register(false, false, 0); + BeanUtils.populate(user, map); + } + + return user; + } + + public String getUserExclusionAttributesAsString() { + return authUtil.getUserExclusionAttributesAsString(); + } + + public String checkMandatoryFields(User user, List excludeAttributes) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + List mandatoryAttributes = authUtil.getUserMandatoryAttributes(); + logger.debug("mandatoryAttributess :{}, excludeAttributes:{} ", mandatoryAttributes, excludeAttributes); + + StringBuilder missingAttributes = new StringBuilder(); + + if (mandatoryAttributes == null || mandatoryAttributes.isEmpty()) { + return missingAttributes.toString(); + } + + List allFields = authUtil.getAllFields(user.getClass()); + logger.debug("All user fields :{} ", allFields); + + Object attributeValue = null; + for (String attribute : mandatoryAttributes) { + logger.debug("User class allFields:{} conatins attribute:{} ? :{} ", allFields, attribute, + authUtil.containsField(allFields, attribute)); + + // check if to be excluded + if (isExcludedAttribute(excludeAttributes, attribute)) { + logger.debug("Not checking if the attribute:{} is missing as it's in excludeAttributes:{}", attribute, + excludeAttributes); + continue; + } + + if (authUtil.containsField(allFields, attribute)) { + logger.debug("Checking if attribute:{} is simple attribute", attribute); + attributeValue = BeanUtils.getProperty(user, attribute); + logger.debug("User basic attribute:{} - attributeValue:{} ", attribute, attributeValue); + } else { + logger.debug("Checking if attribute:{} is custom attribute", attribute); + attributeValue = user.getAttribute(attribute); + logger.debug("User custom attribute:{} - attributeValue:{} ", attribute, attributeValue); + } + + if (attributeValue == null) { + missingAttributes.append(attribute).append(","); + } + } + logger.debug("Checking mandatory missingAttributes:{} ", missingAttributes); + if (missingAttributes.length() > 0) { + missingAttributes.replace(missingAttributes.lastIndexOf(","), missingAttributes.length(), ""); + } + + logger.debug("Returning missingAttributes:{} ", missingAttributes); + return missingAttributes.toString(); + } + + private boolean isExcludedAttribute(List excludeAttributes, String attribute) { + logger.debug(" Is attribute:{} in excludeAttributeList:{} ", attribute, excludeAttributes); + + if (excludeAttributes == null || excludeAttributes.isEmpty()) { + return false; + } + + return excludeAttributes.stream().anyMatch(e -> e.equals(attribute)); + } + + public User parseBirthDateAttribute(User user) { + if (user.getAttributeObjectValues(BIRTH_DATE) != null) { + + Optional optionalBithdate = user.getAttributeObjectValues(BIRTH_DATE).stream().findFirst(); + + if (!optionalBithdate.isPresent()) { + return user; + } + + Date date = mgtUtil.parseStringToDateObj(optionalBithdate.get().toString()); + // parse date with persistenceEntryManager.decodeTime if it is null + if (date == null) { + date = persistenceEntryManager.decodeTime(null, optionalBithdate.get().toString()); + } + user.getCustomAttributes().remove(new CustomObjectAttribute(BIRTH_DATE)); + user.getCustomAttributes().add(new CustomObjectAttribute(BIRTH_DATE, date)); + } + return user; + } + + public User ignoreCustomObjectClassesForNonLDAP(User user) { + String persistenceType = configurationService.getPersistenceType(); + logger.debug("persistenceType: {}, isLDAP?:{}", persistenceType, isLDAP()); + if (!isLDAP()) { + logger.debug( + "Setting CustomObjectClasses :{} to null as its used only for LDAP and current persistenceType is {} ", + user.getCustomObjectClasses(), persistenceType); + user.setCustomObjectClasses(null); + } + return user; + } + + public boolean isLDAP() { + String persistenceType = getPersistenceType(); + logger.debug("persistenceType: {}", persistenceType); + if (PersistenceEntryManager.PERSITENCE_TYPES.ldap.name().equals(persistenceType)) { + return true; + } + return false; + } + + public String getPersistenceType() { + return configurationService.getPersistenceType(); + } + + public User addUser(User user, boolean active) { + return userService.addUser(user, active); + } + + public User updateUser(User user) { + return userService.updateUser(user); + } } From 531501725ccf6d02f3db56bbf4997317792f93e3 Mon Sep 17 00:00:00 2001 From: pujavs Date: Fri, 10 Mar 2023 17:20:05 +0530 Subject: [PATCH 2/4] fix(config-api): user custom attribute changes and agama param changes --- .../io/jans/configapi/util/ApiConstants.java | 2 + .../docs/jans-config-api-swagger.yaml | 121 ++- .../plugin/mgt/rest/UserResource.java | 769 +++++++++--------- .../plugin/mgt/service/UserMgmtService.java | 624 +++++++------- .../auth/AgamaDeploymentsResource.java | 62 +- .../feature/agama/agama-deployment.feature | 46 ++ .../test/resources/karate-config-jenkins.js | 1 + .../src/test/resources/karate-config.js | 1 + 8 files changed, 870 insertions(+), 756 deletions(-) create mode 100644 jans-config-api/server/src/test/resources/feature/agama/agama-deployment.feature diff --git a/jans-config-api/common/src/main/java/io/jans/configapi/util/ApiConstants.java b/jans-config-api/common/src/main/java/io/jans/configapi/util/ApiConstants.java index b6f770e645b..ab3ec3c0e59 100644 --- a/jans-config-api/common/src/main/java/io/jans/configapi/util/ApiConstants.java +++ b/jans-config-api/common/src/main/java/io/jans/configapi/util/ApiConstants.java @@ -12,6 +12,7 @@ private ApiConstants() {} public static final String BASE_API_URL = "/"; public static final String CONFIG = "/config"; + public static final String CONFIGS = "/configs"; public static final String API_CONFIG = "/api-config"; public static final String JWKS = "/jwks"; public static final String JANS_AUTH = "/jans-auth-server"; @@ -113,6 +114,7 @@ private ApiConstants() {} public static final String ACTIVE = "active"; public static final String INACTIVE = "inactive"; public static final String ADD_SCRIPT_TEMPLATE = "addScriptTemplate"; + public static final String REMOVE_NON_LDAP_ATTRIBUTES = "removeNonLDAPAttributes"; // API Protection public static final String PROTECTION_TYPE_OAUTH2 = "oauth2"; diff --git a/jans-config-api/docs/jans-config-api-swagger.yaml b/jans-config-api/docs/jans-config-api-swagger.yaml index 6e15b54d6e0..b14ce5f0590 100644 --- a/jans-config-api/docs/jans-config-api-swagger.yaml +++ b/jans-config-api/docs/jans-config-api-swagger.yaml @@ -173,7 +173,7 @@ paths: - oauth2: - https://jans.io/oauth/config/acrs.write - https://jans.io/oauth/config/write-all - /api/v1/agama-deployment: + /api/v1/agama-deployment/{name}: get: tags: - Agama - Developer Studio @@ -182,7 +182,9 @@ paths: operationId: get-agama-dev-studio-prj-by-name parameters: - name: name - in: query + in: path + description: Agama project name + required: true schema: type: string responses: @@ -209,8 +211,6 @@ paths: security: - oauth2: - https://jans.io/oauth/config/agama.readonly - - https://jans.io/oauth/config/agama.write - - https://jans.io/oauth/config/read-all post: tags: - Agama - Developer Studio @@ -219,7 +219,9 @@ paths: operationId: post-agama-dev-studio-prj parameters: - name: name - in: query + in: path + description: Agama project name + required: true schema: type: string requestBody: @@ -252,7 +254,6 @@ paths: security: - oauth2: - https://jans.io/oauth/config/agama.write - - https://jans.io/oauth/config/write-all delete: tags: - Agama - Developer Studio @@ -261,7 +262,9 @@ paths: operationId: delete-agama-dev-studio-prj parameters: - name: name - in: query + in: path + description: Agama project name + required: true schema: type: string responses: @@ -280,24 +283,49 @@ paths: security: - oauth2: - https://jans.io/oauth/config/agama.delete - /api/v1/agama-deployment/configs: + /api/v1/agama-deployment/configs/{name}: get: - operationId: getConfigs + tags: + - Agama - Developer Studio + summary: Retrieve the list of configs based on name. + description: Retrieve the list of configs based on name. + operationId: get-agama-dev-prj-configs parameters: - name: name - in: query + in: path + description: Agama project name + required: true schema: type: string responses: - default: - description: default response + "200": + description: Agama projects configs content: - application/json: {} + application/json: + schema: + type: string + examples: + Response json example: + description: Response json example + value: "" + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/oauth/config/agama.readonly put: - operationId: setConfigs + tags: + - Agama - Developer Studio + summary: Update an Agama project. + description: Update an Agama project. + operationId: put-agama-dev-studio-prj parameters: - name: name - in: query + in: path + description: Agama project name + required: true schema: type: string requestBody: @@ -310,10 +338,27 @@ paths: additionalProperties: type: object responses: - default: - description: default response + "202": + description: Agama project accepted content: - application/json: {} + application/json: + schema: + type: string + examples: + Response json example: + description: Response json example + value: "" + "400": + description: Bad Request + "401": + description: Unauthorized + "409": + description: Conflict + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/oauth/config/agama.write /api/v1/agama-deployment/list: get: tags: @@ -350,8 +395,6 @@ paths: security: - oauth2: - https://jans.io/oauth/config/agama.readonly - - https://jans.io/oauth/config/agama.write - - https://jans.io/oauth/config/read-all /api/v1/agama: get: tags: @@ -7575,15 +7618,15 @@ components: type: string adminCanAccess: type: boolean - adminCanView: + adminCanEdit: type: boolean - userCanAccess: + userCanEdit: type: boolean userCanView: type: boolean - adminCanEdit: + adminCanView: type: boolean - userCanEdit: + userCanAccess: type: boolean whitePagesCanView: type: boolean @@ -8843,17 +8886,6 @@ components: format: int32 displayName: type: string - authenticationMethod: - type: string - enum: - - client_secret_basic - - client_secret_post - - client_secret_jwt - - private_key_jwt - - access_token - - tls_client_auth - - self_signed_tls_client_auth - - none allAuthenticationMethods: uniqueItems: true type: array @@ -8868,6 +8900,17 @@ components: - tls_client_auth - self_signed_tls_client_auth - none + authenticationMethod: + type: string + enum: + - client_secret_basic + - client_secret_post + - client_secret_jwt + - private_key_jwt + - access_token + - tls_client_auth + - self_signed_tls_client_auth + - none baseDn: type: string inum: @@ -8975,10 +9018,10 @@ components: type: array items: type: object - displayValue: - type: string value: type: object + displayValue: + type: string LocalizedString: type: object properties: @@ -9192,14 +9235,14 @@ components: type: boolean internal: type: boolean + locationPath: + type: string locationType: type: string enum: - ldap - db - file - locationPath: - type: string baseDn: type: string ScriptError: diff --git a/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/rest/UserResource.java b/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/rest/UserResource.java index 8a2395c4047..b395c34f8d1 100644 --- a/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/rest/UserResource.java +++ b/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/rest/UserResource.java @@ -47,384 +47,395 @@ @ApplicationScoped public class UserResource extends BaseResource { - private static final String USER = "user"; - private static final String MAIL = "mail"; - private static final String DISPLAY_NAME = "displayName"; - private static final String JANS_STATUS = "jansStatus"; - private static final String GIVEN_NAME = "givenName"; - private static final String USER_PWD = "userPassword"; - private static final String INUM = "inum"; - - private class UserPagedResult extends PagedResult { - }; - - @Inject - Logger logger; - - @Inject - MgtUtil mgtUtil; - - @Inject - UserMgmtService userMgmtSrv; - - @Operation(summary = "Gets list of users", description = "Gets list of users", operationId = "get-user", tags = { - "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.USER_READ_ACCESS })) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = UserPagedResult.class), examples = @ExampleObject(name = "Response json example", value = "example/user/user-all.json"))), - @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "500", description = "InternalServerError") }) - @GET - @ProtectedApi(scopes = { ApiAccessConstants.USER_READ_ACCESS }) - public Response getUsers( - @Parameter(description = "Search size - max size of the results to return") @DefaultValue(ApiConstants.DEFAULT_LIST_SIZE) @QueryParam(value = ApiConstants.LIMIT) int limit, - @Parameter(description = "Search pattern") @DefaultValue("") @QueryParam(value = ApiConstants.PATTERN) String pattern, - @Parameter(description = "The 1-based index of the first query result") @DefaultValue(ApiConstants.DEFAULT_LIST_START_INDEX) @QueryParam(value = ApiConstants.START_INDEX) int startIndex, - @Parameter(description = "Attribute whose value will be used to order the returned response") @QueryParam(value = ApiConstants.SORT_BY) String sortBy, - @Parameter(description = "Order in which the sortBy param is applied. Allowed values are \"ascending\" and \"descending\"") @QueryParam(value = ApiConstants.SORT_ORDER) String sortOrder) - throws IllegalAccessException, InvocationTargetException { - if (logger.isDebugEnabled()) { - logger.debug("User search param - limit:{}, pattern:{}, startIndex:{}, sortBy:{}, sortOrder:{}", - escapeLog(limit), escapeLog(pattern), escapeLog(startIndex), escapeLog(sortBy), - escapeLog(sortOrder)); - } - SearchRequest searchReq = createSearchRequest(userMgmtSrv.getPeopleBaseDn(), pattern, sortBy, sortOrder, - startIndex, limit, null, userMgmtSrv.getUserExclusionAttributesAsString(), mgtUtil.getRecordMaxCount()); - - return Response.ok(this.doSearch(searchReq)).build(); - } - - @Operation(summary = "Get User by Inum", description = "Get User by Inum", operationId = "get-user-by-inum", tags = { - "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.USER_READ_ACCESS })) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class, description = "CustomUser identified by inum"), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), - @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "404", description = "Not Found"), - @ApiResponse(responseCode = "500", description = "InternalServerError") }) - @GET - @ProtectedApi(scopes = { ApiAccessConstants.USER_READ_ACCESS }) - @Path(ApiConstants.INUM_PATH) - public Response getUserByInum( - @Parameter(description = "User identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) - throws IllegalAccessException, InvocationTargetException { - if (logger.isDebugEnabled()) { - logger.debug("User search by inum:{}", escapeLog(inum)); - } - User user = userMgmtSrv.getUserBasedOnInum(inum); - checkResourceNotNull(user, USER); - logger.debug("user:{}", user); - - // excludedAttributes - user = excludeUserAttributes(user); - logger.debug("user:{}", user); - - // get custom user - CustomUser customUser = getCustomUser(user); - logger.debug("customUser:{}", customUser); - - return Response.ok(customUser).build(); - } - - @Operation(summary = "Create new User", description = "Create new User", operationId = "post-user", tags = { - "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.USER_WRITE_ACCESS })) - @RequestBody(description = "User object", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user-post.json"))) - @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "Created", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class, description = "Created Object"), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), - @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "500", description = "InternalServerError") }) - @POST - @ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS }) - public Response createUser(@Valid CustomUser customUser) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - if (logger.isDebugEnabled()) { - logger.debug("User details to be added - customUser:{}", escapeLog(customUser)); - } - - // get User object - User user = setUserAttributes(customUser); - - // parse birthdate if present - userMgmtSrv.parseBirthDateAttribute(user); - logger.debug("Create user:{}", user); - - // checking mandatory attributes - checkMissingAttributes(user, null); - validateCustomObjectClasses(user); - - user = userMgmtSrv.addUser(user, true); - logger.debug("User created {}", user); - - // excludedAttributes - user = excludeUserAttributes(user); - - // get custom user - customUser = getCustomUser(user); - logger.debug("newly created customUser:{}", customUser); - - return Response.status(Response.Status.CREATED).entity(customUser).build(); - } - - @Operation(summary = "Update User", description = "Update User", operationId = "put-user", tags = { - "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.USER_WRITE_ACCESS })) - @RequestBody(description = "User object", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user.json"))) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), - @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "404", description = "Not Found"), - @ApiResponse(responseCode = "500", description = "InternalServerError") }) - @PUT - @ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS }) - public Response updateUser(@Valid CustomUser customUser) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - if (logger.isDebugEnabled()) { - logger.debug("User details to be updated - customUser:{}", escapeLog(customUser)); - } - - // get User object - User user = setUserAttributes(customUser); - - // parse birthdate if present - userMgmtSrv.parseBirthDateAttribute(user); - logger.debug("Create user:{}", user); - - // checking mandatory attributes - List excludeAttributes = List.of(USER_PWD); - checkMissingAttributes(user, excludeAttributes); - validateCustomObjectClasses(user); - try { - user = userMgmtSrv.updateUser(user); - logger.debug("Updated user:{}", user); - } catch (Exception ex) { - logger.error("Error while updating user", ex); - throwInternalServerException(ex); - } - - // excludedAttributes - user = excludeUserAttributes(user); - - // get custom user - customUser = getCustomUser(user); - logger.debug("updated customUser:{}", customUser); - - return Response.ok(customUser).build(); - - } - - @Operation(summary = "Patch user properties by Inum", description = "Patch user properties by Inum", operationId = "patch-user-by-inum", tags = { - "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.USER_WRITE_ACCESS })) - @RequestBody(description = "UserPatchRequest", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = UserPatchRequest.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user-patch.json"))) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class, description = "Patched CustomUser Object"), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), - @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "404", description = "Not Found"), - @ApiResponse(responseCode = "500", description = "InternalServerError") }) - @PATCH - @ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS }) - @Path(ApiConstants.INUM_PATH) - public Response patchUser( - @Parameter(description = "User identifier") @PathParam(ApiConstants.INUM) @NotNull String inum, - @NotNull UserPatchRequest userPatchRequest) - throws IllegalAccessException, InvocationTargetException, JsonPatchException, IOException { - if (logger.isDebugEnabled()) { - logger.debug("User:{} to be patched with :{} ", escapeLog(inum), escapeLog(userPatchRequest)); - } - // check if user exists - User existingUser = userMgmtSrv.getUserBasedOnInum(inum); - - // parse birthdate if present - userMgmtSrv.parseBirthDateAttribute(existingUser); - checkResourceNotNull(existingUser, USER); - validateCustomObjectClasses(existingUser); - - // patch user - existingUser = userMgmtSrv.patchUser(inum, userPatchRequest); - logger.debug("Patched user:{}", existingUser); - - // excludedAttributes - existingUser = excludeUserAttributes(existingUser); - - // get custom user - CustomUser customUser = getCustomUser(existingUser); - logger.debug("patched customUser:{}", customUser); - - return Response.ok(customUser).build(); - } - - @Operation(summary = "Delete User", description = "Delete User", operationId = "delete-user", tags = { - "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.USER_DELETE_ACCESS })) - @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "No Content"), - @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "404", description = "Not Found"), - @ApiResponse(responseCode = "500", description = "InternalServerError") }) - @DELETE - @Path(ApiConstants.INUM_PATH) - @ProtectedApi(scopes = { ApiAccessConstants.USER_DELETE_ACCESS }) - public Response deleteUser( - @Parameter(description = "User identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) { - if (logger.isDebugEnabled()) { - logger.debug("User to be deleted - inum:{} ", escapeLog(inum)); - } - User user = userMgmtSrv.getUserBasedOnInum(inum); - checkResourceNotNull(user, USER); - userMgmtSrv.removeUser(user); - return Response.noContent().build(); - } - - private UserPagedResult doSearch(SearchRequest searchReq) throws IllegalAccessException, InvocationTargetException { - if (logger.isDebugEnabled()) { - logger.debug("User search params - searchReq:{} ", escapeLog(searchReq)); - } - - PagedResult pagedResult = userMgmtSrv.searchUsers(searchReq); - if (logger.isTraceEnabled()) { - logger.debug("PagedResult - pagedResult:{}", pagedResult); - } - - UserPagedResult pagedCustomUser = new UserPagedResult(); - if (pagedResult != null) { - logger.debug("Users fetched - pagedResult.getEntries():{}", pagedResult.getEntries()); - List users = pagedResult.getEntries(); - - // excludedAttributes - users = userMgmtSrv.excludeAttributes(users, searchReq.getExcludedAttributesStr()); - logger.debug("Users fetched - users:{}", users); - - // parse birthdate if present - users = users.stream().map(user -> userMgmtSrv.parseBirthDateAttribute(user)).collect(Collectors.toList()); - - // get customUser() - List customUsers = getCustomUserList(users); - pagedCustomUser.setStart(pagedResult.getStart()); - pagedCustomUser.setEntriesCount(pagedResult.getEntriesCount()); - pagedCustomUser.setTotalEntriesCount(pagedResult.getTotalEntriesCount()); - pagedCustomUser.setEntries(customUsers); - } - - logger.debug("User pagedCustomUser:{}", pagedCustomUser); - return pagedCustomUser; - - } - - private User excludeUserAttributes(User user) throws IllegalAccessException, InvocationTargetException { - return userMgmtSrv.excludeAttributes(user, userMgmtSrv.getUserExclusionAttributesAsString()); - } - - private void checkMissingAttributes(User user, List excludeAttributes) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - String missingAttributes = userMgmtSrv.checkMandatoryFields(user, excludeAttributes); - logger.debug("missingAttributes:{}", missingAttributes); - - if (StringHelper.isEmpty(missingAttributes)) { - return; - } - - throwMissingAttributeError(missingAttributes); - } - - private List getCustomUserList(List users) { - List customUserList = new ArrayList<>(); - if (users == null || users.isEmpty()) { - return customUserList; - } - - for (User user : users) { - CustomUser customUser = new CustomUser(); - setParentAttributes(customUser, user); - customUserList.add(customUser); - validateCustomObjectClasses(customUser); - } - logger.debug("Custom Users - customUserList:{}", customUserList); - return customUserList; - } - - private CustomUser getCustomUser(User user) { - CustomUser customUser = new CustomUser(); - if (user == null) { - return customUser; - } - setParentAttributes(customUser, user); - logger.debug("Custom User - customUser:{}", customUser); - return customUser; - } - - public CustomUser setParentAttributes(CustomUser customUser, User user) { - customUser.setBaseDn(user.getBaseDn()); - customUser.setCreatedAt(user.getCreatedAt()); - customUser.setCustomAttributes(user.getCustomAttributes()); - customUser.setCustomObjectClasses(user.getCustomObjectClasses()); - customUser.setDn(user.getDn()); - customUser.setOxAuthPersistentJwt(user.getOxAuthPersistentJwt()); - customUser.setUpdatedAt(user.getUpdatedAt()); - customUser.setUserId(user.getUserId()); - - validateCustomObjectClasses(customUser); - return setCustomUserAttributes(customUser, user); - } - - public CustomUser setCustomUserAttributes(CustomUser customUser, User user) { - customUser.setMail(user.getAttribute(MAIL)); - customUser.setDisplayName(user.getAttribute(DISPLAY_NAME)); - customUser.setJansStatus(user.getAttribute(JANS_STATUS)); - customUser.setGivenName(user.getAttribute(GIVEN_NAME)); - customUser.setUserPassword(user.getAttribute(USER_PWD)); - customUser.setInum(user.getAttribute(INUM)); - - customUser.removeAttribute(MAIL); - customUser.removeAttribute(DISPLAY_NAME); - customUser.removeAttribute(JANS_STATUS); - customUser.removeAttribute(GIVEN_NAME); - customUser.removeAttribute(USER_PWD); - customUser.removeAttribute(INUM); - - return customUser; - } - - private User setUserAttributes(CustomUser customUser) { - User user = new User(); - user.setBaseDn(customUser.getBaseDn()); - user.setCreatedAt(customUser.getCreatedAt()); - user.setCustomAttributes(customUser.getCustomAttributes()); - user.setCustomObjectClasses(customUser.getCustomObjectClasses()); - user.setDn(customUser.getDn()); - user.setOxAuthPersistentJwt(customUser.getOxAuthPersistentJwt()); - user.setUpdatedAt(customUser.getUpdatedAt()); - user.setUserId(customUser.getUserId()); - return setUserCustomAttributes(customUser, user); - } - - private User setUserCustomAttributes(CustomUser customUser, User user) { - user.setAttribute(MAIL, customUser.getMail(), false); - user.setAttribute(DISPLAY_NAME, customUser.getDisplayName(), false); - user.setAttribute(JANS_STATUS, customUser.getJansStatus(), false); - user.setAttribute(GIVEN_NAME, customUser.getGivenName(), false); - user.setAttribute(USER_PWD, customUser.getUserPassword(), false); - user.setAttribute(INUM, customUser.getInum(), false); - - logger.debug("Custom User - user:{}", user); - return user; - } - - private User validateCustomObjectClasses(User user) { - logger.info("validate User CustomObjectClasses - User user:{}", user); - if (user == null || (user.getCustomObjectClasses() == null || user.getCustomObjectClasses().length == 0)) { - return user; - } - logger.trace("user.getCustomObjectClasses():{}, userMgmtSrv.getPersistenceType():{}, userMgmtSrv.isLDAP():?{}", - user.getCustomObjectClasses(), userMgmtSrv.getPersistenceType(), userMgmtSrv.isLDAP()); - - if (user.getCustomObjectClasses().length > 0 && !userMgmtSrv.isLDAP()) { - - throwBadRequestException( - "'customObjectClasses' attribute supported only for 'LDAP'. Current Persistence type is '" - + userMgmtSrv.getPersistenceType() + "'. Kindly remove it."); - } - - return userMgmtSrv.ignoreCustomObjectClassesForNonLDAP(user); - } + private static final String USER = "user"; + private static final String MAIL = "mail"; + private static final String DISPLAY_NAME = "displayName"; + private static final String JANS_STATUS = "jansStatus"; + private static final String GIVEN_NAME = "givenName"; + private static final String USER_PWD = "userPassword"; + private static final String INUM = "inum"; + + private class UserPagedResult extends PagedResult { + }; + + @Inject + Logger logger; + + @Inject + MgtUtil mgtUtil; + + @Inject + UserMgmtService userMgmtSrv; + + @Operation(summary = "Gets list of users", description = "Gets list of users", operationId = "get-user", tags = { + "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { + ApiAccessConstants.USER_READ_ACCESS })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = UserPagedResult.class), examples = @ExampleObject(name = "Response json example", value = "example/user/user-all.json"))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @GET + @ProtectedApi(scopes = { ApiAccessConstants.USER_READ_ACCESS }) + public Response getUsers( + @Parameter(description = "Search size - max size of the results to return") @DefaultValue(ApiConstants.DEFAULT_LIST_SIZE) @QueryParam(value = ApiConstants.LIMIT) int limit, + @Parameter(description = "Search pattern") @DefaultValue("") @QueryParam(value = ApiConstants.PATTERN) String pattern, + @Parameter(description = "The 1-based index of the first query result") @DefaultValue(ApiConstants.DEFAULT_LIST_START_INDEX) @QueryParam(value = ApiConstants.START_INDEX) int startIndex, + @Parameter(description = "Attribute whose value will be used to order the returned response") @QueryParam(value = ApiConstants.SORT_BY) String sortBy, + @Parameter(description = "Order in which the sortBy param is applied. Allowed values are \"ascending\" and \"descending\"") @QueryParam(value = ApiConstants.SORT_ORDER) String sortOrder) + throws IllegalAccessException, InvocationTargetException { + if (logger.isDebugEnabled()) { + logger.debug("User search param - limit:{}, pattern:{}, startIndex:{}, sortBy:{}, sortOrder:{}", + escapeLog(limit), escapeLog(pattern), escapeLog(startIndex), escapeLog(sortBy), + escapeLog(sortOrder)); + } + SearchRequest searchReq = createSearchRequest(userMgmtSrv.getPeopleBaseDn(), pattern, sortBy, sortOrder, + startIndex, limit, null, userMgmtSrv.getUserExclusionAttributesAsString(), mgtUtil.getRecordMaxCount()); + + return Response.ok(this.doSearch(searchReq, true)).build(); + } + + @Operation(summary = "Get User by Inum", description = "Get User by Inum", operationId = "get-user-by-inum", tags = { + "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { + ApiAccessConstants.USER_READ_ACCESS })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class, description = "CustomUser identified by inum"), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "404", description = "Not Found"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @GET + @ProtectedApi(scopes = { ApiAccessConstants.USER_READ_ACCESS }) + @Path(ApiConstants.INUM_PATH) + public Response getUserByInum( + @Parameter(description = "User identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) + throws IllegalAccessException, InvocationTargetException { + if (logger.isDebugEnabled()) { + logger.debug("User search by inum:{}", escapeLog(inum)); + } + User user = userMgmtSrv.getUserBasedOnInum(inum); + checkResourceNotNull(user, USER); + logger.debug("user:{}", user); + + // excludedAttributes + user = excludeUserAttributes(user); + logger.debug("user:{}", user); + + // get custom user + CustomUser customUser = getCustomUser(user, true); + logger.debug("customUser:{}", customUser); + + return Response.ok(customUser).build(); + } + + @Operation(summary = "Create new User", description = "Create new User", operationId = "post-user", tags = { + "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { + ApiAccessConstants.USER_WRITE_ACCESS })) + @RequestBody(description = "User object", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user-post.json"))) + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "Created", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class, description = "Created Object"), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @POST + @ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS }) + public Response createUser(@Valid CustomUser customUser, + @Parameter(description = "Boolean flag to indicate if attributes to be removed for non-LDAP DB. Default value is true, indicating non-LDAP attributes will be removed from request.") @DefaultValue("true") @QueryParam(value = ApiConstants.REMOVE_NON_LDAP_ATTRIBUTES) boolean removeNonLDAPAttributes) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + if (logger.isDebugEnabled()) { + logger.debug("User details to be added - customUser:{}, removeNonLDAPAttributes:{}", escapeLog(customUser), + removeNonLDAPAttributes); + } + + // get User object + User user = setUserAttributes(customUser); + + // parse birthdate if present + userMgmtSrv.parseBirthDateAttribute(user); + logger.debug("Create user:{}", user); + + // checking mandatory attributes + checkMissingAttributes(user, null); + ignoreCustomAttributes(user, removeNonLDAPAttributes); + + user = userMgmtSrv.addUser(user, true); + logger.debug("User created {}", user); + + // excludedAttributes + user = excludeUserAttributes(user); + + // get custom user + customUser = getCustomUser(user, removeNonLDAPAttributes); + logger.debug("newly created customUser:{}", customUser); + + return Response.status(Response.Status.CREATED).entity(customUser).build(); + } + + @Operation(summary = "Update User", description = "Update User", operationId = "put-user", tags = { + "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { + ApiAccessConstants.USER_WRITE_ACCESS })) + @RequestBody(description = "User object", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user.json"))) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "404", description = "Not Found"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @PUT + @ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS }) + public Response updateUser(@Valid CustomUser customUser, + @Parameter(description = "Boolean flag to indicate if attributes to be removed for non-LDAP DB. Default value is true, indicating non-LDAP attributes will be removed from request.") @DefaultValue("true") @QueryParam(value = ApiConstants.REMOVE_NON_LDAP_ATTRIBUTES) boolean removeNonLDAPAttributes) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + if (logger.isDebugEnabled()) { + logger.debug("User details to be updated - customUser:{}, removeNonLDAPAttributes:{}", + escapeLog(customUser), removeNonLDAPAttributes); + } + + // get User object + User user = setUserAttributes(customUser); + + // parse birthdate if present + userMgmtSrv.parseBirthDateAttribute(user); + logger.debug("Create user:{}", user); + + // checking mandatory attributes + List excludeAttributes = List.of(USER_PWD); + checkMissingAttributes(user, excludeAttributes); + ignoreCustomAttributes(user, removeNonLDAPAttributes); + + try { + user = userMgmtSrv.updateUser(user); + logger.debug("Updated user:{}", user); + } catch (Exception ex) { + logger.error("Error while updating user", ex); + throwInternalServerException(ex); + } + + // excludedAttributes + user = excludeUserAttributes(user); + + // get custom user + customUser = getCustomUser(user, removeNonLDAPAttributes); + logger.debug("updated customUser:{}", customUser); + + return Response.ok(customUser).build(); + + } + + @Operation(summary = "Patch user properties by Inum", description = "Patch user properties by Inum", operationId = "patch-user-by-inum", tags = { + "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { + ApiAccessConstants.USER_WRITE_ACCESS })) + @RequestBody(description = "UserPatchRequest", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = UserPatchRequest.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user-patch.json"))) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class, description = "Patched CustomUser Object"), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "404", description = "Not Found"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @PATCH + @ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS }) + @Path(ApiConstants.INUM_PATH) + public Response patchUser( + @Parameter(description = "User identifier") @PathParam(ApiConstants.INUM) @NotNull String inum, + @NotNull UserPatchRequest userPatchRequest, + @Parameter(description = "Boolean flag to indicate if attributes to be removed for non-LDAP DB. Default value is true, indicating non-LDAP attributes will be removed from request.") @DefaultValue("true") @QueryParam(value = ApiConstants.REMOVE_NON_LDAP_ATTRIBUTES) boolean removeNonLDAPAttributes) + throws IllegalAccessException, InvocationTargetException, JsonPatchException, IOException { + if (logger.isDebugEnabled()) { + logger.debug("User:{} to be patched with :{}, removeNonLDAPAttributes:{} ", escapeLog(inum), + escapeLog(userPatchRequest), removeNonLDAPAttributes); + } + // check if user exists + User existingUser = userMgmtSrv.getUserBasedOnInum(inum); + + // parse birthdate if present + userMgmtSrv.parseBirthDateAttribute(existingUser); + checkResourceNotNull(existingUser, USER); + ignoreCustomAttributes(existingUser, removeNonLDAPAttributes); + + // patch user + existingUser = userMgmtSrv.patchUser(inum, userPatchRequest); + logger.debug("Patched user:{}", existingUser); + + // excludedAttributes + existingUser = excludeUserAttributes(existingUser); + + // get custom user + CustomUser customUser = getCustomUser(existingUser, removeNonLDAPAttributes); + logger.debug("patched customUser:{}", customUser); + + return Response.ok(customUser).build(); + } + + @Operation(summary = "Delete User", description = "Delete User", operationId = "delete-user", tags = { + "Configuration – User Management" }, security = @SecurityRequirement(name = "oauth2", scopes = { + ApiAccessConstants.USER_DELETE_ACCESS })) + @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "No Content"), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "404", description = "Not Found"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @DELETE + @Path(ApiConstants.INUM_PATH) + @ProtectedApi(scopes = { ApiAccessConstants.USER_DELETE_ACCESS }) + public Response deleteUser( + @Parameter(description = "User identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) { + if (logger.isDebugEnabled()) { + logger.debug("User to be deleted - inum:{} ", escapeLog(inum)); + } + User user = userMgmtSrv.getUserBasedOnInum(inum); + checkResourceNotNull(user, USER); + userMgmtSrv.removeUser(user); + return Response.noContent().build(); + } + + private UserPagedResult doSearch(SearchRequest searchReq, Boolean removeNonLDAPAttributes) + throws IllegalAccessException, InvocationTargetException { + if (logger.isDebugEnabled()) { + logger.debug("User search params - searchReq:{}, removeNonLDAPAttributes:{} ", escapeLog(searchReq), + removeNonLDAPAttributes); + } + + PagedResult pagedResult = userMgmtSrv.searchUsers(searchReq); + if (logger.isTraceEnabled()) { + logger.debug("PagedResult - pagedResult:{}", pagedResult); + } + + UserPagedResult pagedCustomUser = new UserPagedResult(); + if (pagedResult != null) { + logger.debug("Users fetched - pagedResult.getEntries():{}", pagedResult.getEntries()); + List users = pagedResult.getEntries(); + + // excludedAttributes + users = userMgmtSrv.excludeAttributes(users, searchReq.getExcludedAttributesStr()); + logger.debug("Users fetched - users:{}", users); + + // parse birthdate if present + users = users.stream().map(user -> userMgmtSrv.parseBirthDateAttribute(user)).collect(Collectors.toList()); + + // get customUser() + List customUsers = getCustomUserList(users, removeNonLDAPAttributes); + pagedCustomUser.setStart(pagedResult.getStart()); + pagedCustomUser.setEntriesCount(pagedResult.getEntriesCount()); + pagedCustomUser.setTotalEntriesCount(pagedResult.getTotalEntriesCount()); + pagedCustomUser.setEntries(customUsers); + } + + logger.debug("User pagedCustomUser:{}", pagedCustomUser); + return pagedCustomUser; + + } + + private User excludeUserAttributes(User user) throws IllegalAccessException, InvocationTargetException { + return userMgmtSrv.excludeAttributes(user, userMgmtSrv.getUserExclusionAttributesAsString()); + } + + private void checkMissingAttributes(User user, List excludeAttributes) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + String missingAttributes = userMgmtSrv.checkMandatoryFields(user, excludeAttributes); + logger.debug("missingAttributes:{}", missingAttributes); + + if (StringHelper.isEmpty(missingAttributes)) { + return; + } + + throwMissingAttributeError(missingAttributes); + } + + private List getCustomUserList(List users, Boolean removeNonLDAPAttributes) { + List customUserList = new ArrayList<>(); + if (users == null || users.isEmpty()) { + return customUserList; + } + + for (User user : users) { + CustomUser customUser = new CustomUser(); + setParentAttributes(customUser, user, removeNonLDAPAttributes); + customUserList.add(customUser); + ignoreCustomAttributes(customUser, removeNonLDAPAttributes); + } + logger.debug("Custom Users - customUserList:{}", customUserList); + return customUserList; + } + + private CustomUser getCustomUser(User user, Boolean ignoreCustomAttributes) { + CustomUser customUser = new CustomUser(); + if (user == null) { + return customUser; + } + setParentAttributes(customUser, user, ignoreCustomAttributes); + logger.debug("Custom User - customUser:{}", customUser); + return customUser; + } + + public CustomUser setParentAttributes(CustomUser customUser, User user, Boolean removeNonLDAPAttributes) { + customUser.setBaseDn(user.getBaseDn()); + customUser.setCreatedAt(user.getCreatedAt()); + customUser.setCustomAttributes(user.getCustomAttributes()); + customUser.setCustomObjectClasses(user.getCustomObjectClasses()); + customUser.setDn(user.getDn()); + customUser.setOxAuthPersistentJwt(user.getOxAuthPersistentJwt()); + customUser.setUpdatedAt(user.getUpdatedAt()); + customUser.setUserId(user.getUserId()); + + ignoreCustomAttributes(customUser, removeNonLDAPAttributes); + return setCustomUserAttributes(customUser, user); + } + + public CustomUser setCustomUserAttributes(CustomUser customUser, User user) { + customUser.setMail(user.getAttribute(MAIL)); + customUser.setDisplayName(user.getAttribute(DISPLAY_NAME)); + customUser.setJansStatus(user.getAttribute(JANS_STATUS)); + customUser.setGivenName(user.getAttribute(GIVEN_NAME)); + customUser.setUserPassword(user.getAttribute(USER_PWD)); + customUser.setInum(user.getAttribute(INUM)); + + customUser.removeAttribute(MAIL); + customUser.removeAttribute(DISPLAY_NAME); + customUser.removeAttribute(JANS_STATUS); + customUser.removeAttribute(GIVEN_NAME); + customUser.removeAttribute(USER_PWD); + customUser.removeAttribute(INUM); + + return customUser; + } + + private User setUserAttributes(CustomUser customUser) { + User user = new User(); + user.setBaseDn(customUser.getBaseDn()); + user.setCreatedAt(customUser.getCreatedAt()); + user.setCustomAttributes(customUser.getCustomAttributes()); + user.setCustomObjectClasses(customUser.getCustomObjectClasses()); + user.setDn(customUser.getDn()); + user.setOxAuthPersistentJwt(customUser.getOxAuthPersistentJwt()); + user.setUpdatedAt(customUser.getUpdatedAt()); + user.setUserId(customUser.getUserId()); + return setUserCustomAttributes(customUser, user); + } + + private User setUserCustomAttributes(CustomUser customUser, User user) { + user.setAttribute(MAIL, customUser.getMail(), false); + user.setAttribute(DISPLAY_NAME, customUser.getDisplayName(), false); + user.setAttribute(JANS_STATUS, customUser.getJansStatus(), false); + user.setAttribute(GIVEN_NAME, customUser.getGivenName(), false); + user.setAttribute(USER_PWD, customUser.getUserPassword(), false); + user.setAttribute(INUM, customUser.getInum(), false); + + logger.debug("Custom User - user:{}", user); + return user; + } + + private User ignoreCustomAttributes(User user, Boolean removeNonLDAPAttributes) { + logger.info("validate User CustomObjectClasses - User user:{}, removeNonLDAPAttributes:{}", user, + removeNonLDAPAttributes); + if (user == null || (user.getCustomObjectClasses() == null || user.getCustomObjectClasses().length == 0)) { + return user; + } + + logger.trace("user.getCustomObjectClasses():{}, userMgmtSrv.getPersistenceType():{}, userMgmtSrv.isLDAP():?{}", + user.getCustomObjectClasses(), userMgmtSrv.getPersistenceType(), userMgmtSrv.isLDAP()); + + if (removeNonLDAPAttributes) { + return userMgmtSrv.ignoreCustomObjectClassesForNonLDAP(user); + } + return user; + + } } \ No newline at end of file diff --git a/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/service/UserMgmtService.java b/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/service/UserMgmtService.java index 1fc7461ad6e..7b6f03dc57e 100644 --- a/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/service/UserMgmtService.java +++ b/jans-config-api/plugins/user-mgt-plugin/src/main/java/io/jans/configapi/plugin/mgt/service/UserMgmtService.java @@ -37,316 +37,316 @@ @Named("userMgmtSrv") public class UserMgmtService { - @Inject - private Logger logger; - - @Inject - private StaticConfiguration staticConfiguration; - - @Inject - private AppConfiguration appConfiguration; - - @Inject - ConfigurationService configurationService; - - @Inject - PersistenceEntryManager persistenceEntryManager; - - @Inject - AuthUtil authUtil; - - @Inject - MgtUtil mgtUtil; - - @Inject - ConfigUserService userService; - - private static final String BIRTH_DATE = "birthdate"; - - public String getPeopleBaseDn() { - return userService.getPeopleBaseDn(); - } - - public PagedResult searchUsers(SearchRequest searchRequest) { - if (logger.isDebugEnabled()) { - logger.debug("Search Users with searchRequest:{}, getPeopleBaseDn():{}", escapeLog(searchRequest), - getPeopleBaseDn()); - } - Filter searchFilter = null; - List filters = new ArrayList<>(); - if (searchRequest.getFilterAssertionValue() != null && !searchRequest.getFilterAssertionValue().isEmpty()) { - - for (String assertionValue : searchRequest.getFilterAssertionValue()) { - String[] targetArray = new String[] { assertionValue }; - Filter displayNameFilter = Filter.createSubstringFilter(AttributeConstants.DISPLAY_NAME, null, - targetArray, null); - Filter descriptionFilter = Filter.createSubstringFilter(AttributeConstants.DESCRIPTION, null, - targetArray, null); - Filter uidFilter = Filter.createSubstringFilter("uid", null, targetArray, null); - Filter inumFilter = Filter.createSubstringFilter(AttributeConstants.INUM, null, targetArray, null); - filters.add(Filter.createORFilter(displayNameFilter, descriptionFilter, uidFilter, inumFilter)); - } - searchFilter = Filter.createORFilter(filters); - } - logger.debug("Users searchFilter:{}", searchFilter); - return persistenceEntryManager.findPagedEntries(userService.getPeopleBaseDn(), User.class, searchFilter, null, - searchRequest.getSortBy(), SortOrder.getByValue(searchRequest.getSortOrder()), - searchRequest.getStartIndex(), searchRequest.getCount(), searchRequest.getMaxCount()); - - } - - public void removeUser(User user) { - persistenceEntryManager.removeRecursively(user.getDn(), User.class); - } - - public User patchUser(String inum, UserPatchRequest userPatchRequest) throws JsonPatchException, IOException { - if (logger.isDebugEnabled()) { - logger.debug("Details to patch user inum:{}, UserPatchRequest:{} ", escapeLog(inum), - escapeLog(userPatchRequest)); - } - if (StringHelper.isEmpty(inum)) { - return null; - } - - User user = userService.getUserByInum(inum); - if (user == null) { - return null; - } - - logger.debug("User to be patched- user:{}", user); - // apply direct patch for basic attributes - if (StringUtils.isNotEmpty(userPatchRequest.getJsonPatchString())) { - logger.debug("Patch basic attributes"); - user = Jackson.applyPatch(userPatchRequest.getJsonPatchString(), user); - logger.debug("User after patching basic attributes - user:{}", user); - } - - // patch for customAttributes - if (userPatchRequest.getCustomAttributes() != null && !userPatchRequest.getCustomAttributes().isEmpty()) { - updateCustomAttributes(user, userPatchRequest.getCustomAttributes()); - } - - logger.debug("User before patch user:{}", user); - - // persist user - ignoreCustomObjectClassesForNonLDAP(user); - user = userService.updateUser(user); - logger.debug("User after patch user:{}", user); - return user; - - } - - public User getUserBasedOnInum(String inum) { - User result = null; - try { - result = userService.getUserByInum(inum); - } catch (Exception ex) { - logger.error("Failed to load user entry", ex); - } - return result; - } - - private User updateCustomAttributes(User user, List customAttributes) { - logger.debug("Custom Attributes to update for - user:{}, customAttributes:{} ", user, customAttributes); - - if (customAttributes == null || customAttributes.isEmpty()) { - return user; - } - - for (CustomObjectAttribute attribute : customAttributes) { - CustomObjectAttribute existingAttribute = userService.getCustomAttribute(user, attribute.getName()); - logger.debug("Existing CustomAttributes with existingAttribute:{} ", existingAttribute); - - // add - if (existingAttribute == null) { - boolean result = userService.addUserAttribute(user, attribute.getName(), attribute.getValues(), - attribute.isMultiValued()); - logger.debug("Result of adding CustomAttributes attribute:{} , result:{} ", attribute, result); - } - // remove attribute - else if (attribute.getValue() == null || attribute.getValues() == null) { - - user.removeAttribute(attribute.getName()); - } - // replace attribute - else { - existingAttribute.setMultiValued(attribute.isMultiValued()); - existingAttribute.setValues(attribute.getValues()); - } - // Final attribute - logger.debug("Finally user CustomAttributes user.getCustomAttributes:{} ", user.getCustomAttributes()); - - } - - return user; - } - - public List excludeAttributes(List users, String commaSeparatedString) - throws IllegalAccessException, InvocationTargetException { - logger.debug("Attributes:{} to be excluded from users:{} ", commaSeparatedString, users); - - if (users == null || users.isEmpty() || StringUtils.isEmpty(commaSeparatedString)) { - return users; - } - - for (User user : users) { - excludeAttributes(user, commaSeparatedString); - } - logger.debug("Users:{} after excluding attribute:{} ", users, commaSeparatedString); - - return users; - } - - public User excludeAttributes(User user, String commaSeparatedString) - throws IllegalAccessException, InvocationTargetException { - logger.debug("Attributes:{} to be excluded from user:{} ", commaSeparatedString, user); - - if (user == null || StringUtils.isEmpty(commaSeparatedString)) { - return user; - } - - List excludedAttributes = Arrays.asList(commaSeparatedString.split(",")); - logger.debug("Attributes List:{} to be excluded ", excludedAttributes); - - List allFields = authUtil.getAllFields(user.getClass()); - logger.debug("All user fields :{} ", allFields); - - HashMap map = new HashMap<>(); - for (String attribute : excludedAttributes) { - logger.debug("User class allFields:{} conatins attribute:{} ? :{} ", allFields, attribute, - authUtil.containsField(allFields, attribute)); - if (authUtil.containsField(allFields, attribute)) { - logger.debug("User class contains attribute:{} ! ", attribute); - map.put(attribute, null); - } else { - logger.debug("Removing custom attribute:{} from user:{} ", attribute, user); - user.removeAttribute(attribute); - } - } - - logger.debug("Attributes map:{} to be excluded ", map); - if (!map.isEmpty()) { - logger.debug("Removing simple attributes:{} from user object ", map); - BeanUtilsBean.getInstance().getConvertUtils().register(false, false, 0); - BeanUtils.populate(user, map); - } - - return user; - } - - public String getUserExclusionAttributesAsString() { - return authUtil.getUserExclusionAttributesAsString(); - } - - public String checkMandatoryFields(User user, List excludeAttributes) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - List mandatoryAttributes = authUtil.getUserMandatoryAttributes(); - logger.debug("mandatoryAttributess :{}, excludeAttributes:{} ", mandatoryAttributes, excludeAttributes); - - StringBuilder missingAttributes = new StringBuilder(); - - if (mandatoryAttributes == null || mandatoryAttributes.isEmpty()) { - return missingAttributes.toString(); - } - - List allFields = authUtil.getAllFields(user.getClass()); - logger.debug("All user fields :{} ", allFields); - - Object attributeValue = null; - for (String attribute : mandatoryAttributes) { - logger.debug("User class allFields:{} conatins attribute:{} ? :{} ", allFields, attribute, - authUtil.containsField(allFields, attribute)); - - // check if to be excluded - if (isExcludedAttribute(excludeAttributes, attribute)) { - logger.debug("Not checking if the attribute:{} is missing as it's in excludeAttributes:{}", attribute, - excludeAttributes); - continue; - } - - if (authUtil.containsField(allFields, attribute)) { - logger.debug("Checking if attribute:{} is simple attribute", attribute); - attributeValue = BeanUtils.getProperty(user, attribute); - logger.debug("User basic attribute:{} - attributeValue:{} ", attribute, attributeValue); - } else { - logger.debug("Checking if attribute:{} is custom attribute", attribute); - attributeValue = user.getAttribute(attribute); - logger.debug("User custom attribute:{} - attributeValue:{} ", attribute, attributeValue); - } - - if (attributeValue == null) { - missingAttributes.append(attribute).append(","); - } - } - logger.debug("Checking mandatory missingAttributes:{} ", missingAttributes); - if (missingAttributes.length() > 0) { - missingAttributes.replace(missingAttributes.lastIndexOf(","), missingAttributes.length(), ""); - } - - logger.debug("Returning missingAttributes:{} ", missingAttributes); - return missingAttributes.toString(); - } - - private boolean isExcludedAttribute(List excludeAttributes, String attribute) { - logger.debug(" Is attribute:{} in excludeAttributeList:{} ", attribute, excludeAttributes); - - if (excludeAttributes == null || excludeAttributes.isEmpty()) { - return false; - } - - return excludeAttributes.stream().anyMatch(e -> e.equals(attribute)); - } - - public User parseBirthDateAttribute(User user) { - if (user.getAttributeObjectValues(BIRTH_DATE) != null) { - - Optional optionalBithdate = user.getAttributeObjectValues(BIRTH_DATE).stream().findFirst(); - - if (!optionalBithdate.isPresent()) { - return user; - } - - Date date = mgtUtil.parseStringToDateObj(optionalBithdate.get().toString()); - // parse date with persistenceEntryManager.decodeTime if it is null - if (date == null) { - date = persistenceEntryManager.decodeTime(null, optionalBithdate.get().toString()); - } - user.getCustomAttributes().remove(new CustomObjectAttribute(BIRTH_DATE)); - user.getCustomAttributes().add(new CustomObjectAttribute(BIRTH_DATE, date)); - } - return user; - } - - public User ignoreCustomObjectClassesForNonLDAP(User user) { - String persistenceType = configurationService.getPersistenceType(); - logger.debug("persistenceType: {}, isLDAP?:{}", persistenceType, isLDAP()); - if (!isLDAP()) { - logger.debug( - "Setting CustomObjectClasses :{} to null as its used only for LDAP and current persistenceType is {} ", - user.getCustomObjectClasses(), persistenceType); - user.setCustomObjectClasses(null); - } - return user; - } - - public boolean isLDAP() { - String persistenceType = getPersistenceType(); - logger.debug("persistenceType: {}", persistenceType); - if (PersistenceEntryManager.PERSITENCE_TYPES.ldap.name().equals(persistenceType)) { - return true; - } - return false; - } - - public String getPersistenceType() { - return configurationService.getPersistenceType(); - } - - public User addUser(User user, boolean active) { - return userService.addUser(user, active); - } - - public User updateUser(User user) { - return userService.updateUser(user); - } + @Inject + private Logger logger; + + @Inject + private StaticConfiguration staticConfiguration; + + @Inject + private AppConfiguration appConfiguration; + + @Inject + ConfigurationService configurationService; + + @Inject + PersistenceEntryManager persistenceEntryManager; + + @Inject + AuthUtil authUtil; + + @Inject + MgtUtil mgtUtil; + + @Inject + ConfigUserService userService; + + private static final String BIRTH_DATE = "birthdate"; + + public String getPeopleBaseDn() { + return userService.getPeopleBaseDn(); + } + + public PagedResult searchUsers(SearchRequest searchRequest) { + if (logger.isDebugEnabled()) { + logger.debug("Search Users with searchRequest:{}, getPeopleBaseDn():{}", escapeLog(searchRequest), + getPeopleBaseDn()); + } + Filter searchFilter = null; + List filters = new ArrayList<>(); + if (searchRequest.getFilterAssertionValue() != null && !searchRequest.getFilterAssertionValue().isEmpty()) { + + for (String assertionValue : searchRequest.getFilterAssertionValue()) { + String[] targetArray = new String[] { assertionValue }; + Filter displayNameFilter = Filter.createSubstringFilter(AttributeConstants.DISPLAY_NAME, null, + targetArray, null); + Filter descriptionFilter = Filter.createSubstringFilter(AttributeConstants.DESCRIPTION, null, + targetArray, null); + Filter uidFilter = Filter.createSubstringFilter("uid", null, targetArray, null); + Filter inumFilter = Filter.createSubstringFilter(AttributeConstants.INUM, null, targetArray, null); + filters.add(Filter.createORFilter(displayNameFilter, descriptionFilter, uidFilter, inumFilter)); + } + searchFilter = Filter.createORFilter(filters); + } + logger.debug("Users searchFilter:{}", searchFilter); + return persistenceEntryManager.findPagedEntries(userService.getPeopleBaseDn(), User.class, searchFilter, null, + searchRequest.getSortBy(), SortOrder.getByValue(searchRequest.getSortOrder()), + searchRequest.getStartIndex(), searchRequest.getCount(), searchRequest.getMaxCount()); + + } + + public void removeUser(User user) { + persistenceEntryManager.removeRecursively(user.getDn(), User.class); + } + + public User patchUser(String inum, UserPatchRequest userPatchRequest) throws JsonPatchException, IOException { + if (logger.isDebugEnabled()) { + logger.debug("Details to patch user inum:{}, UserPatchRequest:{} ", escapeLog(inum), + escapeLog(userPatchRequest)); + } + if (StringHelper.isEmpty(inum)) { + return null; + } + + User user = userService.getUserByInum(inum); + if (user == null) { + return null; + } + + logger.debug("User to be patched- user:{}", user); + // apply direct patch for basic attributes + if (StringUtils.isNotEmpty(userPatchRequest.getJsonPatchString())) { + logger.debug("Patch basic attributes"); + user = Jackson.applyPatch(userPatchRequest.getJsonPatchString(), user); + logger.debug("User after patching basic attributes - user:{}", user); + } + + // patch for customAttributes + if (userPatchRequest.getCustomAttributes() != null && !userPatchRequest.getCustomAttributes().isEmpty()) { + updateCustomAttributes(user, userPatchRequest.getCustomAttributes()); + } + + logger.debug("User before patch user:{}", user); + + // persist user + ignoreCustomObjectClassesForNonLDAP(user); + user = userService.updateUser(user); + logger.debug("User after patch user:{}", user); + return user; + + } + + public User getUserBasedOnInum(String inum) { + User result = null; + try { + result = userService.getUserByInum(inum); + } catch (Exception ex) { + logger.error("Failed to load user entry", ex); + } + return result; + } + + private User updateCustomAttributes(User user, List customAttributes) { + logger.debug("Custom Attributes to update for - user:{}, customAttributes:{} ", user, customAttributes); + + if (customAttributes == null || customAttributes.isEmpty()) { + return user; + } + + for (CustomObjectAttribute attribute : customAttributes) { + CustomObjectAttribute existingAttribute = userService.getCustomAttribute(user, attribute.getName()); + logger.debug("Existing CustomAttributes with existingAttribute:{} ", existingAttribute); + + // add + if (existingAttribute == null) { + boolean result = userService.addUserAttribute(user, attribute.getName(), attribute.getValues(), + attribute.isMultiValued()); + logger.debug("Result of adding CustomAttributes attribute:{} , result:{} ", attribute, result); + } + // remove attribute + else if (attribute.getValue() == null || attribute.getValues() == null) { + + user.removeAttribute(attribute.getName()); + } + // replace attribute + else { + existingAttribute.setMultiValued(attribute.isMultiValued()); + existingAttribute.setValues(attribute.getValues()); + } + // Final attribute + logger.debug("Finally user CustomAttributes user.getCustomAttributes:{} ", user.getCustomAttributes()); + + } + + return user; + } + + public List excludeAttributes(List users, String commaSeparatedString) + throws IllegalAccessException, InvocationTargetException { + logger.debug("Attributes:{} to be excluded from users:{} ", commaSeparatedString, users); + + if (users == null || users.isEmpty() || StringUtils.isEmpty(commaSeparatedString)) { + return users; + } + + for (User user : users) { + excludeAttributes(user, commaSeparatedString); + } + logger.debug("Users:{} after excluding attribute:{} ", users, commaSeparatedString); + + return users; + } + + public User excludeAttributes(User user, String commaSeparatedString) + throws IllegalAccessException, InvocationTargetException { + logger.debug("Attributes:{} to be excluded from user:{} ", commaSeparatedString, user); + + if (user == null || StringUtils.isEmpty(commaSeparatedString)) { + return user; + } + + List excludedAttributes = Arrays.asList(commaSeparatedString.split(",")); + logger.debug("Attributes List:{} to be excluded ", excludedAttributes); + + List allFields = authUtil.getAllFields(user.getClass()); + logger.debug("All user fields :{} ", allFields); + + HashMap map = new HashMap<>(); + for (String attribute : excludedAttributes) { + logger.debug("User class allFields:{} conatins attribute:{} ? :{} ", allFields, attribute, + authUtil.containsField(allFields, attribute)); + if (authUtil.containsField(allFields, attribute)) { + logger.debug("User class contains attribute:{} ! ", attribute); + map.put(attribute, null); + } else { + logger.debug("Removing custom attribute:{} from user:{} ", attribute, user); + user.removeAttribute(attribute); + } + } + + logger.debug("Attributes map:{} to be excluded ", map); + if (!map.isEmpty()) { + logger.debug("Removing simple attributes:{} from user object ", map); + BeanUtilsBean.getInstance().getConvertUtils().register(false, false, 0); + BeanUtils.populate(user, map); + } + + return user; + } + + public String getUserExclusionAttributesAsString() { + return authUtil.getUserExclusionAttributesAsString(); + } + + public String checkMandatoryFields(User user, List excludeAttributes) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + List mandatoryAttributes = authUtil.getUserMandatoryAttributes(); + logger.debug("mandatoryAttributess :{}, excludeAttributes:{} ", mandatoryAttributes, excludeAttributes); + + StringBuilder missingAttributes = new StringBuilder(); + + if (mandatoryAttributes == null || mandatoryAttributes.isEmpty()) { + return missingAttributes.toString(); + } + + List allFields = authUtil.getAllFields(user.getClass()); + logger.debug("All user fields :{} ", allFields); + + Object attributeValue = null; + for (String attribute : mandatoryAttributes) { + logger.debug("User class allFields:{} conatins attribute:{} ? :{} ", allFields, attribute, + authUtil.containsField(allFields, attribute)); + + // check if to be excluded + if (isExcludedAttribute(excludeAttributes, attribute)) { + logger.debug("Not checking if the attribute:{} is missing as it's in excludeAttributes:{}", attribute, + excludeAttributes); + continue; + } + + if (authUtil.containsField(allFields, attribute)) { + logger.debug("Checking if attribute:{} is simple attribute", attribute); + attributeValue = BeanUtils.getProperty(user, attribute); + logger.debug("User basic attribute:{} - attributeValue:{} ", attribute, attributeValue); + } else { + logger.debug("Checking if attribute:{} is custom attribute", attribute); + attributeValue = user.getAttribute(attribute); + logger.debug("User custom attribute:{} - attributeValue:{} ", attribute, attributeValue); + } + + if (attributeValue == null) { + missingAttributes.append(attribute).append(","); + } + } + logger.debug("Checking mandatory missingAttributes:{} ", missingAttributes); + if (missingAttributes.length() > 0) { + missingAttributes.replace(missingAttributes.lastIndexOf(","), missingAttributes.length(), ""); + } + + logger.debug("Returning missingAttributes:{} ", missingAttributes); + return missingAttributes.toString(); + } + + private boolean isExcludedAttribute(List excludeAttributes, String attribute) { + logger.debug(" Is attribute:{} in excludeAttributeList:{} ", attribute, excludeAttributes); + + if (excludeAttributes == null || excludeAttributes.isEmpty()) { + return false; + } + + return excludeAttributes.stream().anyMatch(e -> e.equals(attribute)); + } + + public User parseBirthDateAttribute(User user) { + if (user.getAttributeObjectValues(BIRTH_DATE) != null) { + + Optional optionalBithdate = user.getAttributeObjectValues(BIRTH_DATE).stream().findFirst(); + + if (!optionalBithdate.isPresent()) { + return user; + } + + Date date = mgtUtil.parseStringToDateObj(optionalBithdate.get().toString()); + // parse date with persistenceEntryManager.decodeTime if it is null + if (date == null) { + date = persistenceEntryManager.decodeTime(null, optionalBithdate.get().toString()); + } + user.getCustomAttributes().remove(new CustomObjectAttribute(BIRTH_DATE)); + user.getCustomAttributes().add(new CustomObjectAttribute(BIRTH_DATE, date)); + } + return user; + } + + public User ignoreCustomObjectClassesForNonLDAP(User user) { + String persistenceType = configurationService.getPersistenceType(); + logger.debug("persistenceType: {}, isLDAP?:{}", persistenceType, isLDAP()); + if (!isLDAP()) { + logger.debug( + "Setting CustomObjectClasses :{} to null as its used only for LDAP and current persistenceType is {} ", + user.getCustomObjectClasses(), persistenceType); + user.setCustomObjectClasses(null); + } + return user; + } + + public boolean isLDAP() { + String persistenceType = getPersistenceType(); + logger.debug("persistenceType: {}", persistenceType); + if (PersistenceEntryManager.PERSITENCE_TYPES.ldap.name().equals(persistenceType)) { + return true; + } + return false; + } + + public String getPersistenceType() { + return configurationService.getPersistenceType(); + } + + public User addUser(User user, boolean active) { + return userService.addUser(user, active); + } + + public User updateUser(User user) { + return userService.updateUser(user); + } } diff --git a/jans-config-api/server/src/main/java/io/jans/configapi/rest/resource/auth/AgamaDeploymentsResource.java b/jans-config-api/server/src/main/java/io/jans/configapi/rest/resource/auth/AgamaDeploymentsResource.java index 1273b9536a0..b9e8874e142 100644 --- a/jans-config-api/server/src/main/java/io/jans/configapi/rest/resource/auth/AgamaDeploymentsResource.java +++ b/jans-config-api/server/src/main/java/io/jans/configapi/rest/resource/auth/AgamaDeploymentsResource.java @@ -8,6 +8,7 @@ import io.jans.as.model.util.Pair; import io.jans.orm.model.PagedResult; 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.ExampleObject; import io.swagger.v3.oas.annotations.media.Schema; @@ -22,6 +23,7 @@ import jakarta.annotation.PostConstruct; import jakarta.inject.Inject; +import jakarta.validation.constraints.NotNull; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -44,18 +46,15 @@ public class AgamaDeploymentsResource extends ConfigBaseResource { private ObjectMapper mapper; @Operation(summary = "Retrieve the list of projects deployed currently.", description = "Retrieve the list of projects deployed currently.", operationId = "get-agama-dev-prj", tags = { - "Agama - Developer Studio" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.AGAMA_READ_ACCESS, ApiAccessConstants.AGAMA_WRITE_ACCESS, - ApiAccessConstants.SUPER_ADMIN_READ_ACCESS })) + "Agama - Developer Studio" }, security = @SecurityRequirement(name = "oauth2", scopes = {ApiAccessConstants.AGAMA_READ_ACCESS})) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Agama projects", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = PagedResult.class), examples = @ExampleObject(name = "Response json example", value = "example/agama/agama-dev-prj-get-all.json"))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "500", description = "InternalServerError") }) @GET - @Path("list") - @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_READ_ACCESS }, groupScopes = { - ApiAccessConstants.AGAMA_WRITE_ACCESS }, superScopes = { ApiAccessConstants.SUPER_ADMIN_READ_ACCESS }) + @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_READ_ACCESS }, groupScopes = {ApiAccessConstants.AGAMA_WRITE_ACCESS}, superScopes = { ApiAccessConstants.SUPER_ADMIN_READ_ACCESS }) @Produces(MediaType.APPLICATION_JSON) + @Path("list") public Response getDeployments(@QueryParam("start") int start, @QueryParam("count") int count) { // this is NOT a search but a paged listing @@ -67,9 +66,7 @@ public Response getDeployments(@QueryParam("start") int start, @QueryParam("coun } @Operation(summary = "Fetches deployed Agama project based on name.", description = "Fetches deployed Agama project based on name.", operationId = "get-agama-dev-studio-prj-by-name", tags = { - "Agama - Developer Studio" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.AGAMA_READ_ACCESS, ApiAccessConstants.AGAMA_WRITE_ACCESS, - ApiAccessConstants.SUPER_ADMIN_READ_ACCESS })) + "Agama - Developer Studio" }, security = @SecurityRequirement(name = "oauth2", scopes = {ApiAccessConstants.AGAMA_READ_ACCESS} )) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Agama project", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Deployment.class), examples = @ExampleObject(name = "Response json example", value = "example/agama/agama-dev-prj-get.json"))), @ApiResponse(responseCode = "204", description = "No Content"), @@ -78,10 +75,10 @@ public Response getDeployments(@QueryParam("start") int start, @QueryParam("coun @ApiResponse(responseCode = "404", description = "Not Found"), @ApiResponse(responseCode = "500", description = "InternalServerError") }) @GET - @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_READ_ACCESS }, groupScopes = { - ApiAccessConstants.AGAMA_WRITE_ACCESS }, superScopes = { ApiAccessConstants.SUPER_ADMIN_READ_ACCESS }) + @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_READ_ACCESS }, groupScopes = {ApiAccessConstants.AGAMA_WRITE_ACCESS}, superScopes = { ApiAccessConstants.SUPER_ADMIN_READ_ACCESS }) @Produces(MediaType.APPLICATION_JSON) - public Response getDeployment(@QueryParam("name") String projectName) { + @Path(ApiConstants.NAME_PARAM_PATH) + public Response getDeployment(@Parameter(description = "Agama project name") @PathParam(ApiConstants.NAME) @NotNull String projectName) { if (projectName == null) { return Response.status(Response.Status.BAD_REQUEST) @@ -103,9 +100,7 @@ public Response getDeployment(@QueryParam("name") String projectName) { } @Operation(summary = "Deploy an Agama project.", description = "Deploy an Agama project.", operationId = "post-agama-dev-studio-prj", tags = { - "Agama - Developer Studio" }, security = @SecurityRequirement(name = "oauth2", scopes = { - ApiAccessConstants.AGAMA_WRITE_ACCESS, - ApiAccessConstants.SUPER_ADMIN_WRITE_ACCESS })) + "Agama - Developer Studio" }, security = @SecurityRequirement(name = "oauth2", scopes = {ApiAccessConstants.AGAMA_WRITE_ACCESS})) @ApiResponses(value = { @ApiResponse(responseCode = "202", description = "Agama project accepted", content = @Content(mediaType = "application/zip", schema = @Schema(implementation = String.class), examples = @ExampleObject(name = "Response json example", value = "example/agama/agama-dev-prj-post.json"))), @ApiResponse(responseCode = "400", description = "Bad Request"), @@ -114,9 +109,10 @@ public Response getDeployment(@QueryParam("name") String projectName) { @ApiResponse(responseCode = "500", description = "InternalServerError") }) @POST @Consumes("application/zip") - @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_WRITE_ACCESS }, + @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_WRITE_ACCESS }, groupScopes = {}, superScopes = { ApiAccessConstants.SUPER_ADMIN_WRITE_ACCESS }) - public Response deploy(@QueryParam("name") String projectName, byte[] gamaBinary) { + @Path(ApiConstants.NAME_PARAM_PATH) + public Response deploy(@Parameter(description = "Agama project name") @PathParam(ApiConstants.NAME) @NotNull String projectName, byte[] gamaBinary) { if (projectName == null || gamaBinary == null) return Response.status(Response.Status.BAD_REQUEST) @@ -143,9 +139,10 @@ public Response deploy(@QueryParam("name") String projectName, byte[] gamaBinary @ApiResponse(responseCode = "409", description = "Conflict"), @ApiResponse(responseCode = "500", description = "InternalServerError") }) @DELETE - @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_DELETE_ACCESS }, + @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_DELETE_ACCESS }, groupScopes = {}, superScopes = { ApiAccessConstants.SUPER_ADMIN_DELETE_ACCESS }) - public Response undeploy(@QueryParam("name") String projectName) { + @Path(ApiConstants.NAME_PARAM_PATH) + public Response undeploy(@Parameter(description = "Agama project name") @PathParam(ApiConstants.NAME) @NotNull String projectName) { if (projectName == null) return Response.status(Response.Status.BAD_REQUEST) @@ -165,11 +162,16 @@ public Response undeploy(@QueryParam("name") String projectName) { } + @Operation(summary = "Retrieve the list of configs based on name.", description = "Retrieve the list of configs based on name.", operationId = "get-agama-dev-prj-configs", tags = { + "Agama - Developer Studio" }, security = @SecurityRequirement(name = "oauth2", scopes = {ApiAccessConstants.AGAMA_READ_ACCESS})) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Agama projects configs", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Map.class), examples = @ExampleObject(name = "Response json example", value = "example/agama/agama-dev-prj-get-configs-all.json"))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) @GET - @Path("configs") - @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_READ_ACCESS }, groupScopes = { - ApiAccessConstants.AGAMA_WRITE_ACCESS }, superScopes = { ApiAccessConstants.SUPER_ADMIN_READ_ACCESS }) - public Response getConfigs(@QueryParam("name") String projectName) throws JsonProcessingException { + @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_READ_ACCESS }, groupScopes = {ApiAccessConstants.AGAMA_WRITE_ACCESS }, superScopes = { ApiAccessConstants.SUPER_ADMIN_READ_ACCESS }) + @Path(ApiConstants.CONFIGS + ApiConstants.NAME_PARAM_PATH) + public Response getConfigs(@Parameter(description = "Agama project name") @PathParam(ApiConstants.NAME) @NotNull String projectName) throws JsonProcessingException { Pair> pair = projectFlows(projectName); Response resp = pair.getFirst(); @@ -193,12 +195,20 @@ public Response getConfigs(@QueryParam("name") String projectName) throws JsonPr } + @Operation(summary = "Update an Agama project.", description = "Update an Agama project.", operationId = "put-agama-dev-studio-prj", tags = { + "Agama - Developer Studio" }, security = @SecurityRequirement(name = "oauth2", scopes = {ApiAccessConstants.AGAMA_WRITE_ACCESS})) + @ApiResponses(value = { + @ApiResponse(responseCode = "202", description = "Agama project accepted", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Map.class), examples = @ExampleObject(name = "Response json example", value = "example/agama/agama-dev-prj-post.json"))), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "409", description = "Conflict"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) @PUT - @Path("configs") @Consumes(MediaType.APPLICATION_JSON) - @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_WRITE_ACCESS }, + @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_WRITE_ACCESS }, groupScopes = {}, superScopes = { ApiAccessConstants.SUPER_ADMIN_WRITE_ACCESS }) - public Response setConfigs(@QueryParam("name") String projectName, + @Path(ApiConstants.CONFIGS + ApiConstants.NAME_PARAM_PATH) + public Response setConfigs(@Parameter(description = "Agama project name") @PathParam(ApiConstants.NAME) @NotNull String projectName, Map> flowsConfigs) { if (flowsConfigs == null) { diff --git a/jans-config-api/server/src/test/resources/feature/agama/agama-deployment.feature b/jans-config-api/server/src/test/resources/feature/agama/agama-deployment.feature new file mode 100644 index 00000000000..af246926300 --- /dev/null +++ b/jans-config-api/server/src/test/resources/feature/agama/agama-deployment.feature @@ -0,0 +1,46 @@ + +Feature: Agama Deployment + +Background: +* def mainUrl = agama_deployment_url +* def funGetEncodedValue = +""" +function(strValue) { +print(' strValue = '+strValue); +if(strValue == null || strValue.length==0){ +return strValue; +} +var URLEncoder = Java.type('java.net.URLEncoder'); +var encodedStrValue = URLEncoder.encode(strValue, "UTF-8"); +print(' encodedStrValue = '+encodedStrValue); +return encodedStrValue; +} +""" + +Scenario: Fetch all Agama deployment without bearer token + Given url mainUrl + When method GET + Then status 401 + And print response + + +Scenario: Fetch all Agama deployment + Given url mainUrl + And print 'accessToken = '+accessToken + And header Authorization = 'Bearer ' + accessToken + When method GET + Then status 200 + And print response + +Scenario: Fetch all Agama deployment + Given url mainUrl + And print 'accessToken = '+accessToken + And header Authorization = 'Bearer ' + accessToken + And param start = 0 + And param count = 3 + When method GET + Then status 200 + And print response + + + diff --git a/jans-config-api/server/src/test/resources/karate-config-jenkins.js b/jans-config-api/server/src/test/resources/karate-config-jenkins.js index 260a59998f8..3e71531a1fc 100644 --- a/jans-config-api/server/src/test/resources/karate-config-jenkins.js +++ b/jans-config-api/server/src/test/resources/karate-config-jenkins.js @@ -64,6 +64,7 @@ function() { session_url: baseUrl + '/jans-config-api/api/v1/jans-auth-server/session', plugin_url: baseUrl + '/jans-config-api/api/v1/plugin', api_config_url: baseUrl + '/jans-config-api/api/v1/api-config', + agama_deployment_url: baseUrl + '/jans-config-api/api/v1/agama-deployment', }; karate.configure('connectTimeout', 30000); diff --git a/jans-config-api/server/src/test/resources/karate-config.js b/jans-config-api/server/src/test/resources/karate-config.js index 8c51ff66d96..1604201d52f 100644 --- a/jans-config-api/server/src/test/resources/karate-config.js +++ b/jans-config-api/server/src/test/resources/karate-config.js @@ -64,6 +64,7 @@ function() { session_url: baseUrl + '/jans-config-api/api/v1/jans-auth-server/session', plugin_url: baseUrl + '/jans-config-api/api/v1/plugin', api_config_url: baseUrl + '/jans-config-api/api/v1/api-config', + agama_deployment_url: baseUrl + '/jans-config-api/api/v1/agama-deployment', }; karate.configure('connectTimeout', 30000); From 2e3e141d24cdca10a501b6fe7cd8533d537bc714 Mon Sep 17 00:00:00 2001 From: pujavs Date: Sat, 11 Mar 2023 00:24:47 +0530 Subject: [PATCH 3/4] fix(config-api): user custom attribute changes and agama param changes --- .../docs/jans-config-api-swagger.yaml | 42 +++++++++---------- .../plugins/docs/user-mgt-plugin-swagger.yaml | 36 +++++++++++++++- .../profiles/local/test.properties | 10 ++--- .../feature/agama/agama-deployment.feature | 6 +-- 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/jans-config-api/docs/jans-config-api-swagger.yaml b/jans-config-api/docs/jans-config-api-swagger.yaml index b14ce5f0590..c7afacf6413 100644 --- a/jans-config-api/docs/jans-config-api-swagger.yaml +++ b/jans-config-api/docs/jans-config-api-swagger.yaml @@ -7616,20 +7616,20 @@ components: $ref: '#/components/schemas/AttributeValidation' tooltip: type: string + whitePagesCanView: + type: boolean adminCanAccess: type: boolean + adminCanView: + type: boolean adminCanEdit: type: boolean + userCanAccess: + type: boolean userCanEdit: type: boolean userCanView: type: boolean - adminCanView: - type: boolean - userCanAccess: - type: boolean - whitePagesCanView: - type: boolean baseDn: type: string PatchRequest: @@ -8353,8 +8353,6 @@ components: type: object additionalProperties: type: string - fapi: - type: boolean allResponseTypesSupported: uniqueItems: true type: array @@ -8364,6 +8362,8 @@ components: - code - token - id_token + fapi: + type: boolean AuthenticationFilter: required: - baseDn @@ -8886,6 +8886,17 @@ components: format: int32 displayName: type: string + authenticationMethod: + type: string + enum: + - client_secret_basic + - client_secret_post + - client_secret_jwt + - private_key_jwt + - access_token + - tls_client_auth + - self_signed_tls_client_auth + - none allAuthenticationMethods: uniqueItems: true type: array @@ -8900,17 +8911,6 @@ components: - tls_client_auth - self_signed_tls_client_auth - none - authenticationMethod: - type: string - enum: - - client_secret_basic - - client_secret_post - - client_secret_jwt - - private_key_jwt - - access_token - - tls_client_auth - - self_signed_tls_client_auth - - none baseDn: type: string inum: @@ -9235,14 +9235,14 @@ components: type: boolean internal: type: boolean - locationPath: - type: string locationType: type: string enum: - ldap - db - file + locationPath: + type: string baseDn: type: string ScriptError: diff --git a/jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml b/jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml index 98076264e6d..762ce2dd8e9 100644 --- a/jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml +++ b/jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml @@ -121,6 +121,15 @@ paths: summary: Update User description: Update User operationId: put-user + parameters: + - name: removeNonLDAPAttributes + in: query + description: "Boolean flag to indicate if attributes to be removed for non-LDAP\ + \ DB. Default value is true, indicating non-LDAP attributes will be removed\ + \ from request." + schema: + type: boolean + default: true requestBody: description: User object content: @@ -302,6 +311,8 @@ paths: "givenName": "Test3", "baseDn": "inum=559a7e26-7a33-4e11-9d42-13266d33261e,ou=people,o=jans" } + "400": + description: Bad Request "401": description: Unauthorized "404": @@ -317,6 +328,15 @@ paths: summary: Create new User description: Create new User operationId: post-user + parameters: + - name: removeNonLDAPAttributes + in: query + description: "Boolean flag to indicate if attributes to be removed for non-LDAP\ + \ DB. Default value is true, indicating non-LDAP attributes will be removed\ + \ from request." + schema: + type: boolean + default: true requestBody: description: User object content: @@ -495,6 +515,8 @@ paths: "givenName": "Test3", "baseDn": "inum=559a7e26-7a33-4e11-9d42-13266d33261e,ou=people,o=jans" } + "400": + description: Bad Request "401": description: Unauthorized "500": @@ -654,6 +676,14 @@ paths: required: true schema: type: string + - name: removeNonLDAPAttributes + in: query + description: "Boolean flag to indicate if attributes to be removed for non-LDAP\ + \ DB. Default value is true, indicating non-LDAP attributes will be removed\ + \ from request." + schema: + type: boolean + default: true requestBody: description: UserPatchRequest content: @@ -772,6 +802,8 @@ paths: "givenName": "Test3", "baseDn": "inum=559a7e26-7a33-4e11-9d42-13266d33261e,ou=people,o=jans" } + "400": + description: Bad Request "401": description: Unauthorized "404": @@ -794,10 +826,10 @@ components: type: array items: type: object - displayValue: - type: string value: type: object + displayValue: + type: string CustomUser: type: object properties: diff --git a/jans-config-api/profiles/local/test.properties b/jans-config-api/profiles/local/test.properties index fbb95638a40..d7fe54e2e6d 100644 --- a/jans-config-api/profiles/local/test.properties +++ b/jans-config-api/profiles/local/test.properties @@ -1,9 +1,9 @@ #LOCAL -test.scopes=https://jans.io/oauth/config/acrs.readonly https://jans.io/oauth/config/acrs.write https://jans.io/oauth/config/attributes.readonly https://jans.io/oauth/config/attributes.write https://jans.io/oauth/config/attributes.delete https://jans.io/oauth/config/cache.readonly https://jans.io/oauth/config/cache.write https://jans.io/oauth/config/openid/clients.readonly https://jans.io/oauth/config/openid/clients.write https://jans.io/oauth/config/openid/clients.delete https://jans.io/oauth/jans-auth-server/config/properties.readonly https://jans.io/oauth/jans-auth-server/config/properties.write https://jans.io/oauth/config/smtp.readonly https://jans.io/oauth/config/smtp.write https://jans.io/oauth/config/smtp.delete https://jans.io/oauth/config/scripts.readonly https://jans.io/oauth/config/scripts.write https://jans.io/oauth/config/scripts.delete https://jans.io/oauth/config/fido2.readonly https://jans.io/oauth/config/fido2.write https://jans.io/oauth/config/jwks.readonly https://jans.io/oauth/config/jwks.write https://jans.io/oauth/config/jwks.delete https://jans.io/oauth/config/database/ldap.readonly https://jans.io/oauth/config/database/ldap.write https://jans.io/oauth/config/database/ldap.delete https://jans.io/oauth/config/logging.readonly https://jans.io/oauth/config/logging.write https://jans.io/oauth/config/scopes.readonly https://jans.io/oauth/config/scopes.write https://jans.io/oauth/config/scopes.delete https://jans.io/oauth/config/uma/resources.readonly https://jans.io/oauth/config/uma/resources.write https://jans.io/oauth/config/uma/resources.delete https://jans.io/oauth/config/database/sql.readonly https://jans.io/oauth/config/database/sql.write https://jans.io/oauth/config/database/sql.delete https://jans.io/oauth/config/stats.readonly jans_stat https://jans.io/scim/users.read https://jans.io/scim/users.write https://jans.io/oauth/config/scim/users.read https://jans.io/oauth/config/scim/users.write https://jans.io/scim/config.readonly https://jans.io/scim/config.write https://jans.io/oauth/config/organization.readonly https://jans.io/oauth/config/organization.write https://jans.io/oauth/config/user.readonly https://jans.io/oauth/config/user.write https://jans.io/oauth/config/user.delete https://jans.io/oauth/config/agama.readonly https://jans.io/oauth/config/agama.write https://jans.io/oauth/config/agama.delete https://jans.io/oauth/jans-auth-server/session.readonly https://jans.io/oauth/jans-auth-server/session.delete revoke_session https://jans.io/oauth/config/read-all https://jans.io/oauth/config/write-all https://jans.io/oauth/config/delete-all https://jans.io/oauth/config/openid-read https://jans.io/oauth/config/openid-write https://jans.io/oauth/config/openid-delete https://jans.io/oauth/config/uma-read https://jans.io/oauth/config/uma-write https://jans.io/oauth/config/uma-delete https://jans.io/oauth/jans-auth-server/config/adminui/user/role.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/role.write https://jans.io/oauth/jans-auth-server/config/adminui/read-all https://jans.io/oauth/jans-auth-server/config/adminui/write-all https://jans.io/oauth/jans-auth-server/config/adminui/user/role.delete https://jans.io/oauth/jans-auth-server/config/adminui/delete-all https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.delete https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.write https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.delete https://jans.io/oauth/jans-auth-server/config/adminui/license.readonly https://jans.io/oauth/jans-auth-server/config/adminui/license.write https://jans.io/oauth/config/plugin.readonly +test.scopes=https://jans.io/oauth/config/acrs.readonly https://jans.io/oauth/config/acrs.write https://jans.io/oauth/config/attributes.readonly https://jans.io/oauth/config/attributes.write https://jans.io/oauth/config/attributes.delete https://jans.io/oauth/config/cache.readonly https://jans.io/oauth/config/cache.write https://jans.io/oauth/config/openid/clients.readonly https://jans.io/oauth/config/openid/clients.write https://jans.io/oauth/config/openid/clients.delete https://jans.io/oauth/jans-auth-server/config/properties.readonly https://jans.io/oauth/jans-auth-server/config/properties.write https://jans.io/oauth/config/smtp.readonly https://jans.io/oauth/config/smtp.write https://jans.io/oauth/config/smtp.delete https://jans.io/oauth/config/scripts.readonly https://jans.io/oauth/config/scripts.write https://jans.io/oauth/config/scripts.delete https://jans.io/oauth/config/fido2.readonly https://jans.io/oauth/config/fido2.write https://jans.io/oauth/config/jwks.readonly https://jans.io/oauth/config/jwks.write https://jans.io/oauth/config/jwks.delete https://jans.io/oauth/config/database/ldap.readonly https://jans.io/oauth/config/database/ldap.write https://jans.io/oauth/config/database/ldap.delete https://jans.io/oauth/config/logging.readonly https://jans.io/oauth/config/logging.write https://jans.io/oauth/config/scopes.readonly https://jans.io/oauth/config/scopes.write https://jans.io/oauth/config/scopes.delete https://jans.io/oauth/config/uma/resources.readonly https://jans.io/oauth/config/uma/resources.write https://jans.io/oauth/config/uma/resources.delete https://jans.io/oauth/config/database/sql.readonly https://jans.io/oauth/config/database/sql.write https://jans.io/oauth/config/database/sql.delete https://jans.io/oauth/config/stats.readonly jans_stat https://jans.io/scim/users.read https://jans.io/scim/users.write https://jans.io/oauth/config/scim/users.read https://jans.io/oauth/config/scim/users.write https://jans.io/scim/config.readonly https://jans.io/scim/config.write https://jans.io/oauth/config/organization.readonly https://jans.io/oauth/config/organization.write https://jans.io/oauth/config/user.readonly https://jans.io/oauth/config/user.write https://jans.io/oauth/config/user.delete https://jans.io/oauth/config/agama.readonly https://jans.io/oauth/config/agama.write https://jans.io/oauth/config/agama.delete https://jans.io/oauth/jans-auth-server/session.readonly https://jans.io/oauth/jans-auth-server/session.delete revoke_session https://jans.io/oauth/config/read-all https://jans.io/oauth/config/write-all https://jans.io/oauth/config/delete-all https://jans.io/oauth/config/openid-read https://jans.io/oauth/config/openid-write https://jans.io/oauth/config/openid-delete https://jans.io/oauth/config/uma-read https://jans.io/oauth/config/uma-write https://jans.io/oauth/config/uma-delete https://jans.io/oauth/jans-auth-server/config/adminui/user/role.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/role.write https://jans.io/oauth/jans-auth-server/config/adminui/read-all https://jans.io/oauth/jans-auth-server/config/adminui/write-all https://jans.io/oauth/jans-auth-server/config/adminui/user/role.delete https://jans.io/oauth/jans-auth-server/config/adminui/delete-all https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.delete https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.write https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.delete https://jans.io/oauth/jans-auth-server/config/adminui/license.readonly https://jans.io/oauth/jans-auth-server/config/adminui/license.write https://jans.io/oauth/config/plugin.readonly https://jans.io/oauth/client/authorizations.readonly https://jans.io/oauth/client/authorizations.delete # jans.server -token.endpoint=https://jans.server2/jans-auth/restv1/token +token.endpoint=https://jans.server1/jans-auth/restv1/token token.grant.type=client_credentials -test.client.id=1800.17bb80e1-2e30-40a9-b52c-be882f304f22 -test.client.secret=g7RbK1zVnejt -test.issuer=https://jans.server2/ \ No newline at end of file +test.client.id=1800.bf52932e-6f81-4a1b-be78-ccc0147f2a32 +test.client.secret=WBvBJiWJnfbh +test.issuer=https://jans.server1/ \ No newline at end of file diff --git a/jans-config-api/server/src/test/resources/feature/agama/agama-deployment.feature b/jans-config-api/server/src/test/resources/feature/agama/agama-deployment.feature index af246926300..df830d55fd9 100644 --- a/jans-config-api/server/src/test/resources/feature/agama/agama-deployment.feature +++ b/jans-config-api/server/src/test/resources/feature/agama/agama-deployment.feature @@ -18,14 +18,14 @@ return encodedStrValue; """ Scenario: Fetch all Agama deployment without bearer token - Given url mainUrl + Given url mainUrl + '/list' When method GET Then status 401 And print response Scenario: Fetch all Agama deployment - Given url mainUrl + Given url mainUrl + '/list' And print 'accessToken = '+accessToken And header Authorization = 'Bearer ' + accessToken When method GET @@ -33,7 +33,7 @@ Scenario: Fetch all Agama deployment And print response Scenario: Fetch all Agama deployment - Given url mainUrl + Given url mainUrl + '/list' And print 'accessToken = '+accessToken And header Authorization = 'Bearer ' + accessToken And param start = 0 From c5e458b095c4c1800e5a0503b7d0a73d9b2cdd44 Mon Sep 17 00:00:00 2001 From: pujavs Date: Sat, 11 Mar 2023 01:01:24 +0530 Subject: [PATCH 4/4] fix(config-api): user custom attribute changes and agama param changes --- .../docs/jans-config-api-swagger.yaml | 38 +++++++------------ .../auth/AgamaDeploymentsResource.java | 22 +++++------ 2 files changed, 23 insertions(+), 37 deletions(-) diff --git a/jans-config-api/docs/jans-config-api-swagger.yaml b/jans-config-api/docs/jans-config-api-swagger.yaml index c7afacf6413..739324c6aeb 100644 --- a/jans-config-api/docs/jans-config-api-swagger.yaml +++ b/jans-config-api/docs/jans-config-api-swagger.yaml @@ -173,7 +173,7 @@ paths: - oauth2: - https://jans.io/oauth/config/acrs.write - https://jans.io/oauth/config/write-all - /api/v1/agama-deployment/{name}: + /api/v1/agama-deployment: get: tags: - Agama - Developer Studio @@ -182,9 +182,7 @@ paths: operationId: get-agama-dev-studio-prj-by-name parameters: - name: name - in: path - description: Agama project name - required: true + in: query schema: type: string responses: @@ -219,9 +217,7 @@ paths: operationId: post-agama-dev-studio-prj parameters: - name: name - in: path - description: Agama project name - required: true + in: query schema: type: string requestBody: @@ -262,9 +258,7 @@ paths: operationId: delete-agama-dev-studio-prj parameters: - name: name - in: path - description: Agama project name - required: true + in: query schema: type: string responses: @@ -283,7 +277,7 @@ paths: security: - oauth2: - https://jans.io/oauth/config/agama.delete - /api/v1/agama-deployment/configs/{name}: + /api/v1/agama-deployment/configs: get: tags: - Agama - Developer Studio @@ -292,9 +286,7 @@ paths: operationId: get-agama-dev-prj-configs parameters: - name: name - in: path - description: Agama project name - required: true + in: query schema: type: string responses: @@ -323,9 +315,7 @@ paths: operationId: put-agama-dev-studio-prj parameters: - name: name - in: path - description: Agama project name - required: true + in: query schema: type: string requestBody: @@ -7618,17 +7608,17 @@ components: type: string whitePagesCanView: type: boolean - adminCanAccess: - type: boolean adminCanView: type: boolean - adminCanEdit: - type: boolean userCanAccess: type: boolean + userCanView: + type: boolean userCanEdit: type: boolean - userCanView: + adminCanAccess: + type: boolean + adminCanEdit: type: boolean baseDn: type: string @@ -8353,6 +8343,8 @@ components: type: object additionalProperties: type: string + fapi: + type: boolean allResponseTypesSupported: uniqueItems: true type: array @@ -8362,8 +8354,6 @@ components: - code - token - id_token - fapi: - type: boolean AuthenticationFilter: required: - baseDn diff --git a/jans-config-api/server/src/main/java/io/jans/configapi/rest/resource/auth/AgamaDeploymentsResource.java b/jans-config-api/server/src/main/java/io/jans/configapi/rest/resource/auth/AgamaDeploymentsResource.java index b9e8874e142..229e79135bf 100644 --- a/jans-config-api/server/src/main/java/io/jans/configapi/rest/resource/auth/AgamaDeploymentsResource.java +++ b/jans-config-api/server/src/main/java/io/jans/configapi/rest/resource/auth/AgamaDeploymentsResource.java @@ -8,7 +8,6 @@ import io.jans.as.model.util.Pair; import io.jans.orm.model.PagedResult; 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.ExampleObject; import io.swagger.v3.oas.annotations.media.Schema; @@ -23,7 +22,6 @@ import jakarta.annotation.PostConstruct; import jakarta.inject.Inject; -import jakarta.validation.constraints.NotNull; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -77,8 +75,7 @@ public Response getDeployments(@QueryParam("start") int start, @QueryParam("coun @GET @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_READ_ACCESS }, groupScopes = {ApiAccessConstants.AGAMA_WRITE_ACCESS}, superScopes = { ApiAccessConstants.SUPER_ADMIN_READ_ACCESS }) @Produces(MediaType.APPLICATION_JSON) - @Path(ApiConstants.NAME_PARAM_PATH) - public Response getDeployment(@Parameter(description = "Agama project name") @PathParam(ApiConstants.NAME) @NotNull String projectName) { + public Response getDeployment(@QueryParam("name") String projectName) { if (projectName == null) { return Response.status(Response.Status.BAD_REQUEST) @@ -111,8 +108,7 @@ public Response getDeployment(@Parameter(description = "Agama project name") @Pa @Consumes("application/zip") @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_WRITE_ACCESS }, groupScopes = {}, superScopes = { ApiAccessConstants.SUPER_ADMIN_WRITE_ACCESS }) - @Path(ApiConstants.NAME_PARAM_PATH) - public Response deploy(@Parameter(description = "Agama project name") @PathParam(ApiConstants.NAME) @NotNull String projectName, byte[] gamaBinary) { + public Response deploy(@QueryParam("name") String projectName, byte[] gamaBinary) { if (projectName == null || gamaBinary == null) return Response.status(Response.Status.BAD_REQUEST) @@ -141,8 +137,7 @@ public Response deploy(@Parameter(description = "Agama project name") @PathParam @DELETE @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_DELETE_ACCESS }, groupScopes = {}, superScopes = { ApiAccessConstants.SUPER_ADMIN_DELETE_ACCESS }) - @Path(ApiConstants.NAME_PARAM_PATH) - public Response undeploy(@Parameter(description = "Agama project name") @PathParam(ApiConstants.NAME) @NotNull String projectName) { + public Response undeploy(@QueryParam("name") String projectName) { if (projectName == null) return Response.status(Response.Status.BAD_REQUEST) @@ -169,9 +164,10 @@ public Response undeploy(@Parameter(description = "Agama project name") @PathPar @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "500", description = "InternalServerError") }) @GET - @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_READ_ACCESS }, groupScopes = {ApiAccessConstants.AGAMA_WRITE_ACCESS }, superScopes = { ApiAccessConstants.SUPER_ADMIN_READ_ACCESS }) - @Path(ApiConstants.CONFIGS + ApiConstants.NAME_PARAM_PATH) - public Response getConfigs(@Parameter(description = "Agama project name") @PathParam(ApiConstants.NAME) @NotNull String projectName) throws JsonProcessingException { + @Path("configs") + @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_READ_ACCESS }, groupScopes = { + ApiAccessConstants.AGAMA_WRITE_ACCESS }, superScopes = { ApiAccessConstants.SUPER_ADMIN_READ_ACCESS }) + public Response getConfigs(@QueryParam("name") String projectName) throws JsonProcessingException { Pair> pair = projectFlows(projectName); Response resp = pair.getFirst(); @@ -204,11 +200,11 @@ public Response getConfigs(@Parameter(description = "Agama project name") @PathP @ApiResponse(responseCode = "409", description = "Conflict"), @ApiResponse(responseCode = "500", description = "InternalServerError") }) @PUT + @Path("configs") @Consumes(MediaType.APPLICATION_JSON) @ProtectedApi(scopes = { ApiAccessConstants.AGAMA_WRITE_ACCESS }, groupScopes = {}, superScopes = { ApiAccessConstants.SUPER_ADMIN_WRITE_ACCESS }) - @Path(ApiConstants.CONFIGS + ApiConstants.NAME_PARAM_PATH) - public Response setConfigs(@Parameter(description = "Agama project name") @PathParam(ApiConstants.NAME) @NotNull String projectName, + public Response setConfigs(@QueryParam("name") String projectName, Map> flowsConfigs) { if (flowsConfigs == null) {