Skip to content

Commit

Permalink
Update password management to handle JDBC
Browse files Browse the repository at this point in the history
  • Loading branch information
SavvasMisaghMoayyed committed Jan 23, 2017
1 parent 1a446d0 commit 6fd8f31
Show file tree
Hide file tree
Showing 11 changed files with 350 additions and 118 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.apereo.cas.configuration.model.support.pm;

import org.apereo.cas.configuration.model.core.authentication.PasswordEncoderProperties;
import org.apereo.cas.configuration.model.core.ticket.SigningEncryptionProperties;
import org.apereo.cas.configuration.model.support.jpa.AbstractJpaProperties;
import org.apereo.cas.configuration.model.support.ldap.AbstractLdapProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

Expand All @@ -20,6 +22,7 @@ public class PasswordManagementProperties {
private String policyPattern = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[$@$!%*?&])[A-Za-z\\d$@$!%*?&]{8,10}";

private Ldap ldap = new Ldap();
private Jdbc jdbc = new Jdbc();
private Reset reset = new Reset();

public Reset getReset() {
Expand All @@ -46,6 +49,14 @@ public void setPolicyPattern(final String policyPattern) {
this.policyPattern = policyPattern;
}

public Jdbc getJdbc() {
return jdbc;
}

public void setJdbc(final Jdbc jdbc) {
this.jdbc = jdbc;
}

public Ldap getLdap() {
return ldap;
}
Expand All @@ -54,6 +65,47 @@ public void setLdap(final Ldap ldap) {
this.ldap = ldap;
}

public static class Jdbc extends AbstractJpaProperties {
@NestedConfigurationProperty
private PasswordEncoderProperties passwordEncoder = new PasswordEncoderProperties();

private String sqlChangePassword;
private String sqlFindEmail;
private String sqlSecurityQuestions;

public String getSqlChangePassword() {
return sqlChangePassword;
}

public void setSqlChangePassword(final String sqlChangePassword) {
this.sqlChangePassword = sqlChangePassword;
}

public String getSqlFindEmail() {
return sqlFindEmail;
}

public void setSqlFindEmail(final String sqlFindEmail) {
this.sqlFindEmail = sqlFindEmail;
}

public String getSqlSecurityQuestions() {
return sqlSecurityQuestions;
}

public void setSqlSecurityQuestions(final String sqlSecurityQuestions) {
this.sqlSecurityQuestions = sqlSecurityQuestions;
}

public PasswordEncoderProperties getPasswordEncoder() {
return passwordEncoder;
}

public void setPasswordEncoder(final PasswordEncoderProperties passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
}

public static class Ldap extends AbstractLdapProperties {

private String baseDn;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3406,3 +3406,34 @@ To learn more about this topic, [please review this guide](Password-Policy-Enfor
# cas.authn.pm.ldap.validator.attributeValues=top
# cas.authn.pm.ldap.validator.dn=
```

### JDBC

```properties
# cas.authn.pm.jdbc.sqlSecurityQuestions=SELECT question, answer FROM table WHERE user=?
# cas.authn.pm.jdbc.sqlFindEmail=SELECT email FROM table WHERE user=?
# cas.authn.pm.jdbc.sqlChangePassword=UPDATE table SET password=? WHERE user=?

# cas.authn.pm.jdbc.healthQuery=SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS
# cas.authn.pm.jdbc.isolateInternalQueries=false
# cas.authn.pm.jdbc.url=jdbc:hsqldb:mem:cas-hsql-database
# cas.authn.pm.jdbc.failFast=true
# cas.authn.pm.jdbc.isolationLevelName=ISOLATION_READ_COMMITTED
# cas.authn.pm.jdbc.dialect=org.hibernate.dialect.HSQLDialect
# cas.authn.pm.jdbc.leakThreshold=10
# cas.authn.pm.jdbc.propagationBehaviorName=PROPAGATION_REQUIRED
# cas.authn.pm.jdbc.batchSize=1
# cas.authn.pm.jdbc.user=sa
# cas.authn.pm.jdbc.ddlAuto=create-drop
# cas.authn.pm.jdbc.maxAgeDays=180
# cas.authn.pm.jdbc.password=
# cas.authn.pm.jdbc.autocommit=false
# cas.authn.pm.jdbc.driverClass=org.hsqldb.jdbcDriver
# cas.authn.pm.jdbc.idleTimeout=5000

# cas.authn.pm.jdbc.passwordEncoder.type=NONE|DEFAULT|STANDARD|BCRYPT|com.example.CustomPasswordEncoder
# cas.authn.pm.jdbc.passwordEncoder.characterEncoding=
# cas.authn.pm.jdbc.passwordEncoder.encodingAlgorithm=
# cas.authn.pm.jdbc.passwordEncoder.secret=
# cas.authn.pm.jdbc.passwordEncoder.strength=16
````
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,8 @@ This functionality needs to be explicitly enabled in CAS settings. You may also

The updated password may be stored inside an LDAP server.
To see the relevant list of CAS properties, please [review this guide](Configuration-Properties.html).

### JDBC

The updated password may be stored inside a database.
To see the relevant list of CAS properties, please [review this guide](Configuration-Properties.html).
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
* @author Scott Battaglia
* @since 3.0.0.3
*/
public abstract class AbstractJdbcUsernamePasswordAuthenticationHandler extends
AbstractUsernamePasswordAuthenticationHandler {
public abstract class AbstractJdbcUsernamePasswordAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {

private JdbcTemplate jdbcTemplate;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
public class QueryDatabaseAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler {

private final String sql;

public QueryDatabaseAuthenticationHandler(final String sql) {
this.sql = sql;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package org.apereo.cas.pm;

import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.CipherExecutor;
import org.apereo.cas.configuration.model.support.pm.PasswordManagementProperties;
import org.apereo.inspektr.common.web.ClientInfo;
import org.apereo.inspektr.common.web.ClientInfoHolder;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.NumericDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.UUID;

/**
* This is {@link BasePasswordManagementService}.
*
* @author Misagh Moayyed
* @since 5.1.0
*/
public abstract class BasePasswordManagementService implements PasswordManagementService {
private static final Logger LOGGER = LoggerFactory.getLogger(BasePasswordManagementService.class);

/**
* Password management settings.
*/
protected final PasswordManagementProperties passwordManagementProperties;

private final CipherExecutor<Serializable, String> cipherExecutor;
private final String issuer;


public BasePasswordManagementService(final CipherExecutor<Serializable, String> cipherExecutor,
final String issuer,
final PasswordManagementProperties passwordManagementProperties) {
this.cipherExecutor = cipherExecutor;
this.issuer = issuer;
this.passwordManagementProperties = passwordManagementProperties;
}

@Override
public String parseToken(final String token) {
try {
final String json = this.cipherExecutor.decode(token);
final JwtClaims claims = JwtClaims.parse(json);

if (!claims.getIssuer().equals(issuer)) {
LOGGER.error("Token issuer does not match CAS");
return null;
}
if (claims.getAudience().isEmpty() || !claims.getAudience().get(0).equals(issuer)) {
LOGGER.error("Token audience does not match CAS");
return null;
}
if (StringUtils.isBlank(claims.getSubject())) {
LOGGER.error("Token has no subject identifier");
return null;
}

final ClientInfo holder = ClientInfoHolder.getClientInfo();
if (!claims.getStringClaimValue("origin").equals(holder.getServerIpAddress())) {
LOGGER.error("Token origin does not match CAS");
return null;
}
if (!claims.getStringClaimValue("client").equals(holder.getClientIpAddress())) {
LOGGER.error("Token client does not match CAS");
return null;
}

if (claims.getExpirationTime().isBefore(NumericDate.now())) {
LOGGER.error("Token has expired.");
return null;
}

return claims.getSubject();
} catch (final Exception e) {
LOGGER.error(e.getMessage(), e);
}
return null;
}

@Override
public String createToken(final String to) {
try {
final String token = UUID.randomUUID().toString();
final JwtClaims claims = new JwtClaims();
claims.setJwtId(token);
claims.setIssuer(issuer);
claims.setAudience(issuer);
claims.setExpirationTimeMinutesInTheFuture(passwordManagementProperties.getReset().getExpirationMinutes());
claims.setIssuedAtToNow();

final ClientInfo holder = ClientInfoHolder.getClientInfo();
claims.setStringClaim("origin", holder.getServerIpAddress());
claims.setStringClaim("client", holder.getClientIpAddress());

claims.setSubject(to);
final String json = claims.toJson();
return this.cipherExecutor.encode(json);
} catch (final Exception e) {
LOGGER.error(e.getMessage(), e);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.apereo.cas.pm;

import org.apereo.cas.CipherExecutor;
import org.apereo.cas.authentication.Credential;
import org.apereo.cas.configuration.model.support.pm.PasswordManagementProperties;

import java.io.Serializable;
import java.util.Map;

/**
* This is {@link NoOpPasswordManagementService}.
*
* @author Misagh Moayyed
* @since 5.1.0
*/
public class NoOpPasswordManagementService extends BasePasswordManagementService {
public NoOpPasswordManagementService(final CipherExecutor<Serializable, String> cipherExecutor,
final String issuer,
final PasswordManagementProperties passwordManagementProperties) {
super(cipherExecutor, issuer, passwordManagementProperties);
}

@Override
public boolean change(final Credential c, final PasswordChangeBean bean) {
return false;
}

@Override
public String findEmail(final String username) {
return null;
}

@Override
public Map<String, String> getSecurityQuestions(final String username) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import org.apereo.cas.authentication.Credential;

import java.util.LinkedHashMap;
import java.util.Map;

/**
Expand All @@ -20,47 +19,37 @@ public interface PasswordManagementService {
* @param bean the bean
* @return true /false
*/
default boolean change(final Credential c, final PasswordChangeBean bean) {
return false;
}
boolean change(Credential c, PasswordChangeBean bean);

/**
* Find email associated with username.
*
* @param username the username
* @return the string
*/
default String findEmail(final String username) {
return null;
}
String findEmail(String username);

/**
* Create token string.
*
* @param username the username
* @return the string
*/
default String createToken(final String username) {
return null;
}
String createToken(String username);

/**
* Parse token string.
*
* @param token the token
* @return the username
*/
default String parseToken(final String token) {
return null;
}
String parseToken(String token);

/**
* Gets security questions.
*
* @param username the username
* @return the security questions
*/
default Map<String, String> getSecurityQuestions(final String username) {
return new LinkedHashMap<>();
}
Map<String, String> getSecurityQuestions(String username);
}
Loading

0 comments on commit 6fd8f31

Please sign in to comment.