3d863824c581bdd512d4a9e743036e0eed3e18d7
[aaf/authz.git] / auth / auth-oauth / src / main / java / org / onap / aaf / auth / oauth / DirectOAuthTAF.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 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  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 package org.onap.aaf.auth.oauth;
23
24 import java.io.IOException;
25 import java.security.GeneralSecurityException;
26 import java.security.NoSuchAlgorithmException;
27 import java.util.Map;
28
29 import javax.servlet.http.HttpServletRequest;
30 import javax.servlet.http.HttpServletResponse;
31
32 import org.onap.aaf.auth.dao.hl.Question;
33 import org.onap.aaf.auth.direct.DirectAAFUserPass;
34 import org.onap.aaf.auth.env.AuthzEnv;
35 import org.onap.aaf.auth.env.AuthzTrans;
36 import org.onap.aaf.auth.layer.Result;
37 import org.onap.aaf.auth.oauth.facade.DirectIntrospect;
38 import org.onap.aaf.auth.rserv.TransFilter;
39 import org.onap.aaf.cadi.CachedPrincipal;
40 import org.onap.aaf.cadi.CachedPrincipal.Resp;
41 import org.onap.aaf.cadi.CadiException;
42 import org.onap.aaf.cadi.CredVal.Type;
43 import org.onap.aaf.cadi.Hash;
44 import org.onap.aaf.cadi.LocatorException;
45 import org.onap.aaf.cadi.PropAccess;
46 import org.onap.aaf.cadi.Taf.LifeForm;
47 import org.onap.aaf.cadi.config.Config;
48 import org.onap.aaf.cadi.oauth.OAuth2HttpTafResp;
49 import org.onap.aaf.cadi.oauth.OAuth2Principal;
50 import org.onap.aaf.cadi.oauth.TokenClient;
51 import org.onap.aaf.cadi.oauth.TokenClientFactory;
52 import org.onap.aaf.cadi.oauth.TokenMgr;
53 import org.onap.aaf.cadi.oauth.TokenMgr.TokenPermLoader;
54 import org.onap.aaf.cadi.oauth.TokenPerm;
55 import org.onap.aaf.cadi.principal.OAuth2FormPrincipal;
56 import org.onap.aaf.cadi.taf.HttpTaf;
57 import org.onap.aaf.cadi.taf.TafResp;
58 import org.onap.aaf.cadi.taf.TafResp.RESP;
59 import org.onap.aaf.cadi.util.Split;
60 import org.onap.aaf.misc.env.APIException;
61
62 import aafoauth.v2_0.Introspect;
63
64 public class DirectOAuthTAF implements HttpTaf {
65     private PropAccess access;
66     private DirectIntrospect<Introspect> oaFacade;
67     private TokenMgr tkMgr;
68     private final DirectAAFUserPass directUserPass;
69     private TokenClient altIntrospectClient;
70
71     public DirectOAuthTAF(AuthzEnv env, Question q,  DirectIntrospect<Introspect> facade) throws APIException, CadiException {
72         access = env.access();
73         oaFacade = facade;
74         tkMgr = TokenMgr.getInstance(access,"dbToken","dbIntrospect");
75         String alt_url = access.getProperty(Config.AAF_ALT_OAUTH2_INTROSPECT_URL,null);
76         TokenClientFactory tcf;
77         if (alt_url!=null) {
78             try {
79                 tcf = TokenClientFactory.instance(access);
80                 String[] split = Split.split(',', alt_url);
81                 int timeout = split.length>1?Integer.parseInt(split[1]):3000;
82                 altIntrospectClient = tcf.newClient(split[0], timeout);
83                 altIntrospectClient.client_creds(access.getProperty(Config.AAF_ALT_CLIENT_ID,null), 
84                                            access.getProperty(Config.AAF_ALT_CLIENT_SECRET,null));
85             } catch (GeneralSecurityException | IOException | LocatorException e) {
86                 throw new CadiException(e);
87             }
88         }
89
90         directUserPass = new DirectAAFUserPass(env,q);
91     }
92
93     @Override
94     public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) {
95         String value;
96         String token;
97         if ((value=req.getHeader("Authorization"))!=null && value.startsWith("Bearer ")) {
98             token = value.substring(7);
99         } else {
100             token = null;
101         }
102
103         if ("application/x-www-form-urlencoded".equals(req.getContentType())) {
104             Map<String, String[]> map = req.getParameterMap();
105             String client_id=null,client_secret=null,username=null,password=null;
106             for (Map.Entry<String, String[]> es : map.entrySet()) {
107                 switch(es.getKey()) {
108                     case "client_id":
109                         for (String s : es.getValue()) {
110                             client_id=s;
111                         }
112                         break;
113                     case "client_secret":
114                         for (String s : es.getValue()) {
115                             client_secret=s;
116                         }
117                         break;
118                     case "username":
119                         for (String s : es.getValue()) {
120                             username=s;
121                         }
122                         break;
123                     case "password":
124                         for (String s : es.getValue()) {
125                             password=s;
126                         }
127                         break;
128                     case "token": 
129                         if (token!=null) { // Defined as both Bearer and Form Encoded - Error
130                             return new OAuth2HttpTafResp(access, null, "Token Info found as both Bearer Token and Form Info", RESP.FAIL, resp, true);
131                         }
132                         for (String s : es.getValue()) {
133                             token=s;
134                         }
135                         break;
136                     // Ignore others
137                 }
138             }
139             
140             if (client_id==null || client_secret==null) {
141                 return new OAuth2HttpTafResp(access, null, "client_id and client_secret required", RESP.TRY_ANOTHER_TAF, resp, false);
142             }
143             
144             if (token==null) { // No Token to work with, use only Client_ID and Client_Secret 
145                 AuthzTrans trans = (AuthzTrans)req.getAttribute(TransFilter.TRANS_TAG);
146
147                 if (directUserPass.validate(client_id, Type.PASSWORD, client_secret.getBytes(), trans)) {
148                     // Client_ID is valid
149                     if (username==null) { // Validating just the Client_ID
150                         return new OAuth2FormHttpTafResp(access,new OAuth2FormPrincipal(client_id,client_id),"OAuth client_id authenticated",RESP.IS_AUTHENTICATED,resp,false);
151                     } else {
152                         //TODO - Does a clientID need specific Authorization to pair authentication with user name?  At the moment, no.
153                         // username is ok.
154                         if (password!=null) {
155                             if (directUserPass.validate(username, Type.PASSWORD, password.getBytes(), trans)) {
156                                 return new OAuth2FormHttpTafResp(access,new OAuth2FormPrincipal(client_id, username),"OAuth username authenticated",RESP.IS_AUTHENTICATED,resp,false);
157                             } else {
158                                 return new OAuth2HttpTafResp(access,null,"OAuth username " + username + " not authenticated ",RESP.FAIL,resp,true);
159                             }
160                         } else { // no Password
161                             //TODO Check for Trust Permission, which requires looking up Perms?
162                             return new OAuth2HttpTafResp(access,null,"OAuth username " + username + " not authenticated ",RESP.FAIL,resp,true);
163                         }
164                     }
165                 } else {
166                     return new OAuth2HttpTafResp(access,null,"OAuth client_id " + client_id + " not authenticated ",RESP.FAIL,resp,true);
167                 }
168             }
169         } 
170         
171         // OK, have only a Token to validate
172         if (token!=null) {
173             AuthzTrans trans = (AuthzTrans)req.getAttribute(TransFilter.TRANS_TAG);
174
175             try {
176                 Result<Introspect> ri = oaFacade.mappedIntrospect(trans, token);
177                 if (ri.isOK()) {
178                     TokenPerm tp = tkMgr.putIntrospect(ri.value, Hash.hashSHA256(token.getBytes()));
179                     if (tp==null) {
180                         return new OAuth2HttpTafResp(access, null, "TokenPerm persistence failure", RESP.FAIL, resp, false);
181                     } else {
182                         return new OAuth2HttpTafResp(access,new OAuth2Principal(tp,Hash.hashSHA256(token.getBytes())),"Token Authenticated",RESP.IS_AUTHENTICATED,resp,false);
183                     }
184                 } else {
185                     return new OAuth2HttpTafResp(access, null, ri.errorString(), RESP.FAIL, resp, false);
186                 }
187             } catch (APIException e) {
188                 trans.error().log(e,"Error getting token");
189                 return new OAuth2HttpTafResp(access, null, "Error getting token: " + e.getMessage(), RESP.TRY_ANOTHER_TAF, resp, false);
190             } catch (NoSuchAlgorithmException e) {
191                 return new OAuth2HttpTafResp(access, null, "Error in security algorithm: " + e.getMessage(), RESP.TRY_ANOTHER_TAF, resp, false);
192             }
193         }
194         return new OAuth2HttpTafResp(access, null, "No OAuth2 Credentials in OAuthForm", RESP.TRY_ANOTHER_TAF, resp, false);
195     }
196
197     @Override
198     public Resp revalidate(CachedPrincipal prin, Object state) {
199         // TODO Auto-generated method stub
200         return null;
201     }
202
203     class ServiceTPL implements TokenPermLoader {
204         private final AuthzTrans trans;
205         public ServiceTPL(AuthzTrans atrans) {
206             trans = atrans;
207         }
208         
209         @Override
210         public org.onap.aaf.cadi.client.Result<TokenPerm> load(String accessToken, byte[] cred) throws APIException, CadiException, LocatorException {
211             Result<Introspect> ri = oaFacade.mappedIntrospect(trans, accessToken);
212             if (ri.notOK()) {
213                 //TODO what should the status mapping be?
214                 return org.onap.aaf.cadi.client.Result.err(ri.status,ri.errorString());
215             }
216             return org.onap.aaf.cadi.client.Result.ok(200,tkMgr.putIntrospect(ri.value, cred));
217         }
218     }
219
220     public DirectAAFUserPass directUserPass() {
221         return directUserPass;
222     }
223 }
224