From 644e19ca20e0f71a10dc6b375d85ad5b6cefce93 Mon Sep 17 00:00:00 2001 From: yasima-csiro Date: Thu, 15 Dec 2022 10:04:19 +1100 Subject: [PATCH] Rebase with develop --- .../ala/userdetails/CognitoUserService.groovy | 209 ++++++++++++++++-- .../userdetails/gorm/GormUserService.groovy | 12 +- .../org/ala/userdetails/IUserService.groovy | 10 + 3 files changed, 199 insertions(+), 32 deletions(-) diff --git a/userdetails-cognito/src/main/groovy/au/org/ala/userdetails/CognitoUserService.groovy b/userdetails-cognito/src/main/groovy/au/org/ala/userdetails/CognitoUserService.groovy index d028173c..0d06d07f 100644 --- a/userdetails-cognito/src/main/groovy/au/org/ala/userdetails/CognitoUserService.groovy +++ b/userdetails-cognito/src/main/groovy/au/org/ala/userdetails/CognitoUserService.groovy @@ -6,8 +6,10 @@ import au.org.ala.users.RoleRecord import au.org.ala.users.UserPropertyRecord import au.org.ala.users.UserRecord import au.org.ala.users.UserRoleRecord +import au.org.ala.ws.security.JwtProperties import au.org.ala.ws.tokens.TokenService import com.amazonaws.services.cognitoidp.AWSCognitoIdentityProvider +import com.amazonaws.services.cognitoidp.model.AddCustomAttributesRequest import com.amazonaws.services.cognitoidp.model.AdminCreateUserRequest import com.amazonaws.services.cognitoidp.model.AdminDisableUserRequest import com.amazonaws.services.cognitoidp.model.AdminEnableUserRequest @@ -20,12 +22,15 @@ import com.amazonaws.services.cognitoidp.model.AdminUpdateUserAttributesRequest import com.amazonaws.services.cognitoidp.model.AssociateSoftwareTokenRequest import com.amazonaws.services.cognitoidp.model.AttributeType import com.amazonaws.services.cognitoidp.model.ConfirmForgotPasswordRequest +import com.amazonaws.services.cognitoidp.model.DescribeUserPoolRequest import com.amazonaws.services.cognitoidp.model.GetUserRequest import com.amazonaws.services.cognitoidp.model.GetUserResult import com.amazonaws.services.cognitoidp.model.ListGroupsRequest import com.amazonaws.services.cognitoidp.model.ListGroupsResult +import com.amazonaws.services.cognitoidp.model.ListUsersInGroupRequest import com.amazonaws.services.cognitoidp.model.ListUsersRequest import com.amazonaws.services.cognitoidp.model.ListUsersResult +import com.amazonaws.services.cognitoidp.model.SchemaAttributeType import com.amazonaws.services.cognitoidp.model.UpdateUserAttributesRequest import com.amazonaws.services.cognitoidp.model.UpdateUserAttributesResult import com.amazonaws.services.cognitoidp.model.SoftwareTokenMfaSettingsType @@ -33,11 +38,13 @@ import com.amazonaws.services.cognitoidp.model.UserNotFoundException import com.amazonaws.services.cognitoidp.model.UserType import com.nimbusds.oauth2.sdk.token.AccessToken import com.amazonaws.services.cognitoidp.model.VerifySoftwareTokenRequest +import grails.converters.JSON import grails.web.servlet.mvc.GrailsParameterMap import groovy.util.logging.Slf4j import org.apache.commons.codec.digest.HmacAlgorithms import org.apache.commons.codec.digest.HmacUtils import org.apache.commons.lang3.NotImplementedException +import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value import java.util.stream.Stream @@ -55,6 +62,8 @@ class CognitoUserService implements IUserService { AWSCognitoIdentityProvider cognitoIdp String poolId + @Autowired + JwtProperties jwtProperties @Value('${attributes.affiliations.enabled:false}') boolean affiliationsEnabled = false @@ -210,7 +219,7 @@ class CognitoUserService implements IUserService { activated: userType.userStatus == "CONFIRMED", locked: !userType.enabled, firstName: attributes['given_name'], lastName: attributes['family_name'], email: attributes['email'], userName: userType.username, - userRoles: attributes['custom:roles']?.split(','), userProperties: userProperties) + userRoles: attributes['custom:role']?.split(','), userProperties: userProperties) } .toList() } @@ -325,6 +334,9 @@ class CognitoUserService implements IUserService { @Override UserRecord getUserById(String userId) { + def userResponse + def userAttributes + if (userId == null || userId == "") { // Problem. This might mean an expired cookie, or it might mean that this service is not in the authorised system list log.debug("Attempt to get current user returned null. This might indicating that this machine is not the authorised system list") @@ -332,27 +344,39 @@ class CognitoUserService implements IUserService { } try { - AdminGetUserResult userResponse = cognitoIdp.adminGetUser(new AdminGetUserRequest().withUsername(userId).withUserPoolId(poolId)) + if (userId.isLong()) { + ListUsersRequest request = new ListUsersRequest() + .withUserPoolId(poolId) + .withFilter("name=\"${userId}\"") + ListUsersResult response = cognitoIdp.listUsers(request) + userResponse = response.users.first() + userAttributes = userResponse.attributes + } - Map attributes = userResponse.userAttributes.collectEntries { [ (it.name): it.value ] } - Collection userProperties = userResponse.userAttributes - .findAll {!mainAttrs.contains(it.name) } - .collect { - new UserPropertyRecord(name: it.name, value: it.value) - } - userProperties.add(new UserPropertyRecord(name: "enableMFA", value: userResponse.getUserMFASettingList()?.size() > 0)) + else { + userResponse = cognitoIdp.adminGetUser(new AdminGetUserRequest().withUsername(userId).withUserPoolId(poolId)) + userAttributes = userResponse.userAttributes + } + Map attributes = userAttributes.collectEntries { [ (it.name): it.value ] } + Collection userProperties = userAttributes + .findAll {!mainAttrs.contains(it.name) } + .collect { + new UserPropertyRecord(name: it.name, value: it.value) + } + userProperties.add(new UserPropertyRecord(name: "enableMFA", value: userResponse.getUserMFASettingList()?.size() > 0)) - UserRecord user = new UserRecord( - userId: userResponse.username, - dateCreated: userResponse.userCreateDate, lastUpdated: userResponse.userLastModifiedDate, - activated: userResponse.userStatus == "CONFIRMED", locked: !userResponse.enabled, - firstName: attributes['given_name'], lastName: attributes['family_name'], - email: attributes['email'], userName: userResponse.username, - userRoles: attributes['custom:roles']?.split(','), userProperties: userProperties - ) - return user + UserRecord user = new UserRecord( + userId: userResponse.username, + dateCreated: userResponse.userCreateDate, lastUpdated: userResponse.userLastModifiedDate, + activated: userResponse.userStatus == "CONFIRMED", locked: !userResponse.enabled, + firstName: attributes['given_name'], lastName: attributes['family_name'], + email: attributes['email'], userName: userResponse.username, + userRoles: attributes['custom:role']?.split(','), userProperties: userProperties + ) + + return user } catch (UserNotFoundException e) { return null @@ -415,7 +439,12 @@ class CognitoUserService implements IUserService { @Override Map getUsersCounts(Locale locale) { - return null + Map jsonMap = [:] + DescribeUserPoolRequest request = new DescribeUserPoolRequest().withUserPoolId(poolId) + def response = cognitoIdp.describeUserPool(request) + jsonMap.totalUsers = response.userPool.estimatedNumberOfUsers + log.debug "jsonMap = ${jsonMap as JSON}" + jsonMap } @Override @@ -505,11 +534,6 @@ class CognitoUserService implements IUserService { throw new NotImplementedException() } - @Override - UserRecord findByUserNameOrEmail(String username) { - throw new NotImplementedException() - } - @Override List listNamesAndEmails() { throw new NotImplementedException() @@ -628,4 +652,141 @@ class CognitoUserService implements IUserService { } } + @Override + UserRecord findByUserNameOrEmail(String userName) { + return listUsers(userName, null, 1)?[0] + } + + @Override + def findUsersByRole(String roleName, List numberIds, List userIds, String pageOrToken) { + ListUsersInGroupRequest request = new ListUsersInGroupRequest().withUserPoolId(poolId).withLimit(10) + if(pageOrToken) { + request.nextToken = pageOrToken + } + request.groupName = roleName.split(jwtProperties.getRolePrefix())[1].toLowerCase() + //TODO: filter using userid and email + //following CustomQueryParameter is not working + //request.putCustomQueryParameter("name", "43954") + def response = cognitoIdp.listUsersInGroup(request) + def users = response.users.stream() + + def results = users.map { userType -> + + Map attributes = userType.attributes.collectEntries { [ (it.name): it.value ] } + Collection userProperties = userType.attributes + .findAll {!mainAttrs.contains(it.name) } + .collect { + new UserPropertyRecord(name: it.name, value: it.value) + } + + new UserRecord( + userId: userType.username, + dateCreated: userType.userCreateDate, lastUpdated: userType.userLastModifiedDate, + activated: userType.userStatus == "CONFIRMED", locked: !userType.enabled, + firstName: attributes['given_name'], lastName: attributes['family_name'], + email: attributes['email'], userName: userType.username, + userRoles: attributes['custom:role']?.split(','), userProperties: userProperties) + }.toList() + + return [results: results, nextToken: response.nextToken] + } + + def getUserDetailsFromIdList(List idList){ + + List users = [] + + ListUsersRequest request = new ListUsersRequest() + .withUserPoolId(poolId) + .withLimit(1) + + idList.forEach{ + if(it instanceof Number) { + request.withFilter("name = \"${it.toString()}\"") + } + else{ + request.withFilter("username = \"${it.toString()}\"") + } + def response = cognitoIdp.listUsers(request) + users.addAll(response.users) + } + + return users.stream().map { userType -> + + Map attributes = userType.attributes.collectEntries { [ (it.name): it.value ] } + Collection userProperties = userType.attributes + .findAll {!mainAttrs.contains(it.name) } + .collect { + new UserPropertyRecord(name: it.name, value: it.value) + } + + new UserRecord( + userId: attributes['name'] ?: userType.username, + dateCreated: userType.userCreateDate, lastUpdated: userType.userLastModifiedDate, + activated: userType.userStatus == "CONFIRMED", locked: !userType.enabled, + firstName: attributes['given_name'], lastName: attributes['family_name'], + email: attributes['email'], userName: userType.username, + userRoles: attributes['custom:role']?.split(','), userProperties: userProperties) + }.toList() + } + + def searchByUsernameOrEmail(String q, int max){ + return [results: listUsers(q, null, max)] + } + + def saveCustomUserProperty(UserRecord user, String name, String value) { + + DescribeUserPoolRequest request = new DescribeUserPoolRequest().withUserPoolId(poolId) + def response = cognitoIdp.describeUserPool(request) + if (response.userPool.schemaAttributes.find{it.name =='custom:' + name} == null) { + + AddCustomAttributesRequest addAttrRequest = new AddCustomAttributesRequest().withUserPoolId(poolId) + + List attList = new ArrayList<>() + attList.add(new SchemaAttributeType().withAttributeDataType("String") + .withMutable(true).withName(name)) + + addAttrRequest.customAttributes = attList + def addAttResponse = cognitoIdp.addCustomAttributes(addAttrRequest) + if (addAttResponse.sdkHttpMetadata.httpStatusCode == 200) { + + def updateUserResponse = addCustomUserProperty(user, name, value) + + if (updateUserResponse.sdkHttpMetadata.httpStatusCode == 200) { + return [property: name, value: value] + } else { + return [] + } + } else { + return [] + } + } + else{ + def updateUserResponse = addCustomUserProperty(user, name, value) + + if (updateUserResponse.sdkHttpMetadata.httpStatusCode == 200) { + return [property: name, value: value] + } else { + return [] + } + } + } + + def addCustomUserProperty(UserRecord user, String name, String value){ + Collection userAttributes = new ArrayList<>() + + userAttributes.add(new AttributeType().withName('custom:' + name).withValue(value)) + + AdminUpdateUserAttributesRequest updateUserRequest = + new AdminUpdateUserAttributesRequest() + .withUserPoolId(poolId) + .withUsername(user.userName) + .withUserAttributes(userAttributes) + + return cognitoIdp.adminUpdateUserAttributes(updateUserRequest) + } + + def getCustomUserProperty(UserRecord user, String name){ + return user.userProperties.findAll{it.name == 'custom:'+ name}.collect{ [property:"$name",value: it.value ] } + } + } diff --git a/userdetails-gorm/src/main/groovy/au/org/ala/userdetails/gorm/GormUserService.groovy b/userdetails-gorm/src/main/groovy/au/org/ala/userdetails/gorm/GormUserService.groovy index 70b09b2c..33541a49 100644 --- a/userdetails-gorm/src/main/groovy/au/org/ala/userdetails/gorm/GormUserService.groovy +++ b/userdetails-gorm/src/main/groovy/au/org/ala/userdetails/gorm/GormUserService.groovy @@ -23,6 +23,7 @@ import au.org.ala.userdetails.EmailService import au.org.ala.userdetails.IUserService import au.org.ala.userdetails.LocationService import au.org.ala.userdetails.PasswordService +import au.org.ala.userdetails.ProfileService import au.org.ala.userdetails.ResultStreamer import au.org.ala.users.RoleRecord import au.org.ala.users.UserPropertyRecord @@ -613,7 +614,7 @@ class GormUserService implements IUserService { } @Override - User findByUserNameOrEmail(String username) { + UserRecord findByUserNameOrEmail(String username) { return User.findByUserNameOrEmail(username, username) } @@ -735,11 +736,6 @@ class GormUserService implements IUserService { return false } - @Override - User findByUserNameOrEmail(String userName) { - return User.findByUserNameOrEmail(userName, userName) - } - @Override def findUsersByRole(String roleName, List numberIds, List userIds, String pageOrToken) { ScrollableResults results = null @@ -795,12 +791,12 @@ class GormUserService implements IUserService { return [results: results] } - def saveCustomUserProperty(User user, String name, String value){ + def saveCustomUserProperty(UserRecord user, String name, String value){ UserProperty property = profileService.saveUserProperty(user, name, value) return property.hasErrors() ? null: property } - def getCustomUserProperty(User user, String name){ + def getCustomUserProperty(UserRecord user, String name){ return profileService.getUserProperty(user, name); } } diff --git a/userdetails-plugin/src/main/groovy/au/org/ala/userdetails/IUserService.groovy b/userdetails-plugin/src/main/groovy/au/org/ala/userdetails/IUserService.groovy index de5802f8..39799105 100644 --- a/userdetails-plugin/src/main/groovy/au/org/ala/userdetails/IUserService.groovy +++ b/userdetails-plugin/src/main/groovy/au/org/ala/userdetails/IUserService.groovy @@ -133,4 +133,14 @@ interface IUserService { boolean verifyUserCode(String userCode) void enableMfa(String userId, boolean enable) + + def findUsersByRole(String roleName, List numberIds, List userIds, String pageOrToken) + + def getUserDetailsFromIdList(List idList) + + def searchByUsernameOrEmail(String q, int max) + + def saveCustomUserProperty(UserRecord user, String name, String value) + + def getCustomUserProperty(UserRecord user, String name) } \ No newline at end of file