1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 module hunt.shiro.authc.pam.AllSuccessfulStrategy;
20 
21 import hunt.shiro.authc.pam.AbstractAuthenticationStrategy;
22 
23 import hunt.logging.Logger;
24 
25 import hunt.shiro.Exceptions;
26 import hunt.shiro.authc.AuthenticationInfo;
27 import hunt.shiro.authc.AuthenticationToken;
28 import hunt.shiro.Exceptions;
29 import hunt.shiro.realm.Realm;
30 
31 
32 /**
33  * <tt>AuthenticationStrategy</tt> implementation that requires <em>all</em> configured realms to
34  * <b>successfully</b> process the submitted <tt>AuthenticationToken</tt> during the log-in attempt.
35  * <p/>
36  * <p>If one or more realms do not support the submitted token, or one or more are unable to acquire
37  * <tt>AuthenticationInfo</tt> for the token, this implementation will immediately fail the log-in attempt for the
38  * associated subject (user).
39  *
40  */
41 class AllSuccessfulStrategy : AbstractAuthenticationStrategy {
42 
43     /** Private class log instance. */
44 
45 
46     /**
47      * Because all realms in this strategy must complete successfully, this implementation ensures that the given
48      * <code>Realm</code> {@link hunt.shiro.realm.Realm#supports(hunt.shiro.authc.AuthenticationToken) supports} the given
49      * <code>token</code> argument.  If it does not, this method
50      * {@link UnsupportedTokenException UnsupportedTokenException} to end the authentication
51      * process immediately. If the realm does support the token, the <code>info</code> argument is returned immediately.
52      */
53     override AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo info){
54         if (!realm.supports(token)) {
55             string msg = "Realm [" ~ (cast(Object)realm).toString() ~ "] of type [" ~ 
56                     typeid(realm).name ~ "] does not support " ~
57                     " the submitted AuthenticationToken [" ~ (cast(Object)token).toString() ~ 
58                     "].  The [" ~ typeid(this).name ~
59                     "] implementation requires all configured realm(s) to support " ~
60                     "and be able to process the submitted AuthenticationToken.";
61             throw new UnsupportedTokenException(msg);
62         }
63 
64         return info;
65     }
66 
67     /**
68      * Merges the specified <code>info</code> into the <code>aggregate</code> argument and returns it (just as the
69      * parent implementation does), but additionally ensures the following:
70      * <ol>
71      * <li>if the <code>Throwable</code> argument is not <code>null</code>, re-throws it to immediately cancel the
72      * authentication process, since this strategy requires all realms to authenticate successfully.</li>
73      * <li>neither the <code>info</code> or <code>aggregate</code> argument is <code>null</code> to ensure that each
74      * realm did in fact authenticate successfully</li>
75      * </ol>
76      */
77     override AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, 
78         AuthenticationInfo info, AuthenticationInfo aggregate, Throwable t){
79         if (t !is null) {
80             auto tCast = cast(AuthenticationException) t;
81             if (tCast !is null) {
82                 //propagate:
83                 throw (tCast);
84             } else {
85                 string msg = "Unable to acquire account data from realm [" ~ (cast(Object)realm).toString() ~ 
86                         "].  The [" ~ typeid(this).name ~ 
87                         " implementation requires all configured realm(s) to operate successfully " ~
88                         "for a successful authentication.";
89                 throw new AuthenticationException(msg, t);
90             }
91         }
92         if (info  is null) {
93             string msg = "Realm [" ~ (cast(Object)realm).toString() ~ 
94                     "] could not find any associated account data for the submitted " ~
95                     "AuthenticationToken [" ~ (cast(Object)token).toString() ~ 
96                     "].  The [AllSuccessfulStrategy] implementation requires " ~
97                     "all configured realm(s) to acquire valid account data for a submitted token during the " ~
98                     "log-in process.";
99             throw new UnknownAccountException(msg);
100         }
101 
102         tracef("Account successfully authenticated using realm [%s]", realm);
103 
104         // If non-null account is returned, then the realm was able to authenticate the
105         // user - so merge the account with any accumulated before:
106         merge(info, aggregate);
107 
108         return aggregate;
109     }
110 }