1 /* 2 * Copyright 2023 Google LLC 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * * Neither the name of Google LLC nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package com.google.auth.oauth2; 33 34 import java.util.HashMap; 35 import java.util.Locale; 36 import java.util.Map; 37 import javax.annotation.Nullable; 38 39 /** 40 * The IdentityPool credential source. Dictates the retrieval method of the external credential, 41 * which can either be through a metadata server or a local file. 42 */ 43 public class IdentityPoolCredentialSource extends ExternalAccountCredentials.CredentialSource { 44 45 private static final long serialVersionUID = -745855247050085694L; 46 IdentityPoolCredentialSourceType credentialSourceType; 47 CredentialFormatType credentialFormatType; 48 String credentialLocation; 49 @Nullable String subjectTokenFieldName; 50 @Nullable Map<String, String> headers; 51 52 /** 53 * The source of the 3P credential. 54 * 55 * <p>If this is a file based 3P credential, the credentials file can be retrieved using the 56 * `file` key. 57 * 58 * <p>If this is URL-based 3p credential, the metadata server URL can be retrieved using the `url` 59 * key. 60 * 61 * <p>The third party credential can be provided in different formats, such as text or JSON. The 62 * format can be specified using the `format` header, which returns a map with keys `type` and 63 * `subject_token_field_name`. If the `type` is json, the `subject_token_field_name` must be 64 * provided. If no format is provided, we expect the token to be in the raw text format. 65 * 66 * <p>Optional headers can be present, and should be keyed by `headers`. 67 */ 68 @SuppressWarnings("unchecked") IdentityPoolCredentialSource(Map<String, Object> credentialSourceMap)69 public IdentityPoolCredentialSource(Map<String, Object> credentialSourceMap) { 70 super(credentialSourceMap); 71 72 if (credentialSourceMap.containsKey("file") && credentialSourceMap.containsKey("url")) { 73 throw new IllegalArgumentException( 74 "Only one credential source type can be set, either file or url."); 75 } 76 77 if (credentialSourceMap.containsKey("file")) { 78 credentialLocation = (String) credentialSourceMap.get("file"); 79 credentialSourceType = IdentityPoolCredentialSourceType.FILE; 80 } else if (credentialSourceMap.containsKey("url")) { 81 credentialLocation = (String) credentialSourceMap.get("url"); 82 credentialSourceType = IdentityPoolCredentialSourceType.URL; 83 } else { 84 throw new IllegalArgumentException( 85 "Missing credential source file location or URL. At least one must be specified."); 86 } 87 88 Map<String, String> headersMap = (Map<String, String>) credentialSourceMap.get("headers"); 89 if (headersMap != null && !headersMap.isEmpty()) { 90 headers = new HashMap<>(); 91 headers.putAll(headersMap); 92 } 93 94 // If the format is not provided, we expect the token to be in the raw text format. 95 credentialFormatType = CredentialFormatType.TEXT; 96 97 Map<String, String> formatMap = (Map<String, String>) credentialSourceMap.get("format"); 98 if (formatMap != null && formatMap.containsKey("type")) { 99 String type = formatMap.get("type"); 100 101 if (type != null && "json".equals(type.toLowerCase(Locale.US))) { 102 // For JSON, the subject_token field name must be provided. 103 if (!formatMap.containsKey("subject_token_field_name")) { 104 throw new IllegalArgumentException( 105 "When specifying a JSON credential type, the subject_token_field_name must be set."); 106 } 107 credentialFormatType = CredentialFormatType.JSON; 108 subjectTokenFieldName = formatMap.get("subject_token_field_name"); 109 } else if (type != null && "text".equals(type.toLowerCase(Locale.US))) { 110 credentialFormatType = CredentialFormatType.TEXT; 111 } else { 112 throw new IllegalArgumentException( 113 String.format("Invalid credential source format type: %s.", type)); 114 } 115 } 116 } 117 hasHeaders()118 boolean hasHeaders() { 119 return headers != null && !headers.isEmpty(); 120 } 121 122 enum IdentityPoolCredentialSourceType { 123 FILE, 124 URL 125 } 126 127 enum CredentialFormatType { 128 TEXT, 129 JSON 130 } 131 } 132