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}