View Javadoc
1   /*
2    * MIT License
3    *
4    * Copyright (c) 2010-2022 The Waffle Project Contributors: https://github.com/Waffle/waffle/graphs/contributors
5    *
6    * Permission is hereby granted, free of charge, to any person obtaining a copy
7    * of this software and associated documentation files (the "Software"), to deal
8    * in the Software without restriction, including without limitation the rights
9    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10   * copies of the Software, and to permit persons to whom the Software is
11   * furnished to do so, subject to the following conditions:
12   *
13   * The above copyright notice and this permission notice shall be included in all
14   * copies or substantial portions of the Software.
15   *
16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22   * SOFTWARE.
23   */
24  package waffle.shiro;
25  
26  import org.apache.shiro.authc.AuthenticationException;
27  import org.apache.shiro.authc.AuthenticationInfo;
28  import org.apache.shiro.authc.AuthenticationToken;
29  import org.apache.shiro.authc.SimpleAuthenticationInfo;
30  import org.apache.shiro.authc.UsernamePasswordToken;
31  import org.apache.shiro.authc.credential.CredentialsMatcher;
32  import org.apache.shiro.authc.credential.HashingPasswordService;
33  import org.apache.shiro.authc.credential.PasswordMatcher;
34  import org.apache.shiro.authc.credential.PasswordService;
35  import org.apache.shiro.authz.AuthorizationInfo;
36  import org.apache.shiro.crypto.hash.Hash;
37  import org.apache.shiro.realm.AuthorizingRealm;
38  import org.apache.shiro.subject.PrincipalCollection;
39  import org.apache.shiro.util.ByteSource;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  
43  import waffle.windows.auth.IWindowsAuthProvider;
44  import waffle.windows.auth.IWindowsIdentity;
45  import waffle.windows.auth.impl.WindowsAuthProviderImpl;
46  
47  /**
48   * A {@link org.apache.shiro.realm.Realm} that authenticates with Active Directory using WAFFLE. Authorization is left
49   * for subclasses to define by implementing the {@link #buildAuthorizationInfo} method.
50   */
51  public abstract class AbstractWaffleRealm extends AuthorizingRealm {
52  
53      /** The Constant LOGGER. */
54      private static final Logger LOGGER = LoggerFactory.getLogger(AbstractWaffleRealm.class);
55  
56      /** The Constant REALM_NAME. */
57      private static final String REALM_NAME = "WAFFLE";
58  
59      /** The provider. */
60      private IWindowsAuthProvider provider = new WindowsAuthProviderImpl();
61  
62      @Override
63      protected final AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken authToken) {
64          AuthenticationInfo authenticationInfo = null;
65          if (authToken instanceof UsernamePasswordToken) {
66              final UsernamePasswordToken token = (UsernamePasswordToken) authToken;
67              final String username = token.getUsername();
68              IWindowsIdentity identity = null;
69              try {
70                  AbstractWaffleRealm.LOGGER.debug("Attempting login for user {}", username);
71                  identity = this.provider.logonUser(username, new String(token.getPassword()));
72                  if (identity.isGuest()) {
73                      AbstractWaffleRealm.LOGGER.debug("Guest identity for user {}; denying access", username);
74                      throw new AuthenticationException("Guest identities are not allowed access");
75                  }
76                  final Object principal = new WaffleFqnPrincipal(identity);
77                  authenticationInfo = this.buildAuthenticationInfo(token, principal);
78                  AbstractWaffleRealm.LOGGER.debug("Successful login for user {}", username);
79              } catch (final RuntimeException e) {
80                  AbstractWaffleRealm.LOGGER.debug("Failed login for user {}", username);
81                  throw new AuthenticationException("Login failed", e);
82              } finally {
83                  if (identity != null) {
84                      identity.dispose();
85                  }
86              }
87          }
88          return authenticationInfo;
89      }
90  
91      /**
92       * Builds the authentication info.
93       *
94       * @param token
95       *            the token
96       * @param principal
97       *            the principal
98       *
99       * @return the authentication info
100      */
101     private AuthenticationInfo buildAuthenticationInfo(final UsernamePasswordToken token, final Object principal) {
102         AuthenticationInfo authenticationInfo;
103         final HashingPasswordService hashService = this.getHashService();
104         if (hashService != null) {
105             final Hash hash = hashService.hashPassword(token.getPassword());
106             final ByteSource salt = hash.getSalt();
107             authenticationInfo = new SimpleAuthenticationInfo(principal, hash, salt, AbstractWaffleRealm.REALM_NAME);
108         } else {
109             final Object creds = token.getCredentials();
110             authenticationInfo = new SimpleAuthenticationInfo(principal, creds, AbstractWaffleRealm.REALM_NAME);
111         }
112         return authenticationInfo;
113     }
114 
115     @Override
116     protected final AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principals) {
117         final WaffleFqnPrincipal principal = principals.oneByType(WaffleFqnPrincipal.class);
118         return principal == null ? null : this.buildAuthorizationInfo(principal);
119     }
120 
121     /**
122      * Assembles the appropriate authorization information for the specified principal.
123      *
124      * @param principal
125      *            the principal for which to assemble authorization information
126      *
127      * @return the authorization information for the specified principal
128      */
129     protected abstract AuthorizationInfo buildAuthorizationInfo(final WaffleFqnPrincipal principal);
130 
131     /**
132      * Allow overriding the default implementation of {@link IWindowsAuthProvider} This is only needed for testing,
133      * since for normal usage the default is what you want.
134      *
135      * @param value
136      *            the windows authorization provider
137      */
138     void setProvider(final IWindowsAuthProvider value) {
139         this.provider = value;
140     }
141 
142     /**
143      * Gets the hash service.
144      *
145      * @return the hash service
146      */
147     private HashingPasswordService getHashService() {
148         final CredentialsMatcher matcher = this.getCredentialsMatcher();
149         if (matcher instanceof PasswordMatcher) {
150             final PasswordMatcher passwordMatcher = (PasswordMatcher) matcher;
151             final PasswordService passwordService = passwordMatcher.getPasswordService();
152             if (passwordService instanceof HashingPasswordService) {
153                 return (HashingPasswordService) passwordService;
154             }
155         }
156         return null;
157     }
158 
159 }