eff47e6380a677e0bb6df0fe0d8c2b56aeb1a8b9
[moon.git] /
1 /*
2  * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.aaa.basic;
10
11 import com.sun.jersey.core.util.Base64;
12 import java.util.List;
13 import java.util.Map;
14 import org.opendaylight.aaa.AuthenticationBuilder;
15 import org.opendaylight.aaa.PasswordCredentialBuilder;
16 import org.opendaylight.aaa.api.Authentication;
17 import org.opendaylight.aaa.api.AuthenticationException;
18 import org.opendaylight.aaa.api.Claim;
19 import org.opendaylight.aaa.api.CredentialAuth;
20 import org.opendaylight.aaa.api.PasswordCredentials;
21 import org.opendaylight.aaa.api.TokenAuth;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 /**
26  * An HTTP Basic authenticator. Note that this is provided as a Hydrogen
27  * backward compatible authenticator, but usage of this authenticator or HTTP
28  * Basic Authentication is highly discouraged due to its vulnerability.
29  *
30  * To obtain a token using the HttpBasicAuth Strategy, add a header to your HTTP
31  * request in the form:
32  * <code>Authorization: Basic BASE_64_ENCODED_CREDENTIALS</code>
33  *
34  * Where <code>BASE_64_ENCODED_CREDENTIALS</code> is the base 64 encoded value
35  * of the user's credentials in the following form: <code>user:password</code>
36  *
37  * For example, assuming the user is "admin" and the password is "admin":
38  * <code>Authorization: Basic YWRtaW46YWRtaW4=</code>
39  *
40  * @author liemmn
41  *
42  */
43 public class HttpBasicAuth implements TokenAuth {
44
45     public static final String AUTH_HEADER = "Authorization";
46
47     public static final String AUTH_SEP = ":";
48
49     public static final String BASIC_PREFIX = "Basic ";
50
51     // TODO relocate this constant
52     public static final String DEFAULT_DOMAIN = "sdn";
53
54     /**
55      * username and password
56      */
57     private static final int NUM_HEADER_CREDS = 2;
58
59     /**
60      * username, password and domain
61      */
62     private static final int NUM_TOKEN_CREDS = 3;
63
64     private static final Logger LOG = LoggerFactory.getLogger(HttpBasicAuth.class);
65
66     volatile CredentialAuth<PasswordCredentials> credentialAuth;
67
68     private static boolean checkAuthHeaderFormat(final String authHeader) {
69         return (authHeader != null && authHeader.startsWith(BASIC_PREFIX));
70     }
71
72     private static String extractAuthHeader(final Map<String, List<String>> headers) {
73         return headers.get(AUTH_HEADER).get(0);
74     }
75
76     private static String[] extractCredentialArray(final String authHeader) {
77         return new String(Base64.base64Decode(authHeader.substring(BASIC_PREFIX.length())))
78                 .split(AUTH_SEP);
79     }
80
81     private static boolean verifyCredentialArray(final String[] creds) {
82         return (creds != null && creds.length == NUM_HEADER_CREDS);
83     }
84
85     private static String[] addDomainToCredentialArray(final String[] creds) {
86         String newCredentialArray[] = new String[NUM_TOKEN_CREDS];
87         System.arraycopy(creds, 0, newCredentialArray, 0, creds.length);
88         newCredentialArray[2] = DEFAULT_DOMAIN;
89         return newCredentialArray;
90     }
91
92     private static Authentication generateAuthentication(
93             CredentialAuth<PasswordCredentials> credentialAuth, final String[] creds)
94             throws ArrayIndexOutOfBoundsException {
95         final PasswordCredentials pc = new PasswordCredentialBuilder().setUserName(creds[0])
96                 .setPassword(creds[1]).setDomain(creds[2]).build();
97         final Claim claim = credentialAuth.authenticate(pc);
98         return new AuthenticationBuilder(claim).build();
99     }
100
101     @Override
102     public Authentication validate(final Map<String, List<String>> headers)
103             throws AuthenticationException {
104         if (headers.containsKey(AUTH_HEADER)) {
105             final String authHeader = extractAuthHeader(headers);
106             if (checkAuthHeaderFormat(authHeader)) {
107                 // HTTP Basic Auth
108                 String[] creds = extractCredentialArray(authHeader);
109                 // If no domain was supplied then use the default one, which is
110                 // "sdn".
111                 if (verifyCredentialArray(creds)) {
112                     creds = addDomainToCredentialArray(creds);
113                 }
114                 // Assumes correct formatting in form Base64("user:password").
115                 // Throws an exception if an unknown format is used.
116                 try {
117                     return generateAuthentication(this.credentialAuth, creds);
118                 } catch (ArrayIndexOutOfBoundsException e) {
119                     final String message = "Login Attempt in Bad Format."
120                             + " Please provide user:password in Base64 format.";
121                     LOG.info(message);
122                     throw new AuthenticationException(message);
123                 }
124             }
125         }
126         return null;
127     }
128
129 }