Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,65 @@ class UserRestApiSpec extends Specification implements OkHttpRestTrait {
login()
}

// void "test OkHttpRestTrait login"() {
// when:
// String token = login('admin', '123')
// then:
// token
// }
void "user can be created without password"() {
when:
def resp = post(endpoint, [username:"test", email:"[email protected]"])

then:
resp.code() == HttpStatus.CREATED.value()

when:
def body = bodyToMap(resp)

then:
body
body.id
body.username == "test"

when:
AppUser user = AppUser.repo.getWithTrx(body.id as Long)

then:
!user.passwordHash
!user.passwordChangedDate

cleanup:
if(user) {
AppUser.withNewTransaction {
AppUser.repo.removeById(user.id)
}
}
}

void "create with password"() {
when:
def resp = post(endpoint, [username:"test", password:'test', email:"[email protected]"])

then:
resp.code() == HttpStatus.CREATED.value()

when:
def body = bodyToMap(resp)

then:
body
body.id
body.username == "test"

when:
AppUser user = AppUser.repo.getWithTrx(body.id as Long)

then:
user.passwordHash
user.passwordChangedDate

cleanup:
if(user) {
AppUser.withNewTransaction {
AppUser.repo.removeById(user.id)
}
}
}

void "test get to make sure display false dont get returned"() {
when:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package gorm.tools.security

import gorm.tools.problem.ValidationProblem
import org.springframework.security.crypto.password.PasswordEncoder
import yakworks.api.problem.data.DataProblemException
import yakworks.security.PasswordConfig
import yakworks.security.Roles
import yakworks.security.gorm.model.SecPasswordHistoryRepo
import yakworks.security.gorm.model.SecRoleUser
import yakworks.security.gorm.model.AppUser
import yakworks.security.gorm.model.AppUserRepo
Expand All @@ -12,11 +15,16 @@ import grails.testing.mixin.integration.Integration
import grails.gorm.transactions.Rollback
import spock.lang.Specification

import javax.inject.Inject

@Integration
@Rollback
class AppUserRepoSpec extends Specification implements DataIntegrationTest, SecuritySpecHelper {

AppUserRepo appUserRepo
SecPasswordHistoryRepo secPasswordHistoryRepo
PasswordEncoder passwordEncoder
@Inject PasswordConfig passwordConfig

Map getUserParams(Map params = [:]){
Map baseParams = [
Expand Down Expand Up @@ -81,7 +89,7 @@ class AppUserRepoSpec extends Specification implements DataIntegrationTest, Secu
}

/*see SecuritySeedData*/
def "test create with roles obj"() {
void "test create with roles obj"() {
when:
// use objects that may get passed in from json in this format
def params = getUserParams([
Expand All @@ -101,7 +109,7 @@ class AppUserRepoSpec extends Specification implements DataIntegrationTest, Secu
}


def "test updating roles"() {
void "test updating roles"() {

when: "current admin has 2 roles 1,3"
assert SecRoleUser.getByUser(1)*.role.id == [1, 3]
Expand Down Expand Up @@ -135,7 +143,7 @@ class AppUserRepoSpec extends Specification implements DataIntegrationTest, Secu
SecRoleUser.findAllByUser(AppUser.get(1))*.role.id == [1L]
}

def "remove roles when user is removed"() {
void "remove roles when user is removed"() {
setup:
Map params = getUserParams([roles: ["ADMIN", "MANAGER"]])
AppUser user = AppUser.create(params)
Expand All @@ -153,7 +161,7 @@ class AppUserRepoSpec extends Specification implements DataIntegrationTest, Secu
!SecRoleUser.get(3, user.id)
}

def testRemove() {
void testRemove() {
setup:
AppUser user = appUserRepo.create(getUserParams())

Expand All @@ -167,6 +175,44 @@ class AppUserRepoSpec extends Specification implements DataIntegrationTest, Secu
AppUser.get(user.id) == null
}

void "test updatePassword"() {
setup:
passwordConfig.historyEnabled = true
passwordConfig.mustContainUppercaseLetter = true
AppUser user = AppUser.get(1L)

expect:
user

when: "same password, nothing changed"
appUserRepo.updatePassword(user, '123')

then: "no history created"
noExceptionThrown()
!secPasswordHistoryRepo.findAllByUser(user.id)

when: "pwd validation fails"
appUserRepo.updatePassword(user, 'test')

then: "no history created"
ValidationProblem.Exception ex = thrown()
ex.violations.size() == 1
ex.violations[0].code == 'security.validation.password.mustcontain.uppercase'

and: "history not created"
!secPasswordHistoryRepo.findAllByUser(user.id)

when: "all good"
appUserRepo.updatePassword(user, 'Xtest')
flush()

then:
noExceptionThrown()

and: "history created"
secPasswordHistoryRepo.findAllByUser(user.id).size() == 1
}

/** printDiffs prints the pertinent params and final data for the test for debugging purposes. */
void printDiffs(Map params, AppUser user, Map result) {
println " key params - result"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
package yakworks.security.gorm

import java.time.LocalDate
import java.time.LocalDateTime

import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic

import grails.gorm.transactions.Transactional
import yakworks.api.Result
import yakworks.api.problem.Problem
import yakworks.message.Msg
import yakworks.message.MsgKey
import yakworks.security.gorm.model.AppUser
import yakworks.security.gorm.model.SecPasswordHistory
import yakworks.security.services.PasswordValidator
Expand All @@ -27,47 +26,58 @@ class AppUserPasswordValidator extends PasswordValidator {
@Override
Result validate(Serializable userId, String pass) {
def res = super.validate(pass)
if (!res.ok) return res
if (!res.ok) return res //return fast if ok

List problemKeys = [] as List<MsgKey>

if (passwordConfig.historyEnabled && passwordExistInHistory(userId, pass)) {
problemKeys << Msg.key("security.validation.password.existsinhistory", [value: passwordConfig.historyLength])
}

if (problemKeys) {
return Problem.of('security.validation.password.error').addViolations(problemKeys)
} else {
return Result.OK()
if (passwordExistInHistory(userId, pass)) {
var msgKey= Msg.key("security.validation.password.existsinhistory", [value: passwordConfig.historyLength])
return Problem.of('security.validation.password.error').addViolations([msgKey])
}
//return ok if its all good
return res
}

/**
* Check if the password exists in user's password history
* NOT USED RIGHT NOW, KEPT FOR REF
*
* @param id AppUser.id
* @param password what to check
* @return true if it exists, false if not enabled or does not exist
*/
@Override
@CompileDynamic
@Transactional(readOnly = true)
boolean passwordExistInHistory(Serializable id, String password) {
List<SecPasswordHistory> passwordHistoryList = SecPasswordHistory.query(userId: id).list()
passwordHistoryList.any { passwordEncoder.matches(password, it.password) }
if (passwordConfig.historyEnabled) {
List<SecPasswordHistory> passwordHistoryList = SecPasswordHistory.query(userId: id).list()
return passwordHistoryList.any { passwordEncoder.matches(password, it.password) }
}
return false
}

/**
* checks if password is expired. first checks the passwordExpired field and then if expireEnabled
* it adds the expireDays to see if we are under that date
*
* @param user is optional, will look in the security context if not passed in
*/
@Override
boolean isPasswordExpired(Serializable id) {
AppUser user = AppUser.get(id)

//if user without password
if(user.passwordHash == null) return false

//can always force a password change by setting passwordExpired field to true
if (user.passwordExpired) return true

if (passwordConfig.expiryEnabled) {
LocalDate expireDate = user.passwordChangedDate?.plusDays(passwordConfig.passwordExpireDays).toLocalDate()
//check if user's password has expired
if (!expireDate || LocalDate.now() >= expireDate) {

//ideally all user should have passwordChangedDate, it gets set initially when user is created and keeps getting updated
//but if null for some old users, thn expire it.
if(user.passwordChangedDate == null) return true

//see if lastChangeDate + passwordExpireDays < today - thn password has already expired
LocalDateTime expiresOnDate = user.passwordChangedDate.plusDays(passwordConfig.passwordExpireDays)
if (expiresOnDate.toLocalDate() < LocalDate.now()) {
return true
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import org.springframework.beans.factory.annotation.Autowired
import grails.gorm.transactions.Transactional
import yakworks.security.gorm.model.AppUser
import yakworks.security.gorm.model.SecLoginHistory
import yakworks.security.gorm.model.SecPasswordHistory
import yakworks.security.services.PasswordValidator
import yakworks.security.user.CurrentUser

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@ package yakworks.security.gorm.api
import groovy.transform.CompileStatic

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration

import yakworks.gorm.config.QueryConfig
import yakworks.security.user.CurrentUser


@CompileStatic
@ConfigurationProperties(prefix="app.security")
class UserSecurityConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import grails.gorm.transactions.Transactional
import yakworks.api.Result
import yakworks.api.problem.Problem
import yakworks.api.problem.data.DataProblemCodes
import yakworks.commons.lang.Validate
import yakworks.security.PasswordConfig
import yakworks.security.services.PasswordValidator

Expand All @@ -35,6 +36,7 @@ class AppUserRepo extends LongIdGormRepo<AppUser> {
@Autowired PasswordConfig passwordConfig
@Autowired PasswordValidator passwordValidator


//cached instance of the query for id to keep it fast
KeyExistsQuery usernameExistsQuery

Expand Down Expand Up @@ -213,8 +215,11 @@ class AppUserRepo extends LongIdGormRepo<AppUser> {
* Updates user's password, Creates password history if enabled.
*/
void updatePassword(AppUser user, String password) {
//no change, its same password, just exit fast
Validate.notEmpty(password)
if (passwordEncoder.matches(password, user.passwordHash)) return

String hashed = encodePassword(password)
if (user.passwordHash == hashed) return //no change so just exit fast

Result valid
if (user.isNew()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class SecPasswordHistoryRepo extends LongIdGormRepo<SecPasswordHistory> {
@Inject PasswordConfig passwordConfig

/**
*
* Creates password history for user,
* Will keep only `passwordConfig.historyLength` records and deletes older if there are more
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,12 @@ class SecuritySeedData {

static void createAppUsers(){

AppUser admin = new AppUser([
id: 1L, username: "admin", email: "[email protected]", password:"123", orgId: 2
]).persist()
AppUser admin = new AppUser([id: 1L, username: "admin", email: "[email protected]", password:"123", orgId: 2]).persist()

admin.addRole(Roles.ADMIN, true)
admin.addRole(Roles.MANAGER, true)

AppUser custUser = new AppUser([
id: 2L, username: "cust", email: "[email protected]", password:"123", orgId: 2
]).persist()
AppUser custUser = new AppUser([id: 2L, username: "cust", email: "[email protected]", password:"123", orgId: 2]).persist()
assert custUser.id == 2

custUser.addRole(Roles.CUSTOMER, true)
Expand All @@ -41,9 +37,7 @@ class SecuritySeedData {
)
assert noRoleUser.id == 3

AppUser readonlyUser = new AppUser([
id: 4L, username: "readonly", email: "[email protected]", password:"123", orgId: 2
]).persist()
AppUser readonlyUser = new AppUser([id: 4L, username: "readonly", email: "[email protected]", password:"123", orgId: 2]).persist()
assert readonlyUser.id == 4
readonlyUser.addRole(Roles.READ_ONLY, true)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,15 @@ class AppUserSpec extends Specification implements GormHibernateTest, SecurityTe
// return get(entity.id)
// }

def "test user create"(){
def "create user"(){
when:
AppUser user = new AppUser(id: 1, orgId: 1, username: "admin", email: "[email protected]", password: "123Foo")
user.persist(flush: true)

then:
user.id == 1
user.passwordHash
user.passwordChangedDate
}

void "did it get the audit stamp fields"() {
Expand Down
Loading