1 /*
2  * JBoss, Home of Professional Open Source.
3  * Copyright 2014 Red Hat, Inc., and individual contributors
4  * as indicated by the @author tags.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * 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, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */

18
19 package io.undertow.security.api;
20
21 import io.undertow.server.HttpServerExchange;
22
23 /**
24  * The interface to be implemented by a single authentication mechanism.
25  * <p>
26  * The implementation of this interface are assumed to be stateless, if there is a need to share state between the authenticate
27  * and handleComplete calls then it should be held in the HttpServerExchange.
28  * <p>
29  * As an in-bound request is received the authenticate method is called on each mechanism in turn until one of the following
30  * occurs: - - A mechanism successfully authenticates the incoming request. - A mechanism attempts but fails to authenticate the
31  * request. - The list of mechanisms is exhausted.
32  * <p>
33  * This means that if the authenticate method is called on a mechanism it should assume it is required to check if it can
34  * actually authenticate the incoming request, anything that would prevent it from performing the check would have already
35  * stopped the authenticate method from being called.
36  * <p>
37  * Authentication is allowed to proceed if either authentication was required AND one handler authenticated the request or it is
38  * allowed to proceed if it is not required AND no handler failed to authenticate the request.
39  * <p>
40  * The handleComplete methods are used as the request processing is returning up the chain, primarily these are used to
41  * challenge the client to authenticate but where supported by the mechanism they could also be used to send mechanism specific
42  * updates back with a request.
43  * <p>
44  * If a mechanism successfully authenticated the incoming request then only the handleComplete method on that mechanism is
45  * called.
46  * <p>
47  * If any mechanism failed or if authentication was required and no mechanism succeeded in authenticating the request then
48  * handleComplete will be called for all mechanisms.
49  * <p>
50  * Finally if authentication was not required handleComplete will not be called for any of the mechanisms.
51  * <p>
52  * The mechanisms will need to double check why handleComplete is being called, if the request was authenticated then they
53  * should do nothing unless the mechanism has intermediate state to send back. If the request was not authenticated then a
54  * challenge should be sent.
55  *
56  * @author Stuart Douglas
57  * @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
58  */

59 public interface AuthenticationMechanism {
60
61     /**
62      * Perform authentication of the request. Any potentially blocking work should be performed in the handoff executor provided
63      *
64      * @param exchange The exchange
65      * @return
66      */

67     AuthenticationMechanismOutcome authenticate(final HttpServerExchange exchange,
68                                                 final SecurityContext securityContext);
69
70     /**
71      * Send an authentication challenge to the remote client.
72      * <p>
73      * The individual mechanisms should update the response headers and body of the message as appropriate however they should
74      * not set the response code, instead that should be indicated in the {@link ChallengeResult} and the most appropriate
75      * overall response code will be selected.
76      *
77      * This method should not return <code>null</code>.
78      *
79      * @param exchange        The exchange
80      * @param securityContext The security context
81      * @return A {@link ChallengeResult} indicating if a challenge was sent and the desired response code.
82      */

83     ChallengeResult sendChallenge(final HttpServerExchange exchange, final SecurityContext securityContext);
84
85     /**
86      * The AuthenticationOutcome is used by an AuthenticationMechanism to indicate the outcome of the call to authenticate, the
87      * overall authentication process will then used this along with the current AuthenticationState to decide how to proceed
88      * with the current request.
89      */

90     enum AuthenticationMechanismOutcome {
91         /**
92          * Based on the current request the mechanism has successfully performed authentication.
93          */

94         AUTHENTICATED,
95
96         /**
97          * The mechanism did not attempt authentication on this request, most likely due to not discovering any applicable
98          * security tokens for this mechanisms in the request.
99          */

100         NOT_ATTEMPTED,
101
102         /**
103          * The mechanism attempted authentication but it did not complete, this could either be due to a failure validating the
104          * tokens from the client or it could be due to the mechanism requiring at least one additional round trip with the
105          * client - either way the request will return challenges to the client.
106          */

107         NOT_AUTHENTICATED;
108     }
109
110     /**
111      * Simple class to wrap the result of requesting a mechanism sends it's challenge.
112      */

113     class ChallengeResult {
114
115         public static final ChallengeResult NOT_SENT = new ChallengeResult(false);
116
117         private final boolean challengeSent;
118         private final Integer statusCode;
119
120         public ChallengeResult(final boolean challengeSent, final Integer statusCode) {
121             this.statusCode = statusCode;
122             this.challengeSent = challengeSent;
123         }
124
125         public ChallengeResult(final boolean challengeSent) {
126             this(challengeSent, null);
127         }
128
129         /**
130          * Obtain the response code desired by this mechanism for the challenge.
131          * <p>
132          * Where multiple mechanisms are in use concurrently all of the requested response codes will be checked and the most
133          * suitable one selected. If no specific response code is required any value less than 0 can be set.
134          *
135          * @return The desired response code or null if no code specified.
136          */

137         public Integer getDesiredResponseCode() {
138             return statusCode;
139         }
140
141         /**
142          * Check if the mechanism did send a challenge.
143          * <p>
144          * Some mechanisms do not send a challenge and just rely on the correct information to authenticate a user being
145          * available in the request, in that case it would be normal for the mechanism to set this to false.
146          *
147          * @return true if a challenge was sent, false otherwise.
148          */

149         public boolean isChallengeSent() {
150             return challengeSent;
151         }
152
153     }
154
155 }
156