Skip to content

Commit b6a9c1f

Browse files
Userdetails web services refactoring (#143)
* Refactor web services #141 Co-authored-by: Simon Bear <simon.bear@csiro.au>
1 parent 4859195 commit b6a9c1f

File tree

22 files changed

+335
-274
lines changed

22 files changed

+335
-274
lines changed

userdetails-cognito/src/main/groovy/au/org/ala/userdetails/CognitoUserService.groovy

+189-66
Large diffs are not rendered by default.

userdetails-gorm/grails-app/controllers/au/org/ala/userdetails/gorm/AuthorisedSystemController.groovy

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package au.org.ala.userdetails.gorm
1717

1818
import au.org.ala.auth.PreAuthorise
19-
import au.org.ala.users.AuthorisedSystem
2019
import grails.converters.JSON
2120
import org.springframework.dao.DataIntegrityViolationException
2221

userdetails-gorm/grails-app/domain/au/org/ala/userdetails/gorm/User.groovy

+1-6
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,7 @@ class User extends UserRecord<Long> implements Serializable {
116116
def propsAsMap(){
117117
def map = [:]
118118
this.getUserProperties().each {
119-
if(it.name == "enableMFA"){
120-
map.put(it.name, it.value.toBoolean())
121-
}
122-
else {
123-
map.put(it.name.startsWith('custom:') ? it.name.substring(7) : it.name, it.value)
124-
}
119+
map.put(it.name.startsWith('custom:') ? it.name.substring(7) : it.name, it.value)
125120
}
126121
map
127122
}

userdetails-gorm/grails-app/init/au/org/ala/userdetails/gorm/Application.groovy

+5-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import au.org.ala.userdetails.IPasswordOperations
2121
import au.org.ala.userdetails.IUserService
2222
import au.org.ala.userdetails.LocationService
2323
import au.org.ala.userdetails.PasswordService
24+
import au.org.ala.userdetails.ProfileService
2425
import au.org.ala.web.AuthService
2526
import au.org.ala.ws.service.WebService
2627
import grails.boot.GrailsApp
@@ -52,7 +53,9 @@ class Application extends GrailsAutoConfiguration {
5253
AuthService authService,
5354
LocationService locationService,
5455
MessageSource messageSource,
55-
WebService webService) {
56+
WebService webService,
57+
ProfileService profileService
58+
) {
5659

5760
// grailsApplication.addArtefact(DomainClassArtefactHandler.TYPE, UserRecord)
5861
// grailsApplication.addArtefact(DomainClassArtefactHandler.TYPE, UserPropertyRecord)
@@ -67,6 +70,7 @@ class Application extends GrailsAutoConfiguration {
6770

6871
userService.grailsApplication = grailsApplication
6972
userService.messageSource = messageSource
73+
userService.profileService = profileService
7074

7175
userService.affiliationsEnabled = grailsApplication.config.getProperty('attributes.affiliations.enabled', Boolean, false)
7276

userdetails-gorm/src/main/groovy/au/org/ala/userdetails/gorm/GormUserService.groovy

+39-27
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import au.org.ala.userdetails.IUserService
2222
import au.org.ala.userdetails.LocationService
2323
import au.org.ala.userdetails.PagedResult
2424
import au.org.ala.userdetails.PasswordService
25+
import au.org.ala.userdetails.ProfileService
2526
import au.org.ala.userdetails.ResultStreamer
2627
import au.org.ala.users.RoleRecord
2728
import au.org.ala.users.UserPropertyRecord
@@ -54,6 +55,7 @@ class GormUserService implements IUserService {
5455
LocationService locationService
5556
MessageSource messageSource
5657
WebService webService
58+
ProfileService profileService
5759

5860
@Value('${attributes.affiliations.enabled:false}')
5961
boolean affiliationsEnabled = false
@@ -113,11 +115,6 @@ class GormUserService implements IUserService {
113115
return user?.locked ?: false
114116
}
115117

116-
@Transactional(readOnly = true)
117-
boolean isEmailRegistered(String email) {
118-
return User.findByEmailOrUserName(email?.toLowerCase(), email?.toLowerCase()) != null
119-
}
120-
121118
@Transactional(readOnly = true)
122119
boolean isEmailInUse(String newEmail) {
123120
return User.findByEmailOrUserName(newEmail?.toLowerCase(), newEmail?.toLowerCase())
@@ -155,7 +152,7 @@ class GormUserService implements IUserService {
155152
return User.findAllByEmailLikeOrLastNameLikeOrFirstNameLike(q, q, q, [offset: paginationToken as int, max: maxResults ])
156153
}
157154

158-
return User.list([offset: paginationToken as int, max: maxResults ])
155+
return User.list([offset: (paginationToken ?: 0) as int, max: maxResults ])
159156
}
160157

161158
@Override
@@ -253,7 +250,7 @@ class GormUserService implements IUserService {
253250

254251
// Now send a temporary password to the user...
255252
try {
256-
resetAndSendTemporaryPassword(userInstance, emailSubject, emailTitle, emailBody, password)
253+
passwordService.resetAndSendTemporaryPassword(userInstance, emailSubject, emailTitle, emailBody, password)
257254
} catch (PasswordResetFailedException ex) {
258255
// Catching the checked exception should prevent the transaction from failing
259256
log.error("Failed to send temporary password via email!", ex)
@@ -274,7 +271,7 @@ class GormUserService implements IUserService {
274271

275272
properties.keySet().each { String propName ->
276273
def propValue = properties[propName] ?: ''
277-
setUserProperty(user, propName, propValue)
274+
setUserProperty(user, propName, propValue as String)
278275
}
279276
}
280277

@@ -530,39 +527,44 @@ class GormUserService implements IUserService {
530527
@Override
531528
void addRoles(Collection<RoleRecord> roleRecords) {
532529
Role.saveAll(roleRecords.collect { new Role(role: it.role, description: it.description) })
533-
534530
}
535531

536-
@Override
537-
List<UserPropertyRecord> findAllAttributesByName(String s) {
538-
UserProperty.findAllByName("flickrId")
539-
}
532+
// *********** Property related services *************
540533

541534
@Override
542-
void addOrUpdateProperty(UserRecord userRecord, String name, String value) {
535+
UserPropertyRecord addOrUpdateProperty(UserRecord userRecord, String name, String value) {
543536
assert userRecord instanceof User
544-
UserProperty.addOrUpdateProperty(userRecord, name, value)
537+
return UserProperty.addOrUpdateProperty(userRecord, name, value)
545538
}
546539

547540
@Override
548-
void removeUserAttributes(UserRecord user, ArrayList<String> attrs) {
549-
def props = UserProperty.findAllByUserAndNameInList(user, attrs)
541+
void removeUserProperty(UserRecord user, ArrayList<String> attrs) {
542+
def props = UserProperty.findAllByUserAndNameInList(user as User, attrs)
550543
if (props) UserProperty.deleteAll(props)
551544
}
552545

553546
@Override
554-
void getUserAttribute(UserRecord userRecord, String attribute) {
555-
UserProperty.findAllByUserAndName(userRecord, attribute)
556-
}
547+
List<UserPropertyRecord> searchProperty(UserRecord userRecord, String attribute) {
548+
List<UserPropertyRecord> propList = []
557549

558-
@Override
559-
List<UserProperty> getAllAvailableProperties() {
560-
UserProperty.withCriteria {
561-
projections {
562-
distinct("name")
563-
}
564-
order("name")
550+
if(userRecord && attribute){
551+
List properties = UserProperty.findAllByUserAndName(userRecord as User, attribute)
552+
propList = properties.collect {new UserPropertyRecord(user: userRecord, name: it.name, value: it.value) }
565553
}
554+
else if(attribute){
555+
propList = UserProperty.findAllByName(attribute)
556+
}
557+
else{
558+
List properties = UserProperty.withCriteria {
559+
projections {
560+
distinct("name")
561+
}
562+
order("name")
563+
} as List
564+
565+
propList = properties.collect { new UserPropertyRecord(user: it.user, name: it.name, value: it.value) }
566+
}
567+
return propList
566568
}
567569

568570
@Override
@@ -652,6 +654,8 @@ class GormUserService implements IUserService {
652654
emailService.sendAccountActivation(user, user.tempAuthKey)
653655
}
654656

657+
// *********** MFA services *************
658+
655659
@Override
656660
String getSecretForMfa() {}
657661

@@ -660,4 +664,12 @@ class GormUserService implements IUserService {
660664

661665
@Override
662666
void enableMfa(String userId, boolean enable){}
667+
668+
def getUserDetailsFromIdList(List idList){
669+
def c = User.createCriteria()
670+
def results = c.list() {
671+
'in'("id", idList.collect { userId -> userId as long } )
672+
}
673+
return results
674+
}
663675
}

userdetails-plugin/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ dependencies {
112112

113113
api "org.grails.plugins:ala-bootstrap3:4.1.0"
114114
api "org.grails.plugins:ala-ws-plugin:3.1.1"
115-
api "org.grails.plugins:ala-ws-security-plugin:4.3.3-SNAPSHOT"
115+
api "org.grails.plugins:ala-ws-security-plugin:4.3.5-SNAPSHOT"
116116
api "org.grails.plugins:ala-auth:5.2.0-SNAPSHOT"
117117
api "org.grails.plugins:ala-admin-plugin:2.3.0"
118118

userdetails-plugin/grails-app/controllers/au/org/ala/userdetails/ExternalSiteController.groovy

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class ExternalSiteController {
6060
@Produces("application/json")
6161
def flickr() {
6262

63-
def flickrIds = userService.findAllAttributesByName("flickrId") // UserPropertyRecord.findAllByName("flickrId")
63+
def flickrIds = userService.searchProperty(null, "flickrId")
6464
render(contentType: "application/json") {
6565
flickrUsers(flickrIds) { UserPropertyRecord flickrId ->
6666
id flickrId.user.id.toString()

userdetails-plugin/grails-app/controllers/au/org/ala/userdetails/ProfileController.groovy

+1-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class ProfileController {
177177
}
178178

179179
if (user) {
180-
userService.removeUserAttributes(user, attrs)
180+
userService.removeUserProperty(user, attrs)
181181
} else {
182182
flash.message = 'Failed to retrieve user details!'
183183
}

userdetails-plugin/grails-app/controllers/au/org/ala/userdetails/PropertyController.groovy

+5-8
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ class PropertyController extends BaseController {
4343
IUserService userService
4444

4545
def profileService
46-
def authorisedSystemService
4746

4847
static allowedMethods = [getProperty: "GET", saveProperty: "POST"]
4948

@@ -64,7 +63,6 @@ class PropertyController extends BaseController {
6463
name = "alaId",
6564
in = QUERY,
6665
description = "The user's ALA ID",
67-
schema = @Schema(implementation = Long),
6866
required = true
6967
),
7068
@Parameter(
@@ -100,14 +98,14 @@ class PropertyController extends BaseController {
10098
@PreAuthorise(requiredScope = 'users/read')
10199
def getProperty() {
102100
String name = params.name
103-
Long alaId = params.long('alaId')
101+
String alaId = params.alaId
104102
if (!name || !alaId) {
105103
badRequest "name and alaId must be provided";
106104
} else {
107105
UserRecord user = userService.getUserById(alaId);
108-
List props
106+
List<UserPropertyRecord> props
109107
if (user) {
110-
props = profileService.getUserProperty(user, name);
108+
props = profileService.getUserProperty(user, name)
111109
render text: props as JSON, contentType: 'application/json'
112110
} else {
113111
notFound "Could not find user for id: ${alaId}";
@@ -130,7 +128,6 @@ class PropertyController extends BaseController {
130128
name = "alaId",
131129
in = QUERY,
132130
description = "The user's ALA ID",
133-
schema = @Schema(implementation = Long),
134131
required = true
135132
),
136133
@Parameter(
@@ -178,15 +175,15 @@ class PropertyController extends BaseController {
178175
def saveProperty(){
179176
String name = params.name;
180177
String value = params.value;
181-
Long alaId = params.long('alaId');
178+
String alaId = params.alaId
182179
if (!name || !alaId) {
183180
badRequest "name and alaId must be provided";
184181
} else {
185182
UserRecord user = userService.getUserById(alaId);
186183
UserPropertyRecord property
187184
if (user) {
188185
property = profileService.saveUserProperty(user, name, value);
189-
if (property.hasErrors()) {
186+
if (!property) {
190187
saveFailed()
191188
} else {
192189
render text: property as JSON, contentType: 'application/json'

userdetails-plugin/grails-app/controllers/au/org/ala/userdetails/RegistrationController.groovy

+5-4
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ class RegistrationController {
293293
}
294294

295295
//create user account...
296-
if (!paramsEmail || userService.isEmailRegistered(paramsEmail)) {
296+
if (!paramsEmail || userService.isEmailInUse(paramsEmail)) {
297297
def inactiveUser = !userService.isActive(paramsEmail)
298298
def lockedUser = userService.isLocked(paramsEmail)
299299
render(view: 'createAccount', model: [edit: false, user: params, props: params, alreadyRegistered: true, inactiveUser: inactiveUser, lockedUser: lockedUser, passwordPolicy: passwordService.buildPasswordPolicy()])
@@ -371,12 +371,13 @@ class RegistrationController {
371371
}
372372

373373
def getSecretForMfa() {
374+
def mfaResponse
374375
try {
375-
def response = [success: true, code: userService.getSecretForMfa()]
376+
mfaResponse = [success: true, code: userService.getSecretForMfa()]
376377
} catch (e) {
377-
def response = [success: false, error: e.message]
378+
mfaResponse = [success: false, error: e.message]
378379
}
379-
render(response as JSON)
380+
render(mfaResponse as JSON)
380381
}
381382

382383
def verifyAndActivateMfa() {

userdetails-plugin/grails-app/controllers/au/org/ala/userdetails/RoleBasedInterceptor.groovy

+12-5
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,19 @@ class RoleBasedInterceptor {
4242
PreAuthorise pa = method.getAnnotation(PreAuthorise) ?: controllerClass.getAnnotation(PreAuthorise)
4343
response.withFormat {
4444
json {
45-
if (!authorisedSystemService.isAuthorisedRequest(request, response, pa.requiredRole(), pa.requiredScope())) {
46-
log.warn("Denying access to $actionName from remote addr: ${request.remoteAddr}, remote host: ${request.remoteHost}")
47-
response.status = HttpStatus.SC_UNAUTHORIZED
48-
render(['error': "Unauthorized"] as JSON)
45+
try{
46+
if (!authorisedSystemService.isAuthorisedRequest(request, response, pa.requiredRole(), pa.requiredScope())) {
47+
log.warn("Denying access to $actionName from remote addr: ${request.remoteAddr}, remote host: ${request.remoteHost}")
48+
response.status = HttpStatus.SC_UNAUTHORIZED
49+
render(['error': "Unauthorized"] as JSON)
4950

50-
result = false
51+
result = false
52+
}
53+
}
54+
catch (Exception e){
55+
log.error(e.getMessage(), e)
56+
response.sendError(HttpStatus.SC_UNAUTHORIZED)
57+
return false
5158
}
5259
}
5360
'*' {

userdetails-plugin/grails-app/controllers/au/org/ala/userdetails/RoleController.groovy

+3-3
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,21 @@ class RoleController {
4242
}
4343

4444
def create() {
45-
[roleInstance: new RoleRecord(params) ]
45+
[roleInstance: new RoleRecord() ]
4646
}
4747

4848
def save() {
4949

5050
def pattern = ~/[A-Z_]{1,}/
5151

5252
if(pattern.matcher(params.role).matches()){
53-
def roleInstance = new RoleRecord(params)
53+
def roleInstance = new RoleRecord(role: params.role, description: params.description)
5454
def saved = userService.addRole(roleInstance) // roleInstance.save(flush: true)
5555
if (!saved) {
5656
render(view: "create", model: [roleInstance: roleInstance])
5757
return
5858
}
59-
flash.message = message(code: 'default.created.message', args: [message(code: 'role.label', default: 'Role'), roleInstance.id])
59+
flash.message = message(code: 'default.created.message', args: [message(code: 'role.label', default: 'Role'), roleInstance.role])
6060
redirect(action: "list")
6161
} else {
6262
flash.message = 'RoleRecord must consist of uppercase characters and underscores only'

0 commit comments

Comments
 (0)