Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
dariah
dariahsp
Commits
6a7cf6f8
Commit
6a7cf6f8
authored
Oct 31, 2020
by
Gradl, Tobias
Browse files
6: Implement configurable role mapping and hierarchy
Task-Url:
#6
parent
977dcd76
Pipeline
#17546
passed with stage
in 1 minute and 52 seconds
Changes
12
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
dariahsp-core/src/main/java/eu/dariah/de/dariahsp/Constants.java
View file @
6a7cf6f8
...
...
@@ -4,9 +4,5 @@ public class Constants {
public
enum
AUTHENTICATION_STAGE
{
AUTHENTICATION
,
ATTRIBUTES
}
public
enum
REQUIRED_ATTRIBUTE_CHECKLOGIC
{
AND
,
OR
,
OPTIONAL
}
public
final
static
String
SAML_CLIENT_NAME
=
"Saml2Client"
;
public
final
static
String
LOCAL_CLIENT_NAME
=
"FormClient"
;
}
dariahsp-core/src/main/java/eu/dariah/de/dariahsp/authenticator/UserProfileCreator.java
View file @
6a7cf6f8
package
eu.dariah.de.dariahsp.authenticator
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.Optional
;
import
java.util.Set
;
import
org.pac4j.core.context.WebContext
;
import
org.pac4j.core.credentials.Credentials
;
import
org.pac4j.core.profile.UserProfile
;
import
org.pac4j.core.profile.creator.AuthenticatorProfileCreator
;
import
org.pac4j.core.profile.creator.ProfileCreator
;
import
eu.dariah.de.dariahsp.config.model.RoleMapping
;
import
eu.dariah.de.dariahsp.model.ExtendedUserProfile
;
import
eu.dariah.de.dariahsp.model.UserImpl
;
import
lombok.Data
;
@Data
public
class
UserProfileCreator
<
C
extends
Credentials
>
implements
ProfileCreator
<
C
>
{
public
final
static
UserProfileCreator
INSTANCE
=
new
UserProfileCreator
<>();
private
static
final
String
ROLE_PREFIX
=
"ROLE_"
;
private
final
String
clientName
;
private
List
<
RoleMapping
>
roleMappings
;
@Override
public
Optional
<
UserProfile
>
create
(
final
C
credentials
,
final
WebContext
context
)
{
if
(
credentials
.
getUserProfile
()==
null
)
{
return
Optional
.
empty
();
}
ExtendedUserProfile
profile
=
new
ExtendedUserProfile
(
credentials
.
getUserProfile
());
return
Optional
.
ofNullable
(
profile
);
if
(
this
.
canMapRoles
(
profile
))
{
profile
.
setRoles
(
this
.
getMappedRoles
(
profile
));
}
return
Optional
.
ofNullable
(
profile
);
}
private
boolean
canMapRoles
(
ExtendedUserProfile
profile
)
{
return
clientName
!=
null
&&
profile
.
getExternalRoles
()!=
null
&&
!
profile
.
getExternalRoles
().
isEmpty
()
&&
roleMappings
!=
null
&&
!
roleMappings
.
isEmpty
();
}
private
Set
<
String
>
getMappedRoles
(
ExtendedUserProfile
profile
)
{
Set
<
String
>
roles
=
new
LinkedHashSet
<>();
for
(
RoleMapping
rm
:
roleMappings
)
{
for
(
String
client
:
rm
.
getMappings
().
keySet
())
{
if
(!
client
.
equals
(
clientName
))
{
continue
;
}
for
(
String
extRole
:
profile
.
getExternalRoles
())
{
if
(!
extRole
.
trim
().
isEmpty
()
&&
rm
.
getMappings
().
get
(
client
).
contains
(
extRole
.
trim
()))
{
roles
.
add
(
ROLE_PREFIX
+
rm
.
getRole
().
toUpperCase
());
}
}
}
}
return
roles
;
}
}
dariahsp-core/src/main/java/eu/dariah/de/dariahsp/config/CombinedSecurityConfigurationAdapter.java
View file @
6a7cf6f8
package
eu.dariah.de.dariahsp.config
;
import
java.util.List
;
import
java.util.Optional
;
import
java.util.stream.Collectors
;
import
org.pac4j.core.client.Client
;
...
...
@@ -8,10 +9,8 @@ import org.pac4j.core.config.Config;
import
org.pac4j.springframework.security.web.Pac4jEntryPoint
;
import
org.pac4j.springframework.security.web.SecurityFilter
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.security.access.expression.SecurityExpressionHandler
;
import
org.springframework.security.access.hierarchicalroles.RoleHierarchy
;
import
org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl
;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity
;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
;
import
org.springframework.security.config.http.SessionCreationPolicy
;
...
...
@@ -22,14 +21,8 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi
public
class
CombinedSecurityConfigurationAdapter
extends
WebSecurityConfigurerAdapter
{
@Autowired
private
Config
config
;
@Autowired
private
Optional
<
RoleHierarchy
>
roleHierarchy
;
@Bean
public
RoleHierarchy
roleHierarchy
()
{
RoleHierarchyImpl
r
=
new
RoleHierarchyImpl
();
r
.
setHierarchy
(
"ROLE_ADMINISTRATOR > ROLE_CONTRIBUTOR > ROLE_USER"
);
return
r
;
}
@Override
protected
void
configure
(
final
HttpSecurity
http
)
throws
Exception
{
...
...
@@ -57,7 +50,9 @@ public class CombinedSecurityConfigurationAdapter extends WebSecurityConfigurerA
protected
SecurityExpressionHandler
<
FilterInvocation
>
webExpressionHandler
()
{
DefaultWebSecurityExpressionHandler
defaultWebSecurityExpressionHandler
=
new
DefaultWebSecurityExpressionHandler
();
defaultWebSecurityExpressionHandler
.
setRoleHierarchy
(
roleHierarchy
());
if
(
roleHierarchy
.
isPresent
())
{
defaultWebSecurityExpressionHandler
.
setRoleHierarchy
(
roleHierarchy
.
get
());
}
return
defaultWebSecurityExpressionHandler
;
}
}
dariahsp-core/src/main/java/eu/dariah/de/dariahsp/config/LocalSecurityProperties.java
View file @
6a7cf6f8
package
eu.dariah.de.dariahsp.config
;
import
eu.dariah.de.dariahsp.local.LocalUsers
;
import
lombok.Getter
;
import
lombok.Setter
;
import
lombok.Data
;
@
Getter
@Setter
@
Data
public
class
LocalSecurityProperties
{
private
boolean
enabled
;
private
String
authorizerName
=
"local"
;
private
LocalUsers
[]
users
;
}
\ No newline at end of file
dariahsp-core/src/main/java/eu/dariah/de/dariahsp/config/SamlProperties.java
View file @
6a7cf6f8
...
...
@@ -14,6 +14,7 @@ import lombok.Setter;
@Getter
@Setter
public
class
SamlProperties
{
private
boolean
enabled
=
true
;
private
String
authorizerName
=
"saml2"
;
private
final
KeystoreProperties
keystore
=
new
KeystoreProperties
();
private
final
MetadataProperties
metadata
=
new
MetadataProperties
();
private
final
SpProperties
sp
=
new
SpProperties
();
...
...
dariahsp-core/src/main/java/eu/dariah/de/dariahsp/config/SecurityConfig.java
View file @
6a7cf6f8
...
...
@@ -6,18 +6,19 @@ import java.nio.file.Files;
import
java.nio.file.Paths
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Optional
;
import
java.util.Set
;
import
org.pac4j.core.authorization.authorizer.RequireAnyRoleAuthorizer
;
import
org.pac4j.core.client.Client
;
import
org.pac4j.core.client.Clients
;
import
org.pac4j.core.config.Config
;
import
org.pac4j.core.context.WebContext
;
import
org.pac4j.core.profile.ProfileManager
;
import
org.pac4j.core.profile.factory.ProfileManagerFactory
;
import
org.pac4j.core.credentials.UsernamePasswordCredentials
;
import
org.pac4j.http.client.indirect.FormClient
;
import
org.pac4j.saml.client.SAML2Client
;
import
org.pac4j.saml.config.SAML2Configuration
;
import
org.pac4j.saml.credentials.SAML2Credentials
;
import
org.pac4j.springframework.annotation.AnnotationConfig
;
import
org.pac4j.springframework.component.ComponentConfig
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
...
...
@@ -32,8 +33,10 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import
eu.dariah.de.dariahsp.CustomAuthorizer
;
import
eu.dariah.de.dariahsp.authenticator.LocalUsernamePasswordAuthenticator
;
import
eu.dariah.de.dariahsp.authenticator.UserProfileCreator
;
import
eu.dariah.de.dariahsp.config.model.RoleMapping
;
import
eu.dariah.de.dariahsp.metadata.MetadataHelper
;
import
lombok.Data
;
import
lombok.Getter
;
import
lombok.extern.slf4j.Slf4j
;
@Data
...
...
@@ -43,10 +46,12 @@ import lombok.extern.slf4j.Slf4j;
@ConfigurationProperties
(
prefix
=
"auth"
)
@Import
({
ComponentConfig
.
class
,
AnnotationConfig
.
class
})
public
class
SecurityConfig
{
private
String
salt
;
private
final
LocalSecurityProperties
local
=
new
LocalSecurityProperties
();
private
final
SamlProperties
saml
=
new
SamlProperties
();
@Getter
private
String
salt
;
@Getter
private
String
roleHierarchy
;
@Getter
private
final
List
<
RoleMapping
>
roleMappings
;
@Bean
public
Optional
<
LocalUsernamePasswordAuthenticator
>
localUsernamePasswordAuthenticator
()
{
...
...
@@ -69,6 +74,18 @@ public class SecurityConfig {
return
new
MetadataHelper
();
}
@Bean
public
Optional
<
RoleHierarchy
>
roleHierarchy
()
{
if
(
roleHierarchy
==
null
||
roleHierarchy
.
isEmpty
())
{
log
.
info
(
"RoleHierarchy not configured; no role hierarchy available"
);
return
Optional
.
empty
();
}
RoleHierarchyImpl
r
=
new
RoleHierarchyImpl
();
r
.
setHierarchy
(
roleHierarchy
);
log
.
info
(
"RoleHierarchy configured: {}"
,
roleHierarchy
);
return
Optional
.
of
(
r
);
}
@Bean
@SuppressWarnings
(
"rawtypes"
)
public
Config
config
()
throws
URISyntaxException
{
...
...
@@ -77,11 +94,11 @@ public class SecurityConfig {
Optional
<
FormClient
>
formClient
=
getFormClient
();
if
(
samlClient
.
isPresent
())
{
samlClient
.
get
().
setProfileCreator
(
User
ProfileCreator
.
INSTANCE
);
samlClient
.
get
().
setProfileCreator
(
saml2
ProfileCreator
()
);
clients
.
add
(
samlClient
.
get
());
}
if
(
formClient
.
isPresent
())
{
formClient
.
get
().
setProfileCreator
(
User
ProfileCreator
.
INSTANCE
);
formClient
.
get
().
setProfileCreator
(
local
ProfileCreator
()
);
clients
.
add
(
formClient
.
get
());
}
...
...
@@ -135,14 +152,32 @@ public class SecurityConfig {
cfg
.
setSupportedProtocols
(
saml
.
getSp
().
getSupportedProtocols
());
cfg
.
setHttpClient
(
saml
.
getSp
().
getHttpClient
());
return
Optional
.
of
(
new
SAML2Client
(
cfg
));
SAML2Client
c
=
new
SAML2Client
(
cfg
);
c
.
setName
(
saml
.
getAuthorizerName
());
return
Optional
.
of
(
c
);
}
private
Optional
<
FormClient
>
getFormClient
()
{
Optional
<
LocalUsernamePasswordAuthenticator
>
localUsernamePasswordAuthenticator
=
localUsernamePasswordAuthenticator
();
if
(
localUsernamePasswordAuthenticator
.
isPresent
())
{
return
Optional
.
of
(
new
FormClient
(
"/login"
,
localUsernamePasswordAuthenticator
.
get
()));
FormClient
c
=
new
FormClient
(
"/login"
,
localUsernamePasswordAuthenticator
.
get
());
c
.
setName
(
local
.
getAuthorizerName
());
return
Optional
.
of
(
c
);
}
return
Optional
.
empty
();
}
private
UserProfileCreator
<
SAML2Credentials
>
saml2ProfileCreator
()
{
UserProfileCreator
<
SAML2Credentials
>
saml2ProfileCreator
=
new
UserProfileCreator
<>(
saml
.
getAuthorizerName
());
saml2ProfileCreator
.
setRoleMappings
(
roleMappings
);
return
saml2ProfileCreator
;
}
private
UserProfileCreator
<
UsernamePasswordCredentials
>
localProfileCreator
()
{
UserProfileCreator
<
UsernamePasswordCredentials
>
localProfileCreator
=
new
UserProfileCreator
<>(
local
.
getAuthorizerName
());
localProfileCreator
.
setRoleMappings
(
roleMappings
);
return
localProfileCreator
;
}
}
dariahsp-core/src/main/java/eu/dariah/de/dariahsp/config/model/RoleMapping.java
0 → 100644
View file @
6a7cf6f8
package
eu.dariah.de.dariahsp.config.model
;
import
java.util.Map
;
import
java.util.Set
;
import
lombok.Data
;
@Data
public
class
RoleMapping
{
private
String
role
;
private
Map
<
String
,
Set
<
String
>>
mappings
;
}
dariahsp-core/src/main/java/eu/dariah/de/dariahsp/metadata/MetadataHelper.java
View file @
6a7cf6f8
...
...
@@ -15,16 +15,19 @@ import org.springframework.core.io.Resource;
import
org.springframework.util.FileCopyUtils
;
import
eu.dariah.de.dariahsp.Constants
;
import
eu.dariah.de.dariahsp.config.SecurityConfig
;
import
lombok.extern.slf4j.Slf4j
;
@Slf4j
public
class
MetadataHelper
implements
InitializingBean
{
@Autowired
private
SecurityConfig
securityConfig
;
@Autowired
private
Config
config
;
private
SAML2Configuration
saml2Config
=
null
;
@Override
public
void
afterPropertiesSet
()
throws
Exception
{
Optional
<?>
client
=
config
.
getClients
().
findClient
(
Constants
.
SAML_CLIENT_NAME
);
Optional
<?>
client
=
config
.
getClients
().
findClient
(
securityConfig
.
getSaml
().
getAuthorizerName
()
);
if
(
client
.
isPresent
())
{
saml2Config
=
SAML2Client
.
class
.
cast
(
client
.
get
()).
getConfiguration
();
}
...
...
dariahsp-core/src/main/resources/config.sample.yml
0 → 100644
View file @
6a7cf6f8
# Config options of the dariahsp core library
# Commented properties reflect default values
auth
:
salt
:
Qmwp4CO7LDkOUDouAcCcUqd9ZGNbRG5Jyr5lpntOuB9
rolehierarchy
:
ROLE_ADMINISTRATOR > ROLE_CONTRIBUTOR > ROLE_USER
rolemappings
:
-
role
:
ADMINISTRATOR
mappings
:
local
:
[
"
application_admin"
]
saml2
:
[
"
application_admin"
]
-
role
:
CONTRIBUTOR
mappings
:
local
:
[
"
application_contributor"
]
saml2
:
[
"
application_contributor"
]
-
role
:
USER
mappings
:
local
:
[
"
application_user"
]
saml2
:
[
"
application_user"
]
local
:
enabled
:
true
# Same password for each user: 1234
users
:
-
username
:
'
admin'
passhash
:
'
$2y$10$nmTcpRxs.RFUstkJJms6U.AW61Jmr64s9VNQLuhpU8gYrgzCapwka'
roles
:
[
"
application_admin"
]
-
username
:
'
contributor'
passhash
:
'
$2y$10$nmTcpRxs.RFUstkJJms6U.AW61Jmr64s9VNQLuhpU8gYrgzCapwka'
roles
:
[
"
application_contributor"
]
-
username
:
'
user'
passhash
:
'
$2y$10$nmTcpRxs.RFUstkJJms6U.AW61Jmr64s9VNQLuhpU8gYrgzCapwka'
roles
:
[
"
application_user"
]
saml
:
enabled
:
false
keystore
:
path
:
/data/_srv/dariahsp/c105-229.cloud.gwdg.de.jks
pass
:
clariah
alias
:
c105-229.cloud.gwdg.de
aliaspass
:
clariah6
metadata
:
url
:
https://aaiproxy.de.dariah.eu/idp/
sp
:
#metadataResource: /data/_srv/dariahsp/sp_metadata.xml
maxAuthAge
:
-1
#baseUrl: https://c105-229.cloud.gwdg.de/dme
#entityId: ${auth.saml.sp.baseUrl}
signMetadata
:
true
#signingMethods: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
#digestMethods: http://www.w3.org/2001/04/xmlenc#sha256, http://www.w3.org/2001/04/xmlenc#sha512
#supportedProtocols: urn:oasis:names:tc:SAML:2.0:protocol
authnRequestSigned
:
true
logoutRequestSigned
:
true
wantsAssertionsSigned
:
true
wantsResponsesSigned
:
false
httpClientTimoutMs
:
2000
requiredAttributes
:
-
stage
:
ATTRIBUTES
required
:
true
attributeGroup
:
-
check
:
AND
attributes
:
-
friendlyName
:
mail
name
:
urn:oid:0.9.2342.19200300.100.1.3
nameFormat
:
urn:oasis:names:tc:SAML:2.0:attrname-format:uri
-
stage
:
ATTRIBUTES
required
:
true
attributeGroup
:
-
check
:
OR
attributes
:
-
friendlyName
:
dariahTermsOfUse
name
:
urn:oid:1.3.6.1.4.1.10126.1.52.4.15
nameFormat
:
urn:oasis:names:tc:SAML:2.0:attrname-format:uri
value
:
Terms_of_Use_v5.pdf
-
friendlyName
:
dariahTermsOfUse
name
:
urn:oid:1.3.6.1.4.1.10126.1.52.4.15
nameFormat
:
urn:oasis:names:tc:SAML:2.0:attrname-format:uri
value
:
foobar-service-agreement_version1.pdf
-
stage
:
AUTHENTICATION
required
:
true
attributeGroup
:
-
check
:
AND
attributes
:
-
friendlyName
:
eduPersonPrincipalName
name
:
urn:oid:1.3.6.1.4.1.5923.1.1.1.6
nameFormat
:
urn:oasis:names:tc:SAML:2.0:attrname-format:uri
-
stage
:
AUTHENTICATION
required
:
false
attributeGroup
:
-
check
:
OPTIONAL
attributes
:
-
friendlyName
:
mail
name
:
urn:oid:0.9.2342.19200300.100.1.3
nameFormat
:
urn:oasis:names:tc:SAML:2.0:attrname-format:uri
-
friendlyName
:
displayName
name
:
urn:oid:2.16.840.1.113730.3.1.241
nameFormat
:
urn:oasis:names:tc:SAML:2.0:attrname-format:uri
\ No newline at end of file
dariahsp-sample/src/main/java/eu/dariah/de/dariahsp/sample/controller/SampleController.java
View file @
6a7cf6f8
...
...
@@ -23,6 +23,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import
org.springframework.web.bind.annotation.ResponseBody
;
import
eu.dariah.de.dariahsp.Constants
;
import
eu.dariah.de.dariahsp.config.SecurityConfig
;
import
eu.dariah.de.dariahsp.error.AuthenticatorNotAvailable
;
import
eu.dariah.de.dariahsp.error.NotFoundException
;
import
eu.dariah.de.dariahsp.error.SAML2MetadataNotFoundException
;
...
...
@@ -37,6 +38,7 @@ public class SampleController {
@Value
(
"${auth.salt}"
)
private
String
salt
;
@Autowired
private
SecurityConfig
securityConfig
;
@Autowired
private
Config
config
;
@Autowired
private
JEEContext
jeeContext
;
@Autowired
private
ProfileManager
profileManager
;
...
...
@@ -59,7 +61,7 @@ public class SampleController {
@RequestMapping
(
"/login"
)
public
String
loginForm
(
Map
<
String
,
Object
>
map
)
{
final
FormClient
formClient
=
(
FormClient
)
config
.
getClients
().
findClient
(
Constants
.
LOCAL_CLIENT_NAME
).
get
();
final
FormClient
formClient
=
(
FormClient
)
config
.
getClients
().
findClient
(
securityConfig
.
getLocal
().
getAuthorizerName
()
).
get
();
map
.
put
(
"callbackUrl"
,
formClient
.
getCallbackUrl
());
return
"form"
;
}
...
...
dariahsp-sample/src/main/resources/application.yml
View file @
6a7cf6f8
...
...
@@ -10,19 +10,33 @@ logging:
auth
:
salt
:
Qmwp4CO7LDkOUDouAcCcUqd9ZGNbRG5Jyr5lpntOuB9
rolehierarchy
:
ROLE_ADMINISTRATOR > ROLE_CONTRIBUTOR > ROLE_USER
rolemappings
:
-
role
:
ADMINISTRATOR
mappings
:
local
:
[
"
application_admin"
]
saml2
:
[
"
application_admin"
]
-
role
:
CONTRIBUTOR
mappings
:
local
:
[
"
application_contributor"
]
saml2
:
[
"
application_contributor"
]
-
role
:
USER
mappings
:
local
:
[
"
application_user"
]
saml2
:
[
"
application_user"
]
local
:
enabled
:
true
# Same password for each user: 1234
users
:
-
username
:
'
admin'
passhash
:
'
$2y$10$nmTcpRxs.RFUstkJJms6U.AW61Jmr64s9VNQLuhpU8gYrgzCapwka'
roles
:
[
"
ROLE_ADMINISTRATOR
"
]
roles
:
[
"
application_admin
"
]
-
username
:
'
contributor'
passhash
:
'
$2y$10$nmTcpRxs.RFUstkJJms6U.AW61Jmr64s9VNQLuhpU8gYrgzCapwka'
roles
:
[
"
ROLE_CONTRIBUTOR
"
]
roles
:
[
"
application_contributor
"
]
-
username
:
'
user'
passhash
:
'
$2y$10$nmTcpRxs.RFUstkJJms6U.AW61Jmr64s9VNQLuhpU8gYrgzCapwka'
roles
:
[
"
ROLE_CONTRIBUTOR
"
]
roles
:
[
"
application_user
"
]
saml
:
enabled
:
false
keystore
:
...
...
dariahsp-sample/src/main/webapp/WEB-INF/views/home.jsp
View file @
6a7cf6f8
...
...
@@ -14,8 +14,8 @@
<a
href=
"/pac4jLogout?url=/?forcepostlogouturl"
>
pac4j local logout
</a><br
/>
<a
href=
"/pac4jCentralLogout?url=/?forcepostlogouturlafteridp"
>
pac4j central local logout
</a>
<br
/>
<a
href=
"forceLogin?client_name=
S
aml2
Client
"
>
Force SAML login
</a>
(even if already authenticated)
<br
/>
<a
href=
"forceLogin?client_name=
FormClient
"
>
Force local login
</a>
(even if already authenticated)
<br
/>
<a
href=
"forceLogin?client_name=
s
aml2"
>
Force SAML login
</a>
(even if already authenticated)
<br
/>
<a
href=
"forceLogin?client_name=
local
"
>
Force local login
</a>
(even if already authenticated)
<br
/>
<br
/><br
/>
profiles: ${profiles}
<br
/>
<br
/>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment