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; } }