2 * Copyright (c) 2014, 2017 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
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
9 package org.opendaylight.aaa.shiro.tokenauthrealm.auth;
11 import java.nio.charset.StandardCharsets;
12 import java.util.Base64;
13 import java.util.List;
15 import org.opendaylight.aaa.api.Authentication;
16 import org.opendaylight.aaa.api.AuthenticationException;
17 import org.opendaylight.aaa.api.Claim;
18 import org.opendaylight.aaa.api.CredentialAuth;
19 import org.opendaylight.aaa.api.PasswordCredentials;
20 import org.opendaylight.aaa.api.TokenAuth;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
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.
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>
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>
39 * For example, assuming the user is "admin" and the password is "admin":
40 * <code>Authorization: Basic YWRtaW46YWRtaW4=</code>
44 public class HttpBasicAuth implements TokenAuth {
46 public static final String AUTH_HEADER = "Authorization";
48 public static final String AUTH_SEP = ":";
50 public static final String BASIC_PREFIX = "Basic ";
52 // TODO relocate this constant
53 public static final String DEFAULT_DOMAIN = "sdn";
56 * username and password.
58 private static final int NUM_HEADER_CREDS = 2;
61 * username, password and domain.
63 private static final int NUM_TOKEN_CREDS = 3;
65 private static final Logger LOG = LoggerFactory.getLogger(HttpBasicAuth.class);
67 private final CredentialAuth<PasswordCredentials> credentialAuth;
69 public HttpBasicAuth(CredentialAuth<PasswordCredentials> credentialAuth) {
70 this.credentialAuth = credentialAuth;
73 private static boolean checkAuthHeaderFormat(final String authHeader) {
74 return authHeader != null && authHeader.startsWith(BASIC_PREFIX);
77 private static String extractAuthHeader(final Map<String, List<String>> headers) {
78 return headers.get(AUTH_HEADER).get(0);
81 private static String[] extractCredentialArray(final String authHeader) {
82 return new String(Base64.getDecoder().decode(authHeader.substring(BASIC_PREFIX.length())),
83 StandardCharsets.UTF_8).split(AUTH_SEP);
86 private static boolean verifyCredentialArray(final String[] creds) {
87 return creds != null && creds.length == NUM_HEADER_CREDS;
90 private static String[] addDomainToCredentialArray(final String[] creds) {
91 String[] newCredentialArray = new String[NUM_TOKEN_CREDS];
92 System.arraycopy(creds, 0, newCredentialArray, 0, creds.length);
93 newCredentialArray[2] = DEFAULT_DOMAIN;
94 return newCredentialArray;
97 private static Authentication generateAuthentication(
98 CredentialAuth<PasswordCredentials> credentialAuth, final String[] creds)
99 throws ArrayIndexOutOfBoundsException {
100 final PasswordCredentials pc = new PasswordCredentialBuilder().setUserName(creds[0])
101 .setPassword(creds[1]).setDomain(creds[2]).build();
102 final Claim claim = credentialAuth.authenticate(pc);
103 return new AuthenticationBuilder(claim).build();
107 public Authentication validate(final Map<String, List<String>> headers)
108 throws AuthenticationException {
109 if (headers.containsKey(AUTH_HEADER)) {
110 final String authHeader = extractAuthHeader(headers);
111 if (checkAuthHeaderFormat(authHeader)) {
113 String[] creds = extractCredentialArray(authHeader);
114 // If no domain was supplied then use the default one, which is
116 if (verifyCredentialArray(creds)) {
117 creds = addDomainToCredentialArray(creds);
119 // Assumes correct formatting in form Base64("user:password").
120 // Throws an exception if an unknown format is used.
122 return generateAuthentication(this.credentialAuth, creds);
123 } catch (ArrayIndexOutOfBoundsException e) {
124 final String message = "Login Attempt in Bad Format."
125 + " Please provide user:password in Base64 format.";
127 throw new AuthenticationException(message, e);