Skip to content

Commit

Permalink
v0.2.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Choon-Chern Lim committed Mar 8, 2016
1 parent c6ce4e8 commit 018347e
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 41 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change Log

## 0.2.1 - 2016-03-07

* Added `SAMLConfigBean.keystorePrivateKeyPassword` to add password for private key.
* Kept storepass and keypass separate.
* Excluded `xml-apis` from dependency because it's known to cause problems in WAS.

## 0.2.0 - 2016-03-02

* Options to allow different authentication method. Default is user/password using IdP's form login page.
Expand Down
57 changes: 25 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Tested against:-
<dependency>
<groupId>com.github.choonchernlim</groupId>
<artifactId>spring-security-adfs-saml2</artifactId>
<version>0.2.0</version>
<version>0.2.1</version>
</dependency>
```

Expand All @@ -32,7 +32,7 @@ Tested against:-
* Sp's public/private keys - to generate digital signature before sending SAML messages to IdP.
* IdP's public certificate - to verify IdP's SAML messages to prevent man-in-the-middle attack.
* To import IdP's public certificate into keystore:-
* `keytool -importcert -file idp-adfs-server.cer -keystore keystore.jks -alias "idp-adfs-server"`
* `keytool -importcert -file idp-adfs-server.cer -keystore keystore.jks -alias idp-adfs-server`

## Usage

Expand All @@ -42,42 +42,17 @@ Tested against:-
@EnableWebSecurity
class AppSecurityConfig extends SAMLWebSecurityConfigurerAdapter {

// All the parameters you can used to configure this module
// See `Configuring SAMLConfigBean` section below for more info.
@Override
protected SAMLConfigBean samlConfigBean() {
return new SAMLConfigBeanBuilder()
// (Required) assuming IdP's ADFS link is https://idp-adfs-server/adfs/ls ...
.setAdfsHostName("idp-adfs-server")
// (Required) keystore containing both Sp's public/private key and imported IdP's
// public certificate.
.setKeyStoreResource(new DefaultResourceLoader().getResource("classpath:keystore.jks"))
// (Required) keystore alias.
.setKeystoreAlias("alias")
// (Required) keystore password.
.setKeystorePassword("secret")
// (Required) where to redirect user if there's no saved request in session
// (ie: user gets logged in by clicking on `/saml/login` link).
.setKeystorePassword("storepass")
.setKeystorePrivateKeyPassword("keypass")
.setSuccessLoginDefaultUrl("/")
// (Required) where to redirect user when logging out.
.setSuccessLogoutUrl("/goodbye")
// (Optional) Where to redirect user on failed login. This is probably not needed
// because IdP should handle the failed login instead of returning back to Sp.
// So, you probably don't need to set this.
// Default is empty string.
.setFailedLoginDefaultUrl("/error")
// (Optional) An opportunity to define user authorities or user properties either
// by cherry picking the claim values from IdP's SAML response or from other
// data sources.
// Default is null.
.setSamlUserDetailsService(new SAMLUserDetailsService() {
@Override
public Object loadUserBySAML(final SAMLCredential credential) throws UsernameNotFoundException {
return ...;
}
})
// (Optional) Authentication method (WIA, user/password, etc)
// Default is user/password authentication.
.setAuthnContexts(ImmutableSet.of(CustomAuthnContext.WINDOWS_INTEGRATED_AUTHN_CTX))
.createSAMLConfigBean();
}

Expand All @@ -91,7 +66,7 @@ class AppSecurityConfig extends SAMLWebSecurityConfigurerAdapter {
SignatureConstants.ALGO_ID_DIGEST_SHA512);
}

// call `samlizedConfig(http)` first to redecorate `http` with SAML configuration
// call `samlizedConfig(http)` first to decorate `http` with SAML configuration
// before configuring app specific HTTP security
@Override
protected void configure(final HttpSecurity http) throws Exception {
Expand All @@ -100,7 +75,7 @@ class AppSecurityConfig extends SAMLWebSecurityConfigurerAdapter {
.anyRequest().authenticated();
}

// call `samlizedConfig(web)` first to redecorate `web` with SAML configuration
// call `samlizedConfig(web)` first to decorate `web` with SAML configuration
// before configuring app specific web security
@Override
public void configure(final WebSecurity web) throws Exception {
Expand All @@ -109,6 +84,24 @@ class AppSecurityConfig extends SAMLWebSecurityConfigurerAdapter {
}
```

