View Javadoc
1   /**
2    * Waffle (https://github.com/dblock/waffle)
3    *
4    * Copyright (c) 2010 - 2015 Application Security, Inc.
5    *
6    * All rights reserved. This program and the accompanying materials
7    * are made available under the terms of the Eclipse Public License v1.0
8    * which accompanies this distribution, and is available at
9    * http://www.eclipse.org/legal/epl-v10.html
10   *
11   * Contributors:
12   *     Application Security, Inc.
13   */
14  package waffle.shiro;
15  
16  import org.apache.shiro.authc.AuthenticationException;
17  import org.apache.shiro.authc.AuthenticationInfo;
18  import org.apache.shiro.authc.AuthenticationToken;
19  import org.apache.shiro.authc.SimpleAuthenticationInfo;
20  import org.apache.shiro.authc.UsernamePasswordToken;
21  import org.apache.shiro.authc.credential.CredentialsMatcher;
22  import org.apache.shiro.authc.credential.HashingPasswordService;
23  import org.apache.shiro.authc.credential.PasswordMatcher;
24  import org.apache.shiro.authc.credential.PasswordService;
25  import org.apache.shiro.authz.AuthorizationInfo;
26  import org.apache.shiro.crypto.hash.Hash;
27  import org.apache.shiro.realm.AuthorizingRealm;
28  import org.apache.shiro.subject.PrincipalCollection;
29  import org.apache.shiro.util.ByteSource;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  import waffle.windows.auth.IWindowsAuthProvider;
34  import waffle.windows.auth.IWindowsIdentity;
35  import waffle.windows.auth.impl.WindowsAuthProviderImpl;
36  
37  /**
38   * A {@link org.apache.shiro.realm.Realm} that authenticates with Active Directory using WAFFLE. Authorization is left
39   * for subclasses to define by implementing the {@link #buildAuthorizationInfo} method.
40   */
41  public abstract class AbstractWaffleRealm extends AuthorizingRealm {
42      
43      /** The Constant LOGGER. */
44      private static final Logger  LOGGER     = LoggerFactory.getLogger(AbstractWaffleRealm.class);
45      
46      /** The Constant REALM_NAME. */
47      private static final String  REALM_NAME = "WAFFLE";
48  
49      /** The provider. */
50      private IWindowsAuthProvider provider   = new WindowsAuthProviderImpl();
51  
52      /* (non-Javadoc)
53       * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)
54       */
55      @Override
56      protected final AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken authToken) {
57          AuthenticationInfo authenticationInfo = null;
58          if (authToken instanceof UsernamePasswordToken) {
59              final UsernamePasswordToken token = (UsernamePasswordToken) authToken;
60              final String username = token.getUsername();
61              IWindowsIdentity identity = null;
62              try {
63                  AbstractWaffleRealm.LOGGER.debug("Attempting login for user {}", username);
64                  identity = this.provider.logonUser(username, new String(token.getPassword()));
65                  if (identity.isGuest()) {
66                      AbstractWaffleRealm.LOGGER.debug("Guest identity for user {}; denying access", username);
67                      throw new AuthenticationException("Guest identities are not allowed access");
68                  }
69                  final Object principal = new WaffleFqnPrincipal(identity);
70                  authenticationInfo = this.buildAuthenticationInfo(token, principal);
71                  AbstractWaffleRealm.LOGGER.debug("Successful login for user {}", username);
72              } catch (final RuntimeException e) {
73                  AbstractWaffleRealm.LOGGER.debug("Failed login for user {}: {}", username, e.getMessage());
74                  AbstractWaffleRealm.LOGGER.trace("{}", e);
75                  throw new AuthenticationException("Login failed", e);
76              } finally {
77                  if (identity != null) {
78                      identity.dispose();
79                  }
80              }
81          }
82          return authenticationInfo;
83      }
84  
85      /**
86       * Builds the authentication info.
87       *
88       * @param token
89       *            the token
90       * @param principal
91       *            the principal
92       * @return the authentication info
93       */
94      private AuthenticationInfo buildAuthenticationInfo(final UsernamePasswordToken token, final Object principal) {
95          AuthenticationInfo authenticationInfo;
96          final HashingPasswordService hashService = this.getHashService();
97          if (hashService != null) {
98              final Hash hash = hashService.hashPassword(token.getPassword());
99              final ByteSource salt = hash.getSalt();
100             authenticationInfo = new SimpleAuthenticationInfo(principal, hash, salt, AbstractWaffleRealm.REALM_NAME);
101         } else {
102             final Object creds = token.getCredentials();
103             authenticationInfo = new SimpleAuthenticationInfo(principal, creds, AbstractWaffleRealm.REALM_NAME);
104         }
105         return authenticationInfo;
106     }
107 
108     /* (non-Javadoc)
109      * @see org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)
110      */
111     @Override
112     protected final AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principals) {
113         final WaffleFqnPrincipal principal = principals.oneByType(WaffleFqnPrincipal.class);
114         return principal == null ? null : this.buildAuthorizationInfo(principal);
115     }
116 
117     /**
118      * Assembles the appropriate authorization information for the specified principal.
119      * 
120      * @param principal
121      *            the principal for which to assemble authorization information
122      * @return the authorization information for the specified principal
123      */
124     protected abstract AuthorizationInfo buildAuthorizationInfo(final WaffleFqnPrincipal principal);
125 
126     /**
127      * Allow overriding the default implementation of {@link IWindowsAuthProvider} This is only needed for testing,
128      * since for normal usage the default is what you want.
129      * 
130      * @param value
131      *            the windows authorization provider
132      */
133     void setProvider(final IWindowsAuthProvider value) {
134         this.provider = value;
135     }
136 
137     /**
138      * Gets the hash service.
139      *
140      * @return the hash service
141      */
142     private HashingPasswordService getHashService() {
143         final CredentialsMatcher matcher = this.getCredentialsMatcher();
144         if (matcher instanceof PasswordMatcher) {
145             final PasswordMatcher passwordMatcher = (PasswordMatcher) matcher;
146             final PasswordService passwordService = passwordMatcher.getPasswordService();
147             if (passwordService instanceof HashingPasswordService) {
148                 return (HashingPasswordService) passwordService;
149             }
150         }
151         return null;
152     }
153 }