001    package org.tynamo.security.federatedaccounts.facebook;
002    
003    import java.util.Collection;
004    import java.util.List;
005    
006    import org.apache.shiro.authc.AccountException;
007    import org.apache.shiro.authc.AuthenticationException;
008    import org.apache.shiro.authc.AuthenticationInfo;
009    import org.apache.shiro.authc.AuthenticationToken;
010    import org.apache.shiro.authc.IncorrectCredentialsException;
011    import org.apache.shiro.authz.AuthorizationException;
012    import org.apache.shiro.authz.Permission;
013    import org.apache.shiro.cache.MemoryConstrainedCacheManager;
014    import org.apache.shiro.realm.AuthenticatingRealm;
015    import org.apache.shiro.subject.PrincipalCollection;
016    import org.apache.tapestry5.ioc.annotations.Inject;
017    import org.apache.tapestry5.ioc.annotations.Symbol;
018    import org.slf4j.Logger;
019    import org.tynamo.security.federatedaccounts.FederatedAccount;
020    import org.tynamo.security.federatedaccounts.oauth.FacebookAccessToken;
021    import org.tynamo.security.federatedaccounts.services.FederatedAccountService;
022    
023    import com.restfb.DefaultFacebookClient;
024    import com.restfb.FacebookClient;
025    import com.restfb.exception.FacebookException;
026    import com.restfb.types.User;
027    
028    /**
029     * <p>
030     * A {@link org.apache.shiro.realm.Realm} that authenticates with Facebook.
031     */
032    public class FacebookRealm extends AuthenticatingRealm {
033            public static final String FACEBOOK_CLIENTID = "facebook.clientid";
034            public static final String FACEBOOK_CLIENTSECRET = "facebook.clientsecret";
035            public static final String FACEBOOK_PERMISSIONS = "facebook.permissions";
036            public static final String FACEBOOK_PRINCIPAL = "facebook.principal";
037    
038            private Logger logger;
039    
040            public static enum PrincipalProperty {
041                    id, email, name
042            }
043    
044            private PrincipalProperty principalProperty;
045    
046            private FederatedAccountService federatedAccountService;
047    
048            public FacebookRealm(Logger logger, FederatedAccountService federatedAccountService,
049                            @Inject @Symbol(FacebookRealm.FACEBOOK_PRINCIPAL) String principalPropertyName) {
050                    super(new MemoryConstrainedCacheManager());
051                    this.federatedAccountService = federatedAccountService;
052                    this.logger = logger;
053                    // Let this throw IllegalArgumentException if value is not supported
054                    this.principalProperty = PrincipalProperty.valueOf(principalPropertyName);
055                    setName(FederatedAccount.Type.facebook.name());
056                    setAuthenticationTokenClass(FacebookAccessToken.class);
057            }
058    
059            @Override
060            protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
061                    FacebookAccessToken token = (FacebookAccessToken) authenticationToken;
062    
063                    FacebookClient facebookClient = new DefaultFacebookClient(authenticationToken.getPrincipal().toString());
064                    User facebookUser;
065                    try {
066                            facebookUser = facebookClient.fetchObject("me", User.class);
067                    } catch (FacebookException e) {
068                            logger.error(e.getMessage(), e);
069                            throw new IncorrectCredentialsException("Facebook security verification failed, terminating authentication request", e);
070                    }
071                    // Null username is invalid, throw an exception if so - indicates that user hasn't granted the right
072                    // permissions (and/or we haven't asked for it)
073                    if (facebookUser == null) throw new AccountException("Null Facebook user is not allowed by this realm.");
074                    // long facebookUserId;
075                    // try {
076                    // facebookUserId = Long.valueOf(facebookUser.getId());
077                    // } catch (NumberFormatException e) {
078                    // logger.error("Facebook implementation has changed, returned id '" + facebookUser.getId() +
079                    // "' cannot be cast to Long");
080                    // throw new AccountException("Unknown user id format. Report this problem to support");
081                    // }
082    
083                    String principalValue = null;
084                    switch (principalProperty) {
085                            case id: principalValue = facebookUser.getId();
086                                    break;
087                            case email: principalValue = facebookUser.getEmail();
088                                    break;
089                            case name: principalValue = facebookUser.getName();
090                                    break;
091                    }
092    
093                    AuthenticationInfo authenticationInfo = federatedAccountService.federate(FederatedAccount.Type.facebook.name(), principalValue,
094                                    authenticationToken, facebookUser);
095                    // returned principalcollection is immutable
096                    // authenticationInfo.getPrincipals().fromRealm(FederatedAccount.Type.facebook.name()).add(authenticationToken);
097                    return authenticationInfo;
098                    // if (federatedAccount.isAccountLocked()) { throw new LockedAccountException("Facebook federated account ["
099                    // + federatedAccount.getUsername() + "] is locked."); }
100                    // if (federatedAccount.isCredentialsExpired()) {
101                    // String msg = "The credentials for account [" + facebookUser.getId() + "] are expired";
102                    // throw new ExpiredCredentialsException(msg);
103                    // }
104                    // return new SimpleAuthenticationInfo(federatedAccount.getUsername(), token.getCredentials(), getName());
105    
106                    // return federatedAccount;
107            }
108    
109            /**
110             * FIXME The following operations should all be removed - https://issues.apache.org/jira/browse/SHIRO-231 requires
111             * AuthenticatingRealm to implement Authorizer, which is wrong. Remove when upgrading Shiro dependency to 1.2
112             */
113            @Override
114            public boolean isPermitted(PrincipalCollection principals, String permission) {
115                    // TODO Auto-generated method stub
116                    return false;
117            }
118    
119            @Override
120            public boolean isPermitted(PrincipalCollection subjectPrincipal, Permission permission) {
121                    // TODO Auto-generated method stub
122                    return false;
123            }
124    
125            @Override
126            public boolean[] isPermitted(PrincipalCollection subjectPrincipal, String... permissions) {
127                    // TODO Auto-generated method stub
128                    return null;
129            }
130    
131            @Override
132            public boolean[] isPermitted(PrincipalCollection subjectPrincipal, List<Permission> permissions) {
133                    // TODO Auto-generated method stub
134                    return null;
135            }
136    
137            @Override
138            public boolean isPermittedAll(PrincipalCollection subjectPrincipal, String... permissions) {
139                    // TODO Auto-generated method stub
140                    return false;
141            }
142    
143            @Override
144            public boolean isPermittedAll(PrincipalCollection subjectPrincipal, Collection<Permission> permissions) {
145                    // TODO Auto-generated method stub
146                    return false;
147            }
148    
149            @Override
150            public void checkPermission(PrincipalCollection subjectPrincipal, String permission) throws AuthorizationException {
151                    // TODO Auto-generated method stub
152    
153            }
154    
155            @Override
156            public void checkPermission(PrincipalCollection subjectPrincipal, Permission permission) throws AuthorizationException {
157                    // TODO Auto-generated method stub
158    
159            }
160    
161            @Override
162            public void checkPermissions(PrincipalCollection subjectPrincipal, String... permissions) throws AuthorizationException {
163                    // TODO Auto-generated method stub
164    
165            }
166    
167            @Override
168            public void checkPermissions(PrincipalCollection subjectPrincipal, Collection<Permission> permissions) throws AuthorizationException {
169                    // TODO Auto-generated method stub
170    
171            }
172    
173            @Override
174            public boolean hasRole(PrincipalCollection subjectPrincipal, String roleIdentifier) {
175                    // TODO Auto-generated method stub
176                    return false;
177            }
178    
179            @Override
180            public boolean[] hasRoles(PrincipalCollection subjectPrincipal, List<String> roleIdentifiers) {
181                    // TODO Auto-generated method stub
182                    return null;
183            }
184    
185            @Override
186            public boolean hasAllRoles(PrincipalCollection subjectPrincipal, Collection<String> roleIdentifiers) {
187                    // TODO Auto-generated method stub
188                    return false;
189            }
190    
191            @Override
192            public void checkRole(PrincipalCollection subjectPrincipal, String roleIdentifier) throws AuthorizationException {
193                    // TODO Auto-generated method stub
194    
195            }
196    
197            @Override
198            public void checkRoles(PrincipalCollection subjectPrincipal, Collection<String> roleIdentifiers) throws AuthorizationException {
199                    // TODO Auto-generated method stub
200    
201            }
202    
203            @Override
204            public void checkRoles(PrincipalCollection subjectPrincipal, String... roleIdentifiers) throws AuthorizationException {
205                    // TODO Auto-generated method stub
206    
207            }
208    
209    }