2 * ============LICENSE_START====================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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====================================================
22 package org.onap.aaf.auth.oauth;
24 import java.io.IOException;
25 import java.security.GeneralSecurityException;
26 import java.security.NoSuchAlgorithmException;
29 import javax.servlet.http.HttpServletRequest;
30 import javax.servlet.http.HttpServletResponse;
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;
62 import aafoauth.v2_0.Introspect;
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;
71 public DirectOAuthTAF(AuthzEnv env, Question q, DirectIntrospect<Introspect> facade) throws APIException, CadiException {
72 access = env.access();
74 tkMgr = TokenMgr.getInstance(access,"dbToken","dbIntrospect");
75 String alt_url = access.getProperty(Config.AAF_ALT_OAUTH2_INTROSPECT_URL,null);
76 TokenClientFactory tcf;
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);
90 directUserPass = new DirectAAFUserPass(env,q);
94 public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) {
97 if ((value=req.getHeader("Authorization"))!=null && value.startsWith("Bearer ")) {
98 token = value.substring(7);
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()) {
109 for (String s : es.getValue()) {
113 case "client_secret":
114 for (String s : es.getValue()) {
119 for (String s : es.getValue()) {
124 for (String s : es.getValue()) {
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);
132 for (String s : es.getValue()) {
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);
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);
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);
152 //TODO - Does a clientID need specific Authorization to pair authentication with user name? At the moment, no.
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);
158 return new OAuth2HttpTafResp(access,null,"OAuth username " + username + " not authenticated ",RESP.FAIL,resp,true);
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);
166 return new OAuth2HttpTafResp(access,null,"OAuth client_id " + client_id + " not authenticated ",RESP.FAIL,resp,true);
171 // OK, have only a Token to validate
173 AuthzTrans trans = (AuthzTrans)req.getAttribute(TransFilter.TRANS_TAG);
176 Result<Introspect> ri = oaFacade.mappedIntrospect(trans, token);
178 TokenPerm tp = tkMgr.putIntrospect(ri.value, Hash.hashSHA256(token.getBytes()));
180 return new OAuth2HttpTafResp(access, null, "TokenPerm persistence failure", RESP.FAIL, resp, false);
182 return new OAuth2HttpTafResp(access,new OAuth2Principal(tp,Hash.hashSHA256(token.getBytes())),"Token Authenticated",RESP.IS_AUTHENTICATED,resp,false);
185 return new OAuth2HttpTafResp(access, null, ri.errorString(), RESP.FAIL, resp, false);
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);
194 return new OAuth2HttpTafResp(access, null, "No OAuth2 Credentials in OAuthForm", RESP.TRY_ANOTHER_TAF, resp, false);
198 public Resp revalidate(CachedPrincipal prin, Object state) {
199 // TODO Auto-generated method stub
203 class ServiceTPL implements TokenPermLoader {
204 private final AuthzTrans trans;
205 public ServiceTPL(AuthzTrans atrans) {
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);
213 //TODO what should the status mapping be?
214 return org.onap.aaf.cadi.client.Result.err(ri.status,ri.errorString());
216 return org.onap.aaf.cadi.client.Result.ok(200,tkMgr.putIntrospect(ri.value, cred));
220 public DirectAAFUserPass directUserPass() {
221 return directUserPass;