001/* 002 * oauth2-oidc-sdk 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.oauth2.sdk.id; 019 020 021import com.nimbusds.oauth2.sdk.util.StringUtils; 022import net.jcip.annotations.Immutable; 023 024import java.net.URI; 025import java.net.URISyntaxException; 026import java.util.Objects; 027 028 029/** 030 * Issuer identifier. 031 * 032 * <p>Valid issuer identifiers are URIs with "https" schema and no query or 033 * fragment component. 034 */ 035@Immutable 036public final class Issuer extends Identifier { 037 038 039 private static final long serialVersionUID = -8033463330193076151L; 040 041 042 /** 043 * Checks if the specified string represents a valid issuer identifier. 044 * This method is {@code null}-safe. 045 * 046 * @param value The issuer string. 047 * 048 * @return {@code true} if the string represents a valid issuer 049 * identifier, else {@code false}. 050 */ 051 public static boolean isValid(final String value) { 052 053 if (value == null) 054 return false; 055 056 try { 057 return isValid(new URI(value)); 058 059 } catch (URISyntaxException e) { 060 061 return false; 062 } 063 } 064 065 066 /** 067 * Checks if the specified issuer is a valid identifier. This method is 068 * {@code null}-safe. 069 * 070 * @param value The issuer. 071 * 072 * @return {@code true} if the value is a valid identifier, else 073 * {@code false}. 074 */ 075 public static boolean isValid(final Issuer value) { 076 077 if (value == null) 078 return false; 079 080 try { 081 return isValid(new URI(value.getValue())); 082 083 } catch (URISyntaxException e) { 084 085 return false; 086 } 087 } 088 089 090 /** 091 * Checks if the specified URI represents a valid issuer identifier. 092 * This method is {@code null}-safe. 093 * 094 * @param value The URI. 095 * 096 * @return {@code true} if the values represents a valid issuer 097 * identifier, else {@code false}. 098 */ 099 public static boolean isValid(final URI value) { 100 101 if (value == null) 102 return false; 103 104 if (value.getScheme() == null || ! value.getScheme().equalsIgnoreCase("https")) 105 return false; 106 107 if (value.getRawQuery() != null) 108 return false; 109 110 return value.getRawFragment() == null; 111 112 } 113 114 115 /** 116 * Creates a new issuer identifier with the specified value. 117 * 118 * @param value The issuer identifier value. Must not be {@code null} 119 * or empty string. 120 */ 121 public Issuer(final String value) { 122 123 super(value); 124 } 125 126 127 /** 128 * Creates a new issuer identifier with the specified URI value. 129 * 130 * @param value The URI value. Must not be {@code null}. 131 */ 132 public Issuer(final URI value) { 133 134 super(value.toString()); 135 } 136 137 138 /** 139 * Creates a new issuer identifier with the specified value. 140 * 141 * @param value The value. Must not be {@code null}. 142 */ 143 public Issuer(final Identifier value) { 144 145 super(value.getValue()); 146 } 147 148 149 /** 150 * Checks if this issuer is a valid identifier. This method is 151 * {@code null}-safe. 152 * 153 * @return {@code true} if the value is a valid identifier, else 154 * {@code false}. 155 */ 156 public boolean isValid() { 157 158 return Issuer.isValid(this); 159 } 160 161 162 @Override 163 public boolean equals(final Object object) { 164 if (this == object) return true; 165 if (object == null || getClass() != object.getClass()) return false; 166 167 Issuer other = (Issuer) object; 168 return Objects.equals(this.getValue(), other.getValue()); 169 } 170 171 172 @Override 173 public int hashCode() { 174 return Objects.hash(getValue()); 175 } 176 177 178 // Strip trailing slashes for equality check (no regex) 179 static String stripTrailingSlashes(final String s) { 180 int end = s.length(); 181 while (end > 0 && s.charAt(end - 1) == '/') { 182 end--; 183 } 184 return (end == s.length()) ? s : s.substring(0, end); 185 } 186 187 188 /** 189 * Compares this issuer's URI to another issuer's URI, ignoring any 190 * trailing slashes. If either issuer is {@link #isValid() invalid} this 191 * method falls back to the regular {@link #equals(Object)} method. 192 * 193 * @param other The other issuer, if {@code null} this method returns 194 * {@code false}. 195 * 196 * @return {@code true} if the URIs are equal, ignoring any trailing 197 * slashes, else {@code false}. 198 */ 199 public boolean equalsIgnoreTrailingSlash(final Issuer other) { 200 201 if (other == null) { 202 return false; 203 } 204 205 if (! isValid() || ! other.isValid()) { 206 // Issuer not URI or otherwise invalid, fall back to 207 // regular equals method 208 return equals(other); 209 } 210 // Strip trailing slash for equality check 211 try { 212 final URI thisURI = URI.create(stripTrailingSlashes(this.toString())); 213 final URI otherURI = URI.create(stripTrailingSlashes(other.toString())); 214 return thisURI.equals(otherURI); 215 } catch (IllegalArgumentException e) { 216 // Fall back to regular equals if URI creation fails 217 return equals(other); 218 } 219 } 220 221 222 /** 223 * Parses an issuer from the specified string. 224 * 225 * @param s The string to parse, {@code null} or empty if no issuer is 226 * specified. 227 * 228 * @return The issuer, {@code null} if the parsed string was 229 * {@code null} or empty. 230 */ 231 public static Issuer parse(final String s) { 232 233 if (StringUtils.isBlank(s)) 234 return null; 235 236 return new Issuer(s); 237 } 238}