Added AAF plugin
[dmaap/kafka11aaf.git] / src / main / java / org / onap / dmaap / kafkaAuthorize / PlainSaslServer1.java
1 /*******************************************************************************
2  *  ============LICENSE_START=======================================================
3  *  org.onap.dmaap
4  *  ================================================================================
5  *  Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6  *  ================================================================================
7  *  Licensed under the Apache License, Version 2.0 (the "License");
8  *  you may not use this file except in compliance with the License.
9  *  You may obtain a copy of the License at
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  *  ============LICENSE_END=========================================================
18  *  
19  *  
20  *******************************************************************************/
21 package org.onap.dmaap.kafkaAuthorize;
22
23 import java.io.UnsupportedEncodingException;
24 import java.util.Arrays;
25 import java.util.Map;
26
27 import javax.security.auth.callback.CallbackHandler;
28 import javax.security.sasl.Sasl;
29 import javax.security.sasl.SaslException;
30 import javax.security.sasl.SaslServer;
31 import javax.security.sasl.SaslServerFactory;
32
33 import org.apache.kafka.common.security.JaasContext;
34 import org.apache.kafka.common.security.authenticator.SaslServerCallbackHandler;
35
36 import org.onap.dmaap.commonauth.kafka.base.authorization.AuthorizationProviderFactory;
37
38 /**
39  * Simple SaslServer implementation for SASL/PLAIN. In order to make this
40  * implementation fully pluggable, authentication of username/password is fully
41  * contained within the server implementation.
42  * <p>
43  * Valid users with passwords are specified in the Jaas configuration file. Each
44  * user is specified with user_<username> as key and <password> as value. This
45  * is consistent with Zookeeper Digest-MD5 implementation.
46  * <p>
47  * To avoid storing clear passwords on disk or to integrate with external
48  * authentication servers in production systems, this module can be replaced
49  * with a different implementation.
50  *
51  */
52 public class PlainSaslServer1 implements SaslServer {
53
54         public static final String PLAIN_MECHANISM = "PLAIN";
55
56         private final JaasContext jaasContext;
57
58         private boolean complete;
59         private String authorizationID;
60
61         public PlainSaslServer1(JaasContext jaasContext) {
62                 this.jaasContext = jaasContext;
63         }
64
65         @Override
66         public byte[] evaluateResponse(byte[] response) throws SaslException {
67                 /*
68                  * Message format (from https://tools.ietf.org/html/rfc4616):
69                  *
70                  * message = [authzid] UTF8NUL authcid UTF8NUL passwd authcid = 1*SAFE ;
71                  * MUST accept up to 255 octets authzid = 1*SAFE ; MUST accept up to 255
72                  * octets passwd = 1*SAFE ; MUST accept up to 255 octets UTF8NUL = %x00
73                  * ; UTF-8 encoded NUL character
74                  *
75                  * SAFE = UTF1 / UTF2 / UTF3 / UTF4 ;; any UTF-8 encoded Unicode
76                  * character except NUL
77                  */
78
79                 String[] tokens;
80                 try {
81                         tokens = new String(response, "UTF-8").split("\u0000");
82                 } catch (UnsupportedEncodingException e) {
83                         throw new SaslException("UTF-8 encoding not supported", e);
84                 }
85                 if (tokens.length != 3)
86                         throw new SaslException("Invalid SASL/PLAIN response: expected 3 tokens, got " + tokens.length);
87                 authorizationID = tokens[0];
88                 String username = tokens[1];
89                 String password = tokens[2];
90
91                 if (username.isEmpty()) {
92                         throw new SaslException("Authentication failed: username not specified");
93                 }
94                 if (password.isEmpty()) {
95                         throw new SaslException("Authentication failed: password not specified");
96                 }
97                 if (authorizationID.isEmpty())
98                         authorizationID = username;
99
100                 String aafResponse = "Not Verified";
101                 try {
102                         aafResponse = AuthorizationProviderFactory.getProviderFactory().getProvider().authenticate(username,
103                                         password);
104                 } catch (Exception e) {
105                 }
106
107                 if (null != aafResponse) {
108                         throw new SaslException("Authentication failed: " + aafResponse + " User " + username);
109                 }
110
111                 complete = true;
112                 return new byte[0];
113         }
114
115         @Override
116         public String getAuthorizationID() {
117                 if (!complete)
118                         throw new IllegalStateException("Authentication exchange has not completed");
119                 return authorizationID;
120         }
121
122         @Override
123         public String getMechanismName() {
124                 return PLAIN_MECHANISM;
125         }
126
127         @Override
128         public Object getNegotiatedProperty(String propName) {
129                 if (!complete)
130                         throw new IllegalStateException("Authentication exchange has not completed");
131                 return null;
132         }
133
134         @Override
135         public boolean isComplete() {
136                 return complete;
137         }
138
139         @Override
140         public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException {
141                 if (!complete)
142                         throw new IllegalStateException("Authentication exchange has not completed");
143                 return Arrays.copyOfRange(incoming, offset, offset + len);
144         }
145
146         @Override
147         public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException {
148                 if (!complete)
149                         throw new IllegalStateException("Authentication exchange has not completed");
150                 return Arrays.copyOfRange(outgoing, offset, offset + len);
151         }
152
153         @Override
154         public void dispose() throws SaslException {
155         }
156
157         public static class PlainSaslServerFactory1 implements SaslServerFactory {
158
159                 @Override
160                 public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map<String, ?> props,
161                                 CallbackHandler cbh) throws SaslException {
162
163                         if (!PLAIN_MECHANISM.equals(mechanism))
164                                 throw new SaslException(
165                                                 String.format("Mechanism \'%s\' is not supported. Only PLAIN is supported.", mechanism));
166
167                         if (!(cbh instanceof SaslServerCallbackHandler))
168                                 throw new SaslException(
169                                                 "CallbackHandler must be of type SaslServerCallbackHandler, but it is: " + cbh.getClass());
170
171                         return new PlainSaslServer1(((SaslServerCallbackHandler) cbh).jaasContext());
172                 }
173
174                 @Override
175                 public String[] getMechanismNames(Map<String, ?> props) {
176                         if (props == null)
177                                 return new String[] { PLAIN_MECHANISM };
178                         String noPlainText = (String) props.get(Sasl.POLICY_NOPLAINTEXT);
179                         if ("true".equals(noPlainText))
180                                 return new String[] {};
181                         else
182                                 return new String[] { PLAIN_MECHANISM };
183                 }
184         }
185 }