001/* 002 * Copyright 2024 Vonage 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.vonage.client.auth; 017 018import com.vonage.client.auth.hashutils.HashUtil; 019import java.util.*; 020 021/** 022 * Internal class, managing a collection of {@link AuthMethod}s. 023 * <p> 024 * This holds a collection of AuthMethod instances, in order of preference, and 025 * allow for simple selection of an appropriate AuthMethod for a particular REST endpoint. 026 */ 027public class AuthCollection { 028 private final SortedSet<AuthMethod> authList; 029 030 /** 031 * Create a new AuthCollection with an empty set of AuthMethods. 032 */ 033 public AuthCollection() { 034 this(new TreeSet<>()); 035 } 036 037 public AuthCollection(AuthMethod... authMethods) { 038 this(new TreeSet<>(Arrays.asList(authMethods))); 039 } 040 041 public AuthCollection(SortedSet<AuthMethod> authMethods) { 042 authList = authMethods; 043 } 044 045 public AuthCollection(UUID applicationId, byte[] privateKeyContents, String key, String secret, HashUtil.HashType hashType, String signature) { 046 this(); 047 048 if (key != null && secret == null && signature == null) { 049 throw new IllegalStateException( 050 "You must provide an API secret or signature secret in addition to your API key."); 051 } 052 if (secret != null && key == null) { 053 throw new IllegalStateException("You must provide an API key in addition to your API secret."); 054 } 055 if (signature != null && key == null) { 056 throw new IllegalStateException("You must provide an API key in addition to your signature secret."); 057 } 058 if (applicationId == null && privateKeyContents != null) { 059 throw new IllegalStateException("You must provide an application ID in addition to your private key."); 060 } 061 if (applicationId != null && privateKeyContents == null) { 062 throw new IllegalStateException("You must provide a private key in addition to your application id."); 063 } 064 065 authList.add(new NoAuthMethod()); 066 if (key != null && secret != null) { 067 authList.add(new ApiKeyHeaderAuthMethod(key, secret)); 068 authList.add(new ApiKeyQueryParamsAuthMethod(key, secret)); 069 } 070 if (key != null && signature != null) { 071 authList.add(new SignatureAuthMethod(key, signature, hashType)); 072 } 073 if (applicationId != null) { 074 authList.add(new JWTAuthMethod(applicationId.toString(), privateKeyContents)); 075 } 076 } 077 078 /** 079 * Add a new {@link AuthMethod} to the set managed by this AuthCollection. 080 * If an auth method of this type already exists, this method will replace it with 081 * the new provided value. 082 * 083 * @param auth AuthMethod method to be added to this collection. 084 */ 085 public void add(AuthMethod auth) { 086 authList.remove(auth); 087 authList.add(auth); 088 } 089 090 /** 091 * Obtain an AuthMethod of type T, if one is contained in this collection. 092 * 093 * @param type The type of AuthMethod to be located. 094 * @param <T> The type of AuthMethod which will be returned. 095 * 096 * @return An AuthMethod subclass matching type. 097 * 098 * @throws VonageUnacceptableAuthException if no matching AuthMethod is found. 099 */ 100 @SuppressWarnings("unchecked") 101 public <T extends AuthMethod> T getAuth(Class<T> type) throws VonageUnacceptableAuthException { 102 for (AuthMethod availableAuthMethod : authList) { 103 if (type.isInstance(availableAuthMethod)) { 104 return (T) availableAuthMethod; 105 } 106 } 107 throw new VonageUnacceptableAuthException(authList, Collections.singletonList(type)); 108 } 109 110 /** 111 * Obtain an {@link AuthMethod} instance for a set of acceptable AuthMethod classes. 112 * 113 * @param acceptableAuthMethodClasses A Set of AuthMethod classes which are suitable for the target REST endpoint. 114 * 115 * @return the preferred AuthMethod from the provided set of acceptable AuthMethod classes. 116 * 117 * @throws VonageUnacceptableAuthException if no appropriate AuthMethod is held by this AuthCollection. 118 */ 119 public AuthMethod getAcceptableAuthMethod(Set<Class<? extends AuthMethod>> acceptableAuthMethodClasses) throws VonageUnacceptableAuthException { 120 for (AuthMethod availableAuthMethod : authList) { 121 for (Class<? extends AuthMethod> acceptable : acceptableAuthMethodClasses) { 122 if (acceptable.isAssignableFrom(availableAuthMethod.getClass())) { 123 return availableAuthMethod; 124 } 125 } 126 } 127 throw new VonageUnacceptableAuthException(authList, acceptableAuthMethodClasses); 128 } 129 130 /** 131 * Utility method for determining whether a certain authentication method has been registered. 132 * 133 * @param authMethod The authentication method type. 134 * 135 * @return {@code true} if the specified auth method is available, {@code false} otherwise. 136 * 137 * @since 7.3.0 138 */ 139 public boolean hasAuthMethod(Class<? extends AuthMethod> authMethod) { 140 return authList.stream().map(AuthMethod::getClass).anyMatch(authMethod::equals); 141 } 142}