001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2016 ForgeRock AS. 015 */ 016package org.forgerock.opendj.rest2ldap.authz; 017 018import static org.forgerock.util.Reject.checkNotNull; 019 020import org.forgerock.http.protocol.Headers; 021import org.forgerock.util.Function; 022import org.forgerock.util.Pair; 023import org.forgerock.util.encode.Base64; 024import org.forgerock.util.promise.NeverThrowsException; 025 026/** 027 * Factory method for function extracting credentials from HTTP request {@link Headers}. 028 */ 029public final class CredentialExtractors { 030 031 /** HTTP Header sent by the client with HTTP basic authentication. */ 032 public static final String HTTP_BASIC_AUTH_HEADER = "Authorization"; 033 034 private CredentialExtractors() { 035 } 036 037 /** 038 * Creates a function which extracts the user's credentials from the standard HTTP Basic header. 039 * 040 * @return the basic extractor singleton 041 */ 042 public static Function<Headers, Pair<String, String>, NeverThrowsException> httpBasicExtractor() { 043 return HttpBasicExtractor.INSTANCE; 044 } 045 046 /** 047 * Creates a function which extracts the user's credentials from custom HTTP header in addition of the standard HTTP 048 * Basic one. 049 * 050 * @param customHeaderUsername 051 * Name of the additional header to check for the user's name 052 * @param customHeaderPassword 053 * Name of the additional header to check for the user's password 054 * @return A new credentials extractors looking for custom header. 055 */ 056 public static Function<Headers, Pair<String, String>, NeverThrowsException> newCustomHeaderExtractor( 057 String customHeaderUsername, String customHeaderPassword) { 058 return new CustomHeaderExtractor(customHeaderUsername, customHeaderPassword); 059 } 060 061 /** Extract the user's credentials from custom {@link Headers}. */ 062 private static final class CustomHeaderExtractor 063 implements Function<Headers, Pair<String, String>, NeverThrowsException> { 064 065 private final String customHeaderUsername; 066 private final String customHeaderPassword; 067 068 /** 069 * Create a new CustomHeaderExtractor. 070 * 071 * @param customHeaderUsername 072 * Name of the header containing the username 073 * @param customHeaderPassword 074 * Name of the header containing the password 075 * @throws NullPointerException 076 * if a parameter is null. 077 */ 078 public CustomHeaderExtractor(String customHeaderUsername, String customHeaderPassword) { 079 this.customHeaderUsername = checkNotNull(customHeaderUsername, "customHeaderUsername cannot be null"); 080 this.customHeaderPassword = checkNotNull(customHeaderPassword, "customHeaderPassword cannot be null"); 081 } 082 083 @Override 084 public Pair<String, String> apply(Headers headers) { 085 final String userName = headers.getFirst(customHeaderUsername); 086 final String password = headers.getFirst(customHeaderPassword); 087 if (userName != null && password != null) { 088 return Pair.of(userName, password); 089 } 090 return HttpBasicExtractor.INSTANCE.apply(headers); 091 } 092 } 093 094 /** Extract the user's credentials from the standard HTTP Basic {@link Headers}. */ 095 private static final class HttpBasicExtractor 096 implements Function<Headers, Pair<String, String>, NeverThrowsException> { 097 098 /** Reference to the HttpBasicExtractor Singleton. */ 099 public static final HttpBasicExtractor INSTANCE = new HttpBasicExtractor(); 100 101 private HttpBasicExtractor() { } 102 103 @Override 104 public Pair<String, String> apply(Headers headers) { 105 final String httpBasicAuthHeader = headers.getFirst(HTTP_BASIC_AUTH_HEADER); 106 if (httpBasicAuthHeader != null) { 107 final Pair<String, String> userCredentials = parseUsernamePassword(httpBasicAuthHeader); 108 if (userCredentials != null) { 109 return userCredentials; 110 } 111 } 112 return null; 113 } 114 115 private Pair<String, String> parseUsernamePassword(String authHeader) { 116 if (authHeader != null && (authHeader.toLowerCase().startsWith("basic"))) { 117 // We received authentication info 118 // Example received header: 119 // "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" 120 final String base64UserCredentials = authHeader.substring("basic".length() + 1); 121 // Example usage of base64: 122 // Base64("Aladdin:open sesame") = "QWxhZGRpbjpvcGVuIHNlc2FtZQ==" 123 final String userCredentials = new String(Base64.decode(base64UserCredentials)); 124 String[] split = userCredentials.split(":"); 125 if (split.length == 2) { 126 return Pair.of(split[0], split[1]); 127 } 128 } 129 return null; 130 } 131 } 132}