001 package org.tynamo.security.federatedaccounts.services;
002
003 import java.util.Map;
004
005 import org.apache.shiro.authc.AuthenticationException;
006 import org.apache.shiro.authc.AuthenticationInfo;
007 import org.apache.shiro.authc.AuthenticationToken;
008 import org.apache.shiro.authc.ExpiredCredentialsException;
009 import org.apache.shiro.authc.LockedAccountException;
010 import org.apache.shiro.authc.SimpleAuthenticationInfo;
011 import org.apache.shiro.subject.SimplePrincipalCollection;
012 import org.hibernate.Session;
013 import org.hibernate.criterion.Restrictions;
014 import org.tynamo.security.federatedaccounts.FederatedAccount;
015
016 public class DefaultHibernateFederatedAccountServiceImpl implements FederatedAccountService {
017 private final Map<String, Object> entityTypesByRealm;
018 private final Class<?> singleEntityType;
019 private final Session session;
020
021 public DefaultHibernateFederatedAccountServiceImpl(Session session, Map<String, Object> entityTypesByRealm) {
022 this.entityTypesByRealm = entityTypesByRealm;
023 singleEntityType = entityTypesByRealm.containsKey("*") ? (Class<?>) entityTypesByRealm.get("*") : null;
024 this.session = session;
025
026 }
027
028 protected final Session getSession() {
029 return session;
030 }
031
032 protected final Map<String, Object> getEntityTypesByRealm() {
033 return entityTypesByRealm;
034 }
035
036 protected FederatedAccount createLocalAccount(Class<?> entityType, String realmName, Object remotePrincipal, Object remoteAccount) {
037 // You could also merge the account if there's already an existing user with the same email/username as
038 // in the remoteAccount
039 FederatedAccount localAccount = null;
040 try {
041 localAccount = (FederatedAccount) entityType.newInstance();
042 // InstantiationException, IllegalAccessException
043 } catch (Exception e) {
044 throw new AuthenticationException("Entity of type " + localAccount.getClass().getSimpleName() + " is not an instance of "
045 + FederatedAccount.class.getSimpleName(), e);
046 }
047 localAccount.federate(realmName, remotePrincipal, remoteAccount);
048 // new account, always save
049 session.save(localAccount);
050 return localAccount;
051 }
052
053 protected boolean updateLocalAccount(FederatedAccount localUser, String realmName, Object remotePrincipal, Object remoteAccount) {
054 boolean modified = localUser.federate(realmName, null, remoteAccount);
055 if (modified) session.update(localUser);
056 return modified;
057 }
058
059 FederatedAccount findLocalAccount(Class<?> entityType, String realmName, Object remotePrincipal, Object remoteAccount) {
060 return (FederatedAccount) session.createCriteria(entityType)
061 .add(Restrictions.eq((String) entityTypesByRealm.get(realmName + IDPROPERTY), remotePrincipal)).uniqueResult();
062 }
063
064 @Override
065 public AuthenticationInfo federate(String realmName, Object remotePrincipal, AuthenticationToken authenticationToken, Object remoteAccount) {
066 Class<?> entityType = singleEntityType == null ? (Class<?>) entityTypesByRealm.get(realmName) : singleEntityType;
067 if (entityType == null) return null;
068 FederatedAccount localAccount = findLocalAccount(entityType, realmName, remotePrincipal, remoteAccount);
069 if (localAccount == null) localAccount = createLocalAccount(entityType, realmName, remotePrincipal, remoteAccount);
070 else updateLocalAccount(localAccount, realmName, null, remoteAccount);
071 if (localAccount.isAccountLocked()) { throw new LockedAccountException("Federated account [" + remotePrincipal + "] is locked."); }
072 if (localAccount.isCredentialsExpired()) {
073 String msg = "The credentials for federated account [" + remotePrincipal + "] are expired";
074 throw new ExpiredCredentialsException(msg);
075 }
076 SimplePrincipalCollection principalCollection = new SimplePrincipalCollection(remotePrincipal, realmName);
077 principalCollection.add(authenticationToken, realmName);
078 return new SimpleAuthenticationInfo(principalCollection, authenticationToken.getCredentials());
079
080 // SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(remotePrincipal,
081 // authenticationToken.getCredentials(), realmName);
082 }
083 }