## Configuring SAMLConfigBean

`SAMLConfigBean` stores app-specific security configuration.

|Property |Required? |Description |
|---------------------------|----------|----------------------------------------------------------------------------------------------------------|
|adfsHostName |Yes |ADFS host name without HTTPS protocol.<p>If ADFS link is `https://idp-adfs-server/adfs/ls`, the value should be `idp-adfs-server`.|
|keyStoreResource |Yes |Keystore containing app's public/private key and ADFS' certificate with public key. |
|keystoreAlias |Yes |Keystore alias. |
|keystorePassword |Yes |Keystore password. |
|keystorePrivateKeyPassword |Yes |Keystore private key password. |
|successLoginDefaultUrl |Yes |Where to redirect user on successful login if no saved request is found in the session. |
|successLogoutUrl |Yes |Where to redirect user on successful logout. |
|failedLoginDefaultUrl |No |Where to redirect user on failed login. This value is set to null, which returns 401 error code on failed login. But, in theory, this will never be used because IdP will handled the failed login on IdP login page.<p><p>Default is `''`, which return 401 error code.|
|samlUserDetailsService |No |For configuring user authorities if needed.<p><p>Default is `null`. |
|authnContexts |No |Determine what authentication methods to use. To use the order of authentication methods defined by IdP, set as empty set. To enable Windows Integrated Auth (WIA) cross browsers and OSes, use `CustomAuthnContext.WINDOWS_INTEGRATED_AUTHN_CTX`.<p><p>Default is `AuthnContext.PASSWORD_AUTHN_CTX` where IdP login page is displayed to obtain user/password.|


## Important SAML Endpoints

There are several SAML processing endpoints, but these are the ones you probably care:-
Expand Down
9 changes: 8 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</parent>

<artifactId>spring-security-adfs-saml2</artifactId>
<version>0.2.0</version>
<version>0.2.1</version>
<packaging>jar</packaging>

