Add a MassMail Batch Program
[aaf/authz.git] / auth / auth-oauth / src / main / java / org / onap / aaf / auth / oauth / facade / OAFacadeImpl.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.facade;
23
24 import static org.onap.aaf.auth.layer.Result.ERR_ActionNotCompleted;
25 import static org.onap.aaf.auth.layer.Result.ERR_BadData;
26 import static org.onap.aaf.auth.layer.Result.ERR_ConflictAlreadyExists;
27 import static org.onap.aaf.auth.layer.Result.ERR_Denied;
28 import static org.onap.aaf.auth.layer.Result.ERR_NotFound;
29 import static org.onap.aaf.auth.layer.Result.ERR_NotImplemented;
30 import static org.onap.aaf.auth.layer.Result.ERR_Policy;
31 import static org.onap.aaf.auth.layer.Result.ERR_Security;
32 import static org.onap.aaf.auth.layer.Result.OK;
33
34 import java.security.Principal;
35
36 import javax.servlet.http.HttpServletRequest;
37 import javax.servlet.http.HttpServletResponse;
38
39 import org.onap.aaf.auth.dao.cass.OAuthTokenDAO;
40 import org.onap.aaf.auth.dao.cass.Status;
41 import org.onap.aaf.auth.dao.hl.Question;
42 import org.onap.aaf.auth.env.AuthzEnv;
43 import org.onap.aaf.auth.env.AuthzTrans;
44 import org.onap.aaf.auth.layer.Result;
45 import org.onap.aaf.auth.oauth.AAF_OAuth;
46 import org.onap.aaf.auth.oauth.mapper.Mapper;
47 import org.onap.aaf.auth.oauth.mapper.Mapper.API;
48 import org.onap.aaf.auth.oauth.service.OAuthService;
49 import org.onap.aaf.auth.oauth.service.OAuthService.GRANT_TYPE;
50 import org.onap.aaf.cadi.util.Holder;
51 import org.onap.aaf.cadi.oauth.OAuth2Principal;
52 import org.onap.aaf.cadi.principal.OAuth2FormPrincipal;
53 import org.onap.aaf.misc.env.APIException;
54 import org.onap.aaf.misc.env.Data;
55 import org.onap.aaf.misc.env.Env;
56 import org.onap.aaf.misc.env.TimeTaken;
57 import org.onap.aaf.misc.rosetta.env.RosettaDF;
58 import org.onap.aaf.misc.rosetta.env.RosettaData;
59
60 import aaf.v2_0.Perms;
61
62 /**
63  * AuthzFacade
64  *
65  * This Service Facade encapsulates the essence of the API Service can do, and provides
66  * a single created object for elements such as RosettaDF.
67  *
68  * The Responsibilities of this class are to:
69  * 1) Interact with the Service Implementation (which might be supported by various kinds of Backend Storage)
70  * 2) Validate incoming data (if applicable)
71  * 3) Convert the Service response into the right Format, and mark the Content Type
72  *         a) In the future, we may support multiple Response Formats, aka JSON or XML, based on User Request.
73  * 4) Log Service info, warnings and exceptions as necessary
74  * 5) When asked by the API layer, this will create and write Error content to the OutputStream
75  *
76  * Note: This Class does NOT set the HTTP Status Code.  That is up to the API layer, so that it can be
77  * clearly coordinated with the API Documentation
78  *
79  * @author Jonathan
80  *
81  */
82 public abstract class OAFacadeImpl<TOKEN_REQ,TOKEN,INTROSPECT,ERROR>
83         extends DirectIntrospectImpl<INTROSPECT> implements OAFacade<INTROSPECT> {
84     private static final String INVALID_INPUT = "Invalid Input";
85     private final RosettaDF<TOKEN> tokenDF;
86     private final RosettaDF<TOKEN_REQ> tokenReqDF;
87     private final RosettaDF<INTROSPECT> introspectDF;
88     private final RosettaDF<ERROR> errDF;
89     public final RosettaDF<Perms> permsDF;
90     private final Mapper<TOKEN_REQ, TOKEN, INTROSPECT, ERROR> mapper;
91
92     public OAFacadeImpl(AAF_OAuth api,
93                       OAuthService service,
94                       Mapper<TOKEN_REQ,TOKEN,INTROSPECT,ERROR> mapper,
95                       Data.TYPE dataType) throws APIException {
96         super(service, mapper);
97         this.mapper = mapper;
98         AuthzEnv env = api.env;
99         (tokenReqDF         = env.newDataFactory(mapper.getClass(API.TOKEN_REQ))).in(dataType).out(dataType);
100         (tokenDF         = env.newDataFactory(mapper.getClass(API.TOKEN))).in(dataType).out(dataType);
101         (introspectDF     = env.newDataFactory(mapper.getClass(API.INTROSPECT))).in(dataType).out(dataType);
102         (permsDF         = env.newDataFactory(Perms.class)).in(dataType).out(dataType);
103         (errDF             = env.newDataFactory(mapper.getClass(API.ERROR))).in(dataType).out(dataType);
104     }
105
106     ///////////////////////////
107     // Tokens
108     ///////////////////////////
109     public static final String CREATE_TOKEN = "createToken";
110     public static final String INTROSPECT = "introspect";
111
112     /* (non-Javadoc)
113      * @see org.onap.aaf.auth.oauth.facade.OAFacade#getToken(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletResponse, org.onap.aaf.auth.oauth.service.OAuthAPI)
114      */
115     @Override
116     public Result<Void> createBearerToken(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
117         TimeTaken tt = trans.start(CREATE_TOKEN, Env.SUB|Env.ALWAYS);
118         try {
119             TOKEN_REQ request;
120             try {
121                 request = mapper.tokenReqFromParams(req);
122                 if (request==null) {
123                     Data<TOKEN_REQ> rd = tokenReqDF.newData().load(req.getInputStream());
124                     if (Question.willSpecialLog(trans, trans.user())) {
125                         Question.logEncryptTrace(trans,rd.asString());
126                     }
127                     request = rd.asObject();
128                 }
129             } catch (APIException e) {
130                 trans.error().log(INVALID_INPUT,IN,CREATE_TOKEN);
131                 return Result.err(Status.ERR_BadData,INVALID_INPUT);
132             }
133
134             // Already validated for Oauth2FormPrincipal
135 //            Result<Void> rv = service.validate(trans,mapper.credsFromReq(request));
136 //            if (rv.notOK()) {
137 //                return rv;
138 //            }
139             Holder<GRANT_TYPE> hgt = new Holder<GRANT_TYPE>(GRANT_TYPE.unknown);
140             Result<OAuthTokenDAO.Data> rs = service.createToken(trans,req,mapper.clientTokenReq(request,hgt),hgt);
141             Result<TOKEN> rp;
142             if (rs.isOKhasData()) {
143                 rp = mapper.tokenFromData(rs);
144             } else {
145                 rp = Result.err(rs);
146             }
147             switch(rp.status) {
148                 case OK:
149                     RosettaData<TOKEN> data = tokenDF.newData(trans).load(rp.value);
150                     if (Question.willSpecialLog(trans, trans.user())) {
151                         Question.logEncryptTrace(trans,data.asString());
152                     }
153                     data.to(resp.getOutputStream());
154                     resp.getOutputStream().print('\n');
155                     setContentType(resp,tokenDF.getOutType());
156                     return Result.ok();
157                 default:
158                     return Result.err(rp);
159             }
160         } catch (Exception e) {
161             trans.error().log(e,IN,CREATE_TOKEN);
162             return Result.err(e);
163         } finally {
164             tt.done();
165         }
166
167     }
168
169 /* (non-Javadoc)
170      * @see org.onap.aaf.auth.oauth.facade.OAFacade#Introspect(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
171      */
172     @Override
173     public Result<Void> introspect(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
174         TimeTaken tt = trans.start(INTROSPECT, Env.SUB|Env.ALWAYS);
175         try {
176             Principal p = req.getUserPrincipal();
177             String token=null;
178             if (p != null) {
179                 if (p instanceof OAuth2Principal) {
180                     RosettaData<INTROSPECT> data = introspectDF.newData(trans).load(mapper.fromPrincipal((OAuth2Principal)p));
181                     if (Question.willSpecialLog(trans, trans.user())) {
182                         Question.logEncryptTrace(trans,data.asString());
183                     }
184                     data.to(resp.getOutputStream());
185                     resp.getOutputStream().print('\n');
186                     setContentType(resp,tokenDF.getOutType());
187                     return Result.ok();
188                 } else if (p instanceof OAuth2FormPrincipal) {
189                     token = req.getParameter("token");
190                 }
191             }
192
193             if (token==null) {
194                 token = req.getParameter("access_token");
195                 if (token==null || token.isEmpty()) {
196                     token = req.getHeader("Authorization");
197                     if (token != null && token.startsWith("Bearer ")) {
198                         token = token.substring(7);
199                     } else {
200                         token = req.getParameter("token");
201                         if (token==null) {
202                             return Result.err(Result.ERR_Security,"token is required");
203                         }
204                     }
205                 }
206             }
207
208             Result<INTROSPECT> rti = mappedIntrospect(trans,token);
209             switch(rti.status) {
210                 case OK:
211                     RosettaData<INTROSPECT> data = introspectDF.newData(trans).load(rti.value);
212                     if (Question.willSpecialLog(trans, trans.user())) {
213                         Question.logEncryptTrace(trans,data.asString());
214                     }
215                     data.to(resp.getOutputStream());
216                     resp.getOutputStream().print('\n');
217                     setContentType(resp,tokenDF.getOutType());
218                     return Result.ok();
219                 default:
220                     return Result.err(rti);
221             }
222         } catch (Exception e) {
223             trans.error().log(e,IN,INTROSPECT);
224             return Result.err(e);
225         } finally {
226             tt.done();
227         }
228     }
229
230
231     /* (non-Javadoc)
232      * @see com.att.authz.facade.AuthzFacade#error(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletResponse, int)
233      *
234      * Note: Conforms to AT&T TSS RESTful Error Structure
235      */
236     @Override
237     public void error(AuthzTrans trans, HttpServletResponse response, Result<?> result) {
238         error(trans, response, result.status,
239                 result.details==null?"":result.details.trim(),
240                 result.variables==null?Result.EMPTY_VARS:result.variables);
241     }
242
243     @Override
244     public void error(AuthzTrans trans, HttpServletResponse response, int status, final String _msg, final Object ... _detail) {
245         String msgId;
246         String prefix;
247         boolean hidemsg=false;
248         switch(status) {
249             case 202:
250             case ERR_ActionNotCompleted:
251                 msgId = "SVC1202";
252                 prefix = "Accepted, Action not complete";
253                 response.setStatus(/*httpstatus=*/202);
254                 break;
255
256             case 403:
257             case ERR_Policy:
258             case ERR_Security:
259             case ERR_Denied:
260                 msgId = "SVC1403";
261                 prefix = "Forbidden";
262                 response.setStatus(/*httpstatus=*/403);
263                 break;
264
265             case 404:
266             case ERR_NotFound:
267                 msgId = "SVC1404";
268                 prefix = "Not Found";
269                 response.setStatus(/*httpstatus=*/404);
270                 break;
271
272             case 406:
273             case ERR_BadData:
274                 msgId="SVC1406";
275                 prefix = "Not Acceptable";
276                 response.setStatus(/*httpstatus=*/406);
277                 break;
278
279             case 409:
280             case ERR_ConflictAlreadyExists:
281                 msgId = "SVC1409";
282                 prefix = "Conflict Already Exists";
283                 response.setStatus(/*httpstatus=*/409);
284                 break;
285
286             case 501:
287             case ERR_NotImplemented:
288                 msgId = "SVC1501";
289                 prefix = "Not Implemented";
290                 response.setStatus(/*httpstatus=*/501);
291                 break;
292
293
294             default:
295                 msgId = "SVC1500";
296                 prefix = "General Service Error";
297                 response.setStatus(/*httpstatus=*/500);
298                 hidemsg=true;
299                 break;
300         }
301
302         try {
303             StringBuilder holder = new StringBuilder();
304             ERROR em = mapper.errorFromMessage(holder, msgId,prefix + ": " + _msg,_detail);
305             trans.checkpoint(
306                     "ErrResp [" +
307                     msgId +
308                     "] " +
309                     holder.toString(),
310                     Env.ALWAYS);
311             if (hidemsg) {
312                 holder.setLength(0);
313                 em = mapper.errorFromMessage(holder, msgId, "Server had an issue processing this request");
314             }
315             errDF.newData(trans).load(em).to(response.getOutputStream());
316
317         } catch (Exception e) {
318             trans.error().log(e,"unable to send response for",_msg);
319         }
320     }
321
322     public Mapper<TOKEN_REQ,TOKEN,INTROSPECT,ERROR> mapper() {
323         return mapper;
324     }
325
326     /* (non-Javadoc)
327      * @see org.onap.aaf.auth.oauth.facade.OAFacade#service()
328      */
329     @Override
330     public OAuthService service() {
331         return service;
332     }
333 }