package org.atricore.idbus.capabilities.atricoreid.as.main.emitter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.atricore.idbus.capabilities.atricoreid.common.*; import org.atricore.idbus.capabilities.atricoreid.common.util.JasonUtils; import org.atricore.idbus.capabilities.sts.main.AbstractSecurityTokenEmitter; import org.atricore.idbus.capabilities.sts.main.SecurityTokenEmissionException; import org.atricore.idbus.capabilities.sts.main.SecurityTokenProcessingContext; import org.atricore.idbus.capabilities.sts.main.WSTConstants; import org.atricore.idbus.capabilities.atricoreid._1_0.protocol.AtricoreIDAccessTokenType; import org.atricore.idbus.capabilities.atricoreid._1_0.protocol.ObjectFactory; import org.atricore.idbus.kernel.main.authn.*; import org.atricore.idbus.kernel.main.store.SSOIdentityManager; import org.atricore.idbus.kernel.planning.IdentityArtifact; import javax.security.auth.Subject; import java.io.IOException; import java.security.Principal; import java.util.Arrays; import java.util.HashSet; import java.util.Random; import java.util.Set; /** * @author <a href=mailto:sgonzalez@atricore.org>Sebastian Gonzalez Oyuela</a> */ public class AtricoreIDAccessTokenEmitter extends AbstractSecurityTokenEmitter { private static final Log logger = LogFactory.getLog(AtricoreIDAccessTokenEmitter.class); private SSOIdentityManager identityManager; private TokenSigner tokenSigner; private TokenEncrypter tokenEncrypter; private Random randomGenerator = new Random(); @Override public boolean canEmit(SecurityTokenProcessingContext context, Object requestToken, String tokenType) { // We can emit for any context with a valid subject when Token Type is SAMLR2! return context.getProperty(WSTConstants.SUBJECT_PROP) != null && WSTConstants.WST_OAUTH2_TOKEN_TYPE.equals(tokenType); } @Override public SecurityToken emit(SecurityTokenProcessingContext context, Object requestToken, String tokenType) throws SecurityTokenEmissionException { try { AtricoreIDSecurityTokenEmissionContext atricoreidEmissionCtx = (AtricoreIDSecurityTokenEmissionContext) context.getProperty(WSTConstants.RST_CTX); Subject subject = (Subject) context.getProperty(WSTConstants.SUBJECT_PROP); // Resolve subject, using configured identity source subject = resolveSubject(subject); if (logger.isTraceEnabled()) logger.trace("Building AtricoreID Token from " + subject); // Build an access token for the subject, AtricoreIDAccessToken token = buildAtricoreIDAccessToken(subject); AtricoreIDAccessTokenEnvelope envelope = buildAtricoreIDAccessTokenEnvelope(token); String tokenValue = marshalAtricoreIDAccessEvnelopeToken(envelope); String uuid = super.uuidGenerator.generateId(); ObjectFactory of = new ObjectFactory(); AtricoreIDAccessTokenType oauthToken = of.createAtricoreIDAccessTokenType(); oauthToken.setTokenType("bearer"); oauthToken.setAccessToken(tokenValue); // Ten minutes, make configurable! oauthToken.setExpiresIn(1000L * 60L * 10L); // Create a security token using the OUT artifact content. SecurityToken st = new SecurityTokenImpl(uuid, oauthToken); logger.debug("Created new security token [" + uuid + "] with content " + (oauthToken.getClass().getSimpleName())); return st; } catch (IOException e) { throw new SecurityTokenEmissionException(e); } catch (AtricoreIDSignatureException e) { throw new SecurityTokenEmissionException(e); } catch (AtricoreIDEncryptionException e) { throw new SecurityTokenEmissionException(e); } } protected AtricoreIDAccessTokenEnvelope buildAtricoreIDAccessTokenEnvelope(AtricoreIDAccessToken token) throws IOException, AtricoreIDEncryptionException, AtricoreIDSignatureException { // Build and deflate String tokenValue = JasonUtils.marshalAccessToken(token); tokenValue = JasonUtils.deflate(tokenValue, true); // Encrypt String encryptAlg = null; if (tokenEncrypter != null) { tokenValue = tokenEncrypter.encrypt(tokenValue); encryptAlg = tokenEncrypter.getEncryptAlg(); } // Sign String sigValue = null; String sigAlg = null; if (tokenSigner != null) { sigValue = tokenSigner.signToken(tokenValue); sigAlg = tokenSigner.getSignAlg(); } return new AtricoreIDAccessTokenEnvelope (encryptAlg, sigAlg, sigValue, tokenValue, true); } protected AtricoreIDAccessToken buildAtricoreIDAccessToken(Subject subject) { // User Set<SSOUser> ssoUsers = subject.getPrincipals(SSOUser.class); assert ssoUsers.size() == 1; AtricoreIDAccessToken at = new AtricoreIDAccessToken(); SSOUser user = ssoUsers.iterator().next(); at.getClaims().add(new AtricoreIDClaim(AtricoreIDClaimType.USERID.toString(), user.getName())); // Just a temporary work-around. at.getClaims().add(new AtricoreIDClaim(AtricoreIDClaimType.UNKNOWN.toString(), "UNKNOWN")); // Roles Set<SSORole> ssoRoles = subject.getPrincipals(SSORole.class); for (SSORole ssoRole : ssoRoles) { at.getClaims().add(new AtricoreIDClaim(AtricoreIDClaimType.ROLE.toString(), ssoRole.getName())); } // Create some randon information, to make every token different! at.setTimeStamp(System.currentTimeMillis()); // is this thread-safe ?! at.setRnd(randomGenerator.nextInt()); return at; } /** * TODO : Use identity planning, and reuse some STS actions! * * @param subject * @return */ protected Subject resolveSubject(Subject subject) { AtricoreIDAccessToken at = new AtricoreIDAccessToken(); Set<SSOUser> ssoUsers = subject.getPrincipals(SSOUser.class); Set<SimplePrincipal> simplePrincipals = subject.getPrincipals(SimplePrincipal.class); if (ssoUsers != null && ssoUsers.size() > 0) { if (logger.isDebugEnabled()) logger.debug("Emitting token for Subject with SSOUser"); // Build Subject // s = new Subject(true, s.getPrincipals(), s.getPrivateCredentials(), s.getPublicCredentials()); } else { try { // Resolve SSOUser SimplePrincipal sp = simplePrincipals.iterator().next(); String username = sp.getName(); SSOIdentityManager idMgr = getIdentityManager(); if (idMgr == null) throw new IllegalStateException("SSOIdentityManager not configured for plan " + getClass().getSimpleName()); if (logger.isTraceEnabled()) logger.trace("Resolving SSOUser for " + username); Set<Principal> principals = new HashSet<Principal>(); // Find SSOUser principal SSOUser ssoUser = idMgr.findUser(username); principals.add(ssoUser); // Find SSORole principals SSORole[] ssoRoles = idMgr.findRolesByUsername(username); principals.addAll(Arrays.asList(ssoRoles)); // Use existing SSOPolicyEnforcement principals Set<SSOPolicyEnforcementStatement> ssoPolicies = subject.getPrincipals(SSOPolicyEnforcementStatement.class); if (ssoPolicies != null) { if (logger.isDebugEnabled()) logger.debug("Adding " + ssoPolicies.size() + " SSOPolicyEnforcement principals "); principals.addAll(ssoPolicies); } // Build Subject subject = new Subject(true, principals, subject.getPublicCredentials(), subject.getPrivateCredentials()); } catch (Exception e) { throw new SecurityTokenEmissionException(e); } } return subject; } private String marshalAtricoreIDAccessEvnelopeToken(AtricoreIDAccessTokenEnvelope envelope) throws IOException { return JasonUtils.marshalAccessTokenEnvelope(envelope, true); } @Override protected IdentityArtifact createOutArtifact(Object requestToken, String tokenType) { throw new UnsupportedOperationException("Operatino not available"); } public SSOIdentityManager getIdentityManager() { return identityManager; } public void setIdentityManager(SSOIdentityManager identityManager) { this.identityManager = identityManager; } public TokenSigner getTokenSigner() { return tokenSigner; } public void setTokenSigner(TokenSigner tokenSigner) { this.tokenSigner = tokenSigner; } public TokenEncrypter getTokenEncrypter() { return tokenEncrypter; } public void setTokenEncrypter(TokenEncrypter tokenEncrypter) { this.tokenEncrypter = tokenEncrypter; } }