1 /*******************************************************************************
2 * ============LICENSE_START=======================================================
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
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=========================================================
19 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21 *******************************************************************************/
22 package org.onap.dmaap.dmf.mr.security.impl;
24 import com.att.eelf.configuration.EELFLogger;
25 import com.att.eelf.configuration.EELFManager;
26 import com.att.nsa.configs.ConfigDbException;
27 import com.att.nsa.drumlin.till.data.sha1HmacSigner;
28 import com.att.nsa.security.NsaApiKey;
29 import com.att.nsa.security.db.NsaApiDb;
30 import org.onap.dmaap.dmf.mr.beans.DMaaPContext;
31 import org.onap.dmaap.dmf.mr.security.DMaaPAuthenticator;
33 import javax.servlet.http.HttpServletRequest;
34 import java.text.ParseException;
35 import java.text.SimpleDateFormat;
36 import java.util.Date;
39 * This authenticator handles an AWS-like authentication, originally used by the
40 * Cambria server (the API server for UEB).
46 public class DMaaPOriginalUebAuthenticator<K extends NsaApiKey> implements DMaaPAuthenticator<K> {
48 * constructor initialization
51 * @param requestTimeWindowMs
53 public DMaaPOriginalUebAuthenticator(NsaApiDb<K> db, long requestTimeWindowMs) {
55 fRequestTimeWindowMs = requestTimeWindowMs;
63 public boolean qualify(HttpServletRequest req) {
64 // accept anything that comes in with X-(Cambria)Auth in the header
65 final String xAuth = getFirstHeader(req, new String[] { "X-CambriaAuth", "X-Auth" });
70 * method for authentication
75 public K isAuthentic(HttpServletRequest req) {
76 final String remoteAddr = req.getRemoteAddr();
77 // Cambria originally used "Cambria..." headers, but as the API key
79 // general, we take either form.
80 final String xAuth = getFirstHeader(req, new String[] { "X-CambriaAuth", "X-Auth" });
81 final String xDate = getFirstHeader(req, new String[] { "X-CambriaDate", "X-Date" });
83 final String httpDate = req.getHeader("Date");
85 final String xNonce = getFirstHeader(req, new String[] { "X-Nonce" });
86 return authenticate(remoteAddr, xAuth, xDate, httpDate, xNonce);
90 * Authenticate a user's request. This method returns the API key if the
91 * user is authentic, null otherwise.
98 * @return an api key record, or null
100 public K authenticate(String remoteAddr, String xAuth, String xDate, String httpDate, String nonce) {
102 authLog("No X-Auth header on request", remoteAddr);
106 final String[] xAuthParts = xAuth.split(":");
107 if (xAuthParts.length != 2) {
108 authLog("Bad X-Auth header format (" + xAuth + ")", remoteAddr);
113 // get the api key and signature
114 final String clientApiKey = xAuthParts[0];
115 final String clientApiHash = xAuthParts[1];
116 if (clientApiKey.length() == 0 || clientApiHash.length() == 0) {
117 authLog("Bad X-Auth header format (" + xAuth + ")", remoteAddr);
120 // if the user provided X-Date, use that. Otherwise, go for Date
121 final String dateString = xDate != null ? xDate : httpDate;
122 final Date clientDate = getClientDate(dateString);
123 if (clientDate == null) {
124 authLog("Couldn't parse client date '" + dateString + "'. Preferring X-Date over Date.", remoteAddr);
127 // check the time range
128 final long nowMs = System.currentTimeMillis();
129 final long diffMs = Math.abs(nowMs - clientDate.getTime());
130 if (diffMs > fRequestTimeWindowMs) {
131 authLog("Client date is not in acceptable range of server date. Client:" + clientDate.getTime()
132 + ", Server: " + nowMs + ", Threshold: " + fRequestTimeWindowMs + ".", remoteAddr);
137 apiRecord = fDb.loadApiKey(clientApiKey);
138 if (apiRecord == null) {
139 authLog("No such API key " + clientApiKey, remoteAddr);
142 } catch (ConfigDbException e) {
143 authLog("Couldn't load API key " + clientApiKey + ": " + e.getMessage(), remoteAddr);
146 // make the signed content
147 final StringBuilder sb = new StringBuilder();
148 sb.append(dateString);
153 final String signedContent = sb.toString();
154 // now check the signed date string
155 final String serverCalculatedSignature = sha1HmacSigner.sign(signedContent, apiRecord.getSecret());
156 if (serverCalculatedSignature == null || !serverCalculatedSignature.equals(clientApiHash)) {
157 authLog("Signatures don't match. Rec'd " + clientApiHash + ", expect " + serverCalculatedSignature + ".",
161 authLog("authenticated " + apiRecord.getKey(), remoteAddr);
166 * Get the first value of the first existing header from the headers list
170 * @return a header value, or null if none exist
172 private static String getFirstHeader(HttpServletRequest req, String[] headers) {
173 for (String header : headers) {
174 final String result = req.getHeader(header);
182 * Parse the date string into a Date using one of the supported date
186 * @return a date, or null
188 private static Date getClientDate(String dateString) {
189 if (dateString == null) {
195 for (String dateFormat : kDateFormats) {
196 final SimpleDateFormat parser = new SimpleDateFormat(dateFormat, java.util.Locale.US);
197 if (!dateFormat.contains("z") && !dateFormat.contains("Z")) {
198 parser.setTimeZone(TIMEZONE_GMT);
202 result = parser.parse(dateString);
204 } catch (ParseException e) {
205 // presumably wrong format
211 private static void authLog(String msg, String remoteAddr) {
212 log.info("AUTH-LOG(" + remoteAddr + "): " + msg);
215 private final NsaApiDb<K> fDb;
216 private final long fRequestTimeWindowMs;
218 private static final java.util.TimeZone TIMEZONE_GMT = java.util.TimeZone.getTimeZone("GMT");
220 private static final String kDateFormats[] =
222 // W3C date format (RFC 3339).
223 "yyyy-MM-dd'T'HH:mm:ssz",
224 "yyyy-MM-dd'T'HH:mm:ssXXX", // as of Java 7, reqd to handle colon in TZ offset
226 // Preferred HTTP date format (RFC 1123).
227 "EEE, dd MMM yyyy HH:mm:ss zzz",
229 // simple unix command line 'date' format
230 "EEE MMM dd HH:mm:ss z yyyy",
232 // Common date format (RFC 822).
233 "EEE, dd MMM yy HH:mm:ss z",
234 "EEE, dd MMM yy HH:mm z",
235 "dd MMM yy HH:mm:ss z",
238 // Obsoleted HTTP date format (ANSI C asctime() format).
239 "EEE MMM dd HH:mm:ss yyyy",
241 // Obsoleted HTTP date format (RFC 1036).
242 "EEEE, dd-MMM-yy HH:mm:ss zzz",
263 // logger declaration
265 private static final EELFLogger log = EELFManager.getInstance().getLogger(DMaaPOriginalUebAuthenticator.class);
268 // TODO Auto-generated method stub
272 public K authenticate(DMaaPContext ctx) {
287 public void addAuthenticator ( DMaaPAuthenticator<K> a )