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    }