Skip to content

Commit

Permalink
Add support to set mapped user attributes with values from saml respo…
Browse files Browse the repository at this point in the history
…nse.
  • Loading branch information
fpanwaskar committed Feb 28, 2012
1 parent 3b99fb0 commit a283041
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ spring-security-saml-*-plugin.xml
spring-security-saml-*.pom
.DS_Store
web-app/
*.iws
*.ipr
*.iml
4 changes: 4 additions & 0 deletions grails-app/domain/test/TestSamlUser.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class TestSamlUser {
boolean accountExpired
boolean accountLocked
boolean passwordExpired
String email
String firstName

Set<TestRole> getAuthorities() {
TestUserRole.findAllByUser(this).collect { it.role } as Set
Expand All @@ -31,5 +33,7 @@ class TestSamlUser {
static constraints = {
username blank: false, unique: true
password blank: false
email nullable: true
firstName nullable: true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
*/
package es.salenda.grails.plugins.springsecurity.saml

import java.util.Collection
import java.util.Set

import org.codehaus.groovy.grails.plugins.springsecurity.GormUserDetailsService
import org.springframework.beans.BeanUtils
import org.springframework.security.core.GrantedAuthority
Expand Down Expand Up @@ -53,6 +50,7 @@ class SpringSamlUserDetailsService extends GormUserDetailsService implements SAM
}

def user = generateSecurityUser(username)
user = mapAdditionalAttributes(credential, user)
if (user) {
log.debug "Loading database roles for $username..."
def authorities = getAuthoritiesForUser(credential)
Expand All @@ -76,6 +74,18 @@ class SpringSamlUserDetailsService extends GormUserDetailsService implements SAM
return credential.nameID?.value
}
}

protected Object mapAdditionalAttributes(credential, user) {
samlUserAttributeMappings.each { key, value ->
def attribute = credential.getAttributeByName(value)
def samlValue = attribute?.attributeValues?.value
if (samlValue) {
user."$key" = samlValue?.first()
}
}

user
}

protected Collection<GrantedAuthority> getAuthoritiesForUser(SAMLCredential credential) {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthorityImpl>()
Expand Down Expand Up @@ -159,7 +169,8 @@ class SpringSamlUserDetailsService extends GormUserDetailsService implements SAM
if (!existingUser) {
user.save()
} else {
user = existingUser
user = updateUserProperties(existingUser, user)

joinClass.removeAll user
}

Expand All @@ -170,6 +181,13 @@ class SpringSamlUserDetailsService extends GormUserDetailsService implements SAM
}
}
}

private Object updateUserProperties(existingUser, user) {
samlUserAttributeMappings.each { key, value ->
existingUser."$key" = user."$key"
}
return existingUser
}

private Object getRole(String authority) {
if (authority && authorityNameField && authorityClassName) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package es.salenda.grails.plugins.springsecurity.saml

import grails.test.mixin.*
import org.junit.*

@TestFor(MetadataController)
class MetadataControllerTests {

def metadata

@Before
void init() {
metadata = [hostedSPName: 'splocal', SPEntityNames: ['testsp'], IDPEntityNames: ['testidp'] ]
controller.metadata = metadata
}

void testIndexReturnsMetadataValuesInModel() {
def model = controller.index()

assert model.hostedSP == metadata.hostedSPName
assert model.spList == metadata.SPEntityNames
assert model.idpList == metadata.IDPEntityNames
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class SpringSamlUserDetailsServiceTest {

assert user.username == username
}

@Test
void "loadUserBySAML should raise an exception if username not supplied in saml response"() {

Expand Down Expand Up @@ -131,6 +131,23 @@ class SpringSamlUserDetailsServiceTest {
assert TestSamlUser.count() == 1
assert TestSamlUser.findByUsername(userDetails.username)
}

@Test
void "loadUserBySAML should set additional mapped attributes on the user"() {
def emailAddress = "test@mailinator.com"
def firstname = "Jack"
service.samlAutoCreateActive = true
service.samlAutoCreateKey = 'username'

service.samlUserAttributeMappings = [ email: "$MAIL_ATTR_NAME", firstName: "$FIRSTNAME_ATTR_NAME" ]
setMockSamlAttributes(credential, ["$USERNAME_ATTR_NAME": username, "$MAIL_ATTR_NAME": emailAddress, "$FIRSTNAME_ATTR_NAME":firstname])

def user = service.loadUserBySAML(credential)
def samlUser = TestSamlUser.findByUsername(username)
assert samlUser.email == emailAddress
assert samlUser.firstName == firstname
}


@Test
void "loadUserBySAML should not persist a user that already exists"() {
Expand Down Expand Up @@ -199,4 +216,46 @@ class SpringSamlUserDetailsServiceTest {
assert removedExistingRoles
assert savedNewRoles
}

@Test
void "loadUserBySAML should set any mapped fields for a user"() {
def emailAddress = "test@mailinator.com"
def firstname = "Jack"

service.samlAutoCreateActive = true
service.samlAutoCreateKey = 'username'
service.samlUserAttributeMappings = [ email: "$MAIL_ATTR_NAME", firstName: "$FIRSTNAME_ATTR_NAME" ]
setMockSamlAttributes(credential, ["$USERNAME_ATTR_NAME": username, "$MAIL_ATTR_NAME": emailAddress, "$FIRSTNAME_ATTR_NAME":firstname])

def user = new TestSamlUser(username: username, password: 'test')
assert user.save()

TestUserRole.metaClass.'static'.removeAll = {TestSamlUser samlUser -> }

service.loadUserBySAML(credential)

def updatedUser = TestSamlUser.findByUsername(username)
assert updatedUser.email == emailAddress
assert updatedUser.firstName == firstname
}

@Test
void "loadUserBySAML should update mapped fields for a user"() {
def intialEmail = 'myfirstmail@mailinator.com'
def emailAddress = "test@mailinator.com"

service.samlAutoCreateActive = true
service.samlAutoCreateKey = 'username'
service.samlUserAttributeMappings = [ email: "$MAIL_ATTR_NAME"]
setMockSamlAttributes(credential, ["$USERNAME_ATTR_NAME": username, "$MAIL_ATTR_NAME": emailAddress])
TestUserRole.metaClass.'static'.removeAll = {TestSamlUser samlUser -> }

def user = new TestSamlUser(username: username, password: 'test', email: intialEmail)
assert user.save()

service.loadUserBySAML(credential)

def updatedUser = TestSamlUser.findByUsername(username)
assert updatedUser.email == emailAddress
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class UnitTestUtils {

static final USERNAME_ATTR_NAME = 'usernameAttribute'
static final GROUP_ATTR_NAME = 'groups'
static final MAIL_ATTR_NAME = 'mail'
static final FIRSTNAME_ATTR_NAME = 'firstname'

static void mockWithTransaction() {
TestSamlUser.metaClass.'static'.withTransaction = { Closure callable ->
Expand Down Expand Up @@ -71,6 +73,18 @@ class UnitTestUtils {
]
]
}
else if (name == MAIL_ATTR_NAME) {
return [attributeValues: [
[value: attributes.get("${MAIL_ATTR_NAME}")]
]
]
}
else if (name == FIRSTNAME_ATTR_NAME) {
return [attributeValues: [
[value: attributes.get("${FIRSTNAME_ATTR_NAME}")]
]
]
}
else if (name == GROUP_ATTR_NAME) {
return [
[attributeValues: [
Expand Down

0 comments on commit a283041

Please sign in to comment.