<name>Spring Security ADFS SAML2</name>
Expand Down Expand Up @@ -62,6 +62,13 @@
<groupId>org.springframework.security.extensions</groupId>
<artifactId>spring-security-saml2-core</artifactId>
<version>${spring-security-saml2.version}</version>
<exclusions>
<!-- Remove dependency that causes problem in WAS -->
<exclusion>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public final class SAMLConfigBean {
*/
private final String keystorePassword;

/**
* (REQUIRED) Keystore private key password.
*/
private final String keystorePrivateKeyPassword;

/**
* (REQUIRED) Where to redirect user on successful login if no saved request is found in the session.
*/
Expand Down Expand Up @@ -75,17 +80,20 @@ public final class SAMLConfigBean {
final Resource keyStoreResource,
final String keystoreAlias,
final String keystorePassword,
final String keystorePrivateKeyPassword,
final String successLoginDefaultUrl,
final String successLogoutUrl,
final String failedLoginDefaultUrl,
final SAMLUserDetailsService samlUserDetailsService,
final Set<String> authnContexts) {

//@formatter:off
this.adfsHostName = expect(adfsHostName, "ADFS host name").not().toBeBlank().check();

this.keyStoreResource = (Resource) expect(keyStoreResource, "Key store").not().toBeNull().check();
this.keystoreAlias = expect(keystoreAlias, "Keystore alias").not().toBeBlank().check();
this.keystorePassword = expect(keystorePassword, "Keystore password").not().toBeBlank().check();
this.keystorePrivateKeyPassword = expect(keystorePrivateKeyPassword, "Keystore private key password").not().toBeBlank().check();

this.successLoginDefaultUrl = expect(successLoginDefaultUrl, "Success login URL").not().toBeBlank().check();
this.successLogoutUrl = expect(successLogoutUrl, "Success logout URL").not().toBeBlank().check();
Expand All @@ -95,6 +103,7 @@ public final class SAMLConfigBean {
this.samlUserDetailsService = samlUserDetailsService;

this.authnContexts = Optional.fromNullable(authnContexts).or(ImmutableSet.of(AuthnContext.PASSWORD_AUTHN_CTX));
//@formatter:on
}

public String getAdfsHostName() {
Expand All @@ -113,6 +122,10 @@ public String getKeystorePassword() {
return keystorePassword;
}

public String getKeystorePrivateKeyPassword() {
return keystorePrivateKeyPassword;
}

public String getSuccessLoginDefaultUrl() {
return successLoginDefaultUrl;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public final class SAMLConfigBeanBuilder {
private Resource keyStoreResource;
private String keystoreAlias;
private String keystorePassword;
private String keystorePrivateKeyPassword;
private String successLoginDefaultUrl;
private String successLogoutUrl;
private String failedLoginDefaultUrl;
Expand All @@ -39,6 +40,11 @@ public SAMLConfigBeanBuilder setKeystorePassword(final String keystorePassword)
return this;
}

public SAMLConfigBeanBuilder setKeystorePrivateKeyPassword(final String keystorePrivateKeyPassword) {
this.keystorePrivateKeyPassword = keystorePrivateKeyPassword;
return this;
}

public SAMLConfigBeanBuilder setSuccessLoginDefaultUrl(final String successLoginDefaultUrl) {
this.successLoginDefaultUrl = successLoginDefaultUrl;
return this;
Expand Down Expand Up @@ -69,6 +75,7 @@ public SAMLConfigBean createSAMLConfigBean() {
keyStoreResource,
keystoreAlias,
keystorePassword,
keystorePrivateKeyPassword,
successLoginDefaultUrl,
successLogoutUrl,
failedLoginDefaultUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ public KeyManager keyManager() {
return new JKSKeyManager(samlConfigBean().getKeyStoreResource(),
samlConfigBean().getKeystorePassword(),
ImmutableMap.of(samlConfigBean().getKeystoreAlias(),
samlConfigBean().getKeystorePassword()),
samlConfigBean().getKeystorePrivateKeyPassword()),
samlConfigBean().getKeystoreAlias());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class SAMLConfigBeanSpec extends Specification {
setKeyStoreResource(keystoreResource).
setKeystoreAlias('keystoreAlias').
setKeystorePassword('keystorePassword').
setKeystorePrivateKeyPassword('keystorePrivateKeyPassword').
setSuccessLoginDefaultUrl('successLoginDefaultUrl').
setSuccessLogoutUrl('successLogoutUrl').
setFailedLoginDefaultUrl('failedLoginDefaultUrl').
Expand All @@ -42,6 +43,7 @@ class SAMLConfigBeanSpec extends Specification {
bean.keyStoreResource == keystoreResource
bean.keystoreAlias == 'keystoreAlias'
bean.keystorePassword == 'keystorePassword'
bean.keystorePrivateKeyPassword == 'keystorePrivateKeyPassword'
bean.successLoginDefaultUrl == 'successLoginDefaultUrl'
bean.successLogoutUrl == 'successLogoutUrl'
bean.failedLoginDefaultUrl == 'failedLoginDefaultUrl'
Expand All @@ -62,6 +64,7 @@ class SAMLConfigBeanSpec extends Specification {
bean.keyStoreResource == keystoreResource
bean.keystoreAlias == 'keystoreAlias'
bean.keystorePassword == 'keystorePassword'
bean.keystorePrivateKeyPassword == 'keystorePrivateKeyPassword'
bean.successLoginDefaultUrl == 'successLoginDefaultUrl'
bean.successLogoutUrl == 'successLogoutUrl'
bean.failedLoginDefaultUrl == ''
Expand All @@ -88,13 +91,14 @@ class SAMLConfigBeanSpec extends Specification {
thrown expectedException

where:
field | expectedException
'AdfsHostName' | StringBlankPreconditionException
'KeyStoreResource' | ObjectNullPreconditionException
'KeystoreAlias' | StringBlankPreconditionException
'KeystorePassword' | StringBlankPreconditionException
'SuccessLoginDefaultUrl' | StringBlankPreconditionException
'SuccessLogoutUrl' | StringBlankPreconditionException
field | expectedException
'AdfsHostName' | StringBlankPreconditionException
'KeyStoreResource' | ObjectNullPreconditionException
'KeystoreAlias' | StringBlankPreconditionException
'KeystorePassword' | StringBlankPreconditionException
'KeystorePrivateKeyPassword' | StringBlankPreconditionException
'SuccessLoginDefaultUrl' | StringBlankPreconditionException
'SuccessLogoutUrl' | StringBlankPreconditionException
}

}

0 comments on commit 018347e

Please sign in to comment.