Fix packaging for shiro bundle
[aaa.git] / aaa-shiro / impl / src / main / java / org / opendaylight / aaa / shiro / tokenauthrealm / auth / HttpBasicAuth.java
1 /*
2  * Copyright (c) 2014, 2017 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.shiro.tokenauthrealm.auth;
10
11 import java.util.Base64;
12 import java.util.List;
13 import java.util.Map;
14 import org.opendaylight.aaa.api.Authentication;
15 import org.opendaylight.aaa.api.AuthenticationException;
16 import org.opendaylight.aaa.api.Claim;
17 import org.opendaylight.aaa.api.CredentialAuth;
18 import org.opendaylight.aaa.api.PasswordCredentials;
19 import org.opendaylight.aaa.api.TokenAuth;
20 import org.opendaylight.aaa.shiro.tokenauthrealm.ServiceLocator;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 /**
25  * An HTTP Basic authenticator. Note that this is provided as a Hydrogen
26  * backward compatible authenticator, but usage of this authenticator or HTTP
27  * Basic Authentication is highly discouraged due to its vulnerability.
28  *
29  * <p>
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  * <p>
35  * Where <code>BASE_64_ENCODED_CREDENTIALS</code> is the base 64 encoded value
36  * of the user's credentials in the following form: <code>user:password</code>
37  *
38  * <p>
39  * For example, assuming the user is "admin" and the password is "admin":
40  * <code>Authorization: Basic YWRtaW46YWRtaW4=</code>
41  *
42  * @author liemmn
43  */
44 public class HttpBasicAuth implements TokenAuth {
45
46     public static final String AUTH_HEADER = "Authorization";
47
48     public static final String AUTH_SEP = ":";
49
50     public static final String BASIC_PREFIX = "Basic ";
51
52     // TODO relocate this constant
53     public static final String DEFAULT_DOMAIN = "sdn";
54
55     /**
56      * username and password.
57      */
58     private static final int NUM_HEADER_CREDS = 2;
59
60     /**
61      * username, password and domain.
62      */
63     private static final int NUM_TOKEN_CREDS = 3;
64
65     private static final Logger LOG = LoggerFactory.getLogger(HttpBasicAuth.class);
66
67     volatile CredentialAuth<PasswordCredentials> credentialAuth =
68             ServiceLocator.getInstance().getCredentialAuth();
69
70     private static boolean checkAuthHeaderFormat(final String authHeader) {
71         return authHeader != null && authHeader.startsWith(BASIC_PREFIX);
72     }
73
74     private static String extractAuthHeader(final Map<String, List<String>> headers) {
75         return headers.get(AUTH_HEADER).get(0);
76     }
77
78     private static String[] extractCredentialArray(final String authHeader) {
79         return new String(Base64.getDecoder().decode(authHeader.substring(BASIC_PREFIX.length())))
80                 .split(AUTH_SEP);
81     }
82
83     private static boolean verifyCredentialArray(final String[] creds) {
84         return creds != null && creds.length == NUM_HEADER_CREDS;
85     }
86
87     private static String[] addDomainToCredentialArray(final String[] creds) {
88         String[] newCredentialArray = new String[NUM_TOKEN_CREDS];
89         System.arraycopy(creds, 0, newCredentialArray, 0, creds.length);
90         newCredentialArray[2] = DEFAULT_DOMAIN;
91         return newCredentialArray;
92     }
93
94     private static Authentication generateAuthentication(
95             CredentialAuth<PasswordCredentials> credentialAuth, final String[] creds)
96             throws ArrayIndexOutOfBoundsException {
97         final PasswordCredentials pc = new PasswordCredentialBuilder().setUserName(creds[0])
98                 .setPassword(creds[1]).setDomain(creds[2]).build();
99         final Claim claim = credentialAuth.authenticate(pc);
100         return new AuthenticationBuilder(claim).build();
101     }
102
103     @Override
104     public Authentication validate(final Map<String, List<String>> headers)
105             throws AuthenticationException {
106         if (headers.containsKey(AUTH_HEADER)) {
107             final String authHeader = extractAuthHeader(headers);
108             if (checkAuthHeaderFormat(authHeader)) {
109                 // HTTP Basic Auth
110                 String[] creds = extractCredentialArray(authHeader);
111                 // If no domain was supplied then use the default one, which is
112                 // "sdn".
113                 if (verifyCredentialArray(creds)) {
114                     creds = addDomainToCredentialArray(creds);
115                 }
116                 // Assumes correct formatting in form Base64("user:password").
117                 // Throws an exception if an unknown format is used.
118                 try {
119                     return generateAuthentication(this.credentialAuth, creds);
120                 } catch (ArrayIndexOutOfBoundsException e) {
121                     final String message = "Login Attempt in Bad Format."
122                             + " Please provide user:password in Base64 format.";
123                     LOG.info(message);
124                     throw new AuthenticationException(message);
125                 }
126             }
127         }
128         return null;
129     }
130 }