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.impl;
20
21 import io.undertow.UndertowMessages;
22 import io.undertow.security.api.AuthenticationMechanism;
23 import io.undertow.security.api.AuthenticationMechanismFactory;
24 import io.undertow.security.api.SecurityContext;
25 import io.undertow.security.idm.Account;
26 import io.undertow.security.idm.IdentityManager;
27 import io.undertow.security.idm.PasswordCredential;
28 import io.undertow.server.HttpServerExchange;
29 import io.undertow.server.handlers.Cookie;
30 import io.undertow.server.handlers.form.FormParserFactory;
31 import io.undertow.util.HttpString;
32
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.Map;
36
37 import static io.undertow.security.api.AuthenticationMechanism.AuthenticationMechanismOutcome.AUTHENTICATED;
38 import static io.undertow.security.api.AuthenticationMechanism.AuthenticationMechanismOutcome.NOT_ATTEMPTED;
39 import static io.undertow.security.api.AuthenticationMechanism.AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
40
41 /**
42  * A authentication mechanism that requires the presence of two headers in the request. One of these will be used as a
43  * principal and the other as a password credential.
44  *
45  * @author Stuart Douglas
46  */

47 public class GenericHeaderAuthenticationMechanism implements AuthenticationMechanism {
48
49     public static final AuthenticationMechanismFactory FACTORY = new Factory();
50
51     public static final String NAME = "GENERIC_HEADER";
52     public static final String IDENTITY_HEADER = "identity-header";
53     public static final String SESSION_HEADER = "session-header";
54
55     private final String mechanismName;
56     private final List<HttpString> identityHeaders;
57     private final List<String> sessionCookieNames;
58     private final IdentityManager identityManager;
59
60     public GenericHeaderAuthenticationMechanism(String mechanismName, List<HttpString> identityHeaders, List<String> sessionCookieNames, IdentityManager identityManager) {
61         this.mechanismName = mechanismName;
62         this.identityHeaders = identityHeaders;
63         this.sessionCookieNames = sessionCookieNames;
64         this.identityManager = identityManager;
65     }
66
67     @Override
68     public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
69         String principal = getPrincipal(exchange);
70         if(principal == null) {
71             return NOT_ATTEMPTED;
72         }
73         String session = getSession(exchange);
74         if(session == null) {
75             return NOT_ATTEMPTED;
76         }
77         Account account = identityManager.verify(principal, new PasswordCredential(session.toCharArray()));
78         if(account == null) {
79             securityContext.authenticationFailed(UndertowMessages.MESSAGES.authenticationFailed(principal), mechanismName);
80             return NOT_AUTHENTICATED;
81         }
82         securityContext.authenticationComplete(account, mechanismName, false);
83         return AUTHENTICATED;
84     }
85
86     private String getSession(HttpServerExchange exchange) {
87         for(String header : sessionCookieNames) {
88             Cookie cookie = exchange.getRequestCookies().get(header);
89             if(cookie != null) {
90                 return cookie.getValue();
91             }
92         }
93         return null;
94     }
95
96     private String getPrincipal(HttpServerExchange exchange) {
97         for(HttpString header : identityHeaders) {
98             String res = exchange.getRequestHeaders().getFirst(header);
99             if(res != null) {
100                 return res;
101             }
102         }
103         return null;
104     }
105
106     @Override
107     public ChallengeResult sendChallenge(HttpServerExchange exchange, SecurityContext securityContext) {
108         return ChallengeResult.NOT_SENT;
109     }
110
111
112     public static class Factory implements AuthenticationMechanismFactory {
113
114         @Deprecated
115         public Factory(IdentityManager identityManager) {
116         }
117
118         public Factory() {
119
120         }
121
122         @Override
123         public AuthenticationMechanism create(String mechanismName, IdentityManager identityManager, FormParserFactory formParserFactory, Map<String, String> properties) {
124             String identity = properties.get(IDENTITY_HEADER);
125             if(identity == null) {
126                 throw UndertowMessages.MESSAGES.authenticationPropertyNotSet(mechanismName, IDENTITY_HEADER);
127             }
128             String session = properties.get(SESSION_HEADER);
129             if(session == null) {
130                 throw UndertowMessages.MESSAGES.authenticationPropertyNotSet(mechanismName, SESSION_HEADER);
131             }
132             List<HttpString> ids = new ArrayList<>();
133             for(String s : identity.split(",")) {
134                 ids.add(new HttpString(s));
135             }
136             List<String> sessions = new ArrayList<>();
137             for(String s : session.split(",")) {
138                 sessions.add(s);
139             }
140             return new GenericHeaderAuthenticationMechanism(mechanismName, ids, sessions, identityManager);
141         }
142     }
143 }
144