001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.bean.openapi3;
018
019import static org.apache.juneau.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.Shorts.*;
021import static org.apache.juneau.marshall.internal.ConverterUtils.*;
022
023import java.util.*;
024
025import org.apache.juneau.commons.collections.*;
026
027/**
028 * Configuration details for a supported OAuth Flow.
029 *
030 * <p>
031 * The OAuthFlow Object provides configuration details for a supported OAuth Flow. This object contains the URLs and
032 * scopes needed to configure a specific OAuth 2.0 flow. Different flows require different combinations of URLs and
033 * have different security characteristics.
034 *
035 * <h5 class='section'>OpenAPI Specification:</h5>
036 * <p>
037 * The OAuthFlow Object is composed of the following fields:
038 * <ul class='spaced-list'>
039 *    <li><c>authorizationUrl</c> (string) - The authorization URL to be used for this flow. This MUST be in the form of a URL
040 *    <li><c>tokenUrl</c> (string) - The token URL to be used for this flow. This MUST be in the form of a URL
041 *    <li><c>refreshUrl</c> (string) - The URL to be used for obtaining refresh tokens. This MUST be in the form of a URL
042 *    <li><c>scopes</c> (map of strings) - The available scopes for the OAuth2 security scheme. A map between the scope name and a short description for it
043 * </ul>
044 *
045 * <h5 class='section'>Example:</h5>
046 * <p class='bcode'>
047 *    <jc>// Construct using SwaggerBuilder.</jc>
048 *    OAuthFlow <jv>x</jv> = <jsm>oauthFlow</jsm>()
049 *       .setAuthorizationUrl(<js>"https://example.com/oauth/authorize"</js>)
050 *       .setTokenUrl(<js>"https://example.com/oauth/token"</js>)
051 *       .setScopes(<jsm>map</jsm>(<js>"read"</js>, <js>"Read access"</js>, <js>"write"</js>, <js>"Write access"</js>));
052 *
053 *    <jc>// Serialize using JsonSerializer.</jc>
054 *    String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>x</jv>);
055 *
056 *    <jc>// Or just use toString() which does the same as above.</jc>
057 *    <jv>json</jv> = <jv>x</jv>.toString();
058 * </p>
059 * <p class='bcode'>
060 *    <jc>// Output</jc>
061 *    {
062 *       <js>"authorizationUrl"</js>: <js>"https://example.com/oauth/authorize"</js>,
063 *       <js>"tokenUrl"</js>: <js>"https://example.com/oauth/token"</js>,
064 *       <js>"scopes"</js>: {
065 *          <js>"read"</js>: <js>"Read access"</js>,
066 *          <js>"write"</js>: <js>"Write access"</js>
067 *       }
068 *    }
069 * </p>
070 *
071 * <h5 class='section'>See Also:</h5><ul>
072 *    <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#oauth-flow-object">OpenAPI Specification &gt; OAuth Flow Object</a>
073 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/authentication/oauth2/">OpenAPI OAuth2 Authentication</a>
074 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
075 * </ul>
076 */
077@SuppressWarnings({
078   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
079})
080public class OAuthFlow extends OpenApiElement {
081
082   private String authorizationUrl;
083   private String tokenUrl;
084   private String refreshUrl;
085   private final Map<String,String> scopes = map();
086
087   /**
088    * Default constructor.
089    */
090   public OAuthFlow() {}
091
092   /**
093    * Copy constructor.
094    *
095    * @param copyFrom The object to copy.
096    */
097   public OAuthFlow(OAuthFlow copyFrom) {
098      super(copyFrom);
099
100      this.authorizationUrl = copyFrom.authorizationUrl;
101      this.tokenUrl = copyFrom.tokenUrl;
102      this.refreshUrl = copyFrom.refreshUrl;
103      scopes.putAll(copyFrom.scopes);
104   }
105
106   /**
107    * Adds a single value to the <property>scopes</property> property.
108    *
109    * @param name The scope name.  Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown.
110    * @param description A short description for the scope.  Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown.
111    * @return This object
112    */
113   public OAuthFlow addScope(String name, String description) {
114      reqnn("name", name);
115      reqnn("description", description);
116      scopes.put(name, description);
117      return this;
118   }
119
120   /**
121    * Make a deep copy of this object.
122    *
123    * @return A deep copy of this object.
124    */
125   public OAuthFlow copy() {
126      return new OAuthFlow(this);
127   }
128
129   @Override /* Overridden from OpenApiElement */
130   public <T> T get(String property, Class<T> type) {
131      reqnn("property", property);
132      return switch (property) {
133         case "refreshUrl" -> toType(getRefreshUrl(), type);
134         case "tokenUrl" -> toType(getTokenUrl(), type);
135         case "authorizationUrl" -> toType(getAuthorizationUrl(), type);
136         case "scopes" -> toType(getScopes(), type);
137         default -> super.get(property, type);
138      };
139   }
140
141   /**
142    * Bean property getter:  <property>authorizationUrl</property>.
143    *
144    * <p>
145    * The authorization URL to be used for this flow.
146    *
147    * @return The property value, or <jk>null</jk> if it is not set.
148    */
149   public String getAuthorizationUrl() { return authorizationUrl; }
150
151   /**
152    * Bean property getter:  <property>refreshUrl</property>.
153    *
154    * <p>
155    * The URL to be used for obtaining refresh tokens.
156    *
157    * @return The property value, or <jk>null</jk> if it is not set.
158    */
159   public String getRefreshUrl() { return refreshUrl; }
160
161   /**
162    * Bean property getter:  <property>scopes</property>.
163    *
164    * <p>
165    * The available scopes for the OAuth2 security scheme.
166    *
167    * @return The property value, or <jk>null</jk> if it is not set.
168    */
169   public Map<String,String> getScopes() { return nie(scopes); }
170
171   /**
172    * Bean property getter:  <property>tokenUrl</property>.
173    *
174    * <p>
175    * The token URL to be used for this flow.
176    *
177    * @return The property value, or <jk>null</jk> if it is not set.
178    */
179   public String getTokenUrl() { return tokenUrl; }
180
181   @Override /* Overridden from OpenApiElement */
182   public Set<String> keySet() {
183      // @formatter:off
184      var s = stb(String.class)
185         .addIf(nn(authorizationUrl), "authorizationUrl")
186         .addIf(nn(refreshUrl), "refreshUrl")
187         .addIf(ine(scopes), "scopes")
188         .addIf(nn(tokenUrl), "tokenUrl")
189         .build();
190      // @formatter:on
191      return new MultiSet<>(s, super.keySet());
192   }
193
194   @Override /* Overridden from OpenApiElement */
195   public OAuthFlow set(String property, Object value) {
196      reqnn("property", property);
197      return switch (property) {
198         case "authorizationUrl" -> setAuthorizationUrl(s(value));
199         case "refreshUrl" -> setRefreshUrl(s(value));
200         case "scopes" -> setScopes(toMapBuilder(value, String.class, String.class).sparse().build());
201         case "tokenUrl" -> setTokenUrl(s(value));
202         default -> {
203            super.set(property, value);
204            yield this;
205         }
206      };
207   }
208
209   /**
210    * Bean property setter:  <property>authorizationUrl</property>.
211    *
212    * <p>
213    * The authorization URL to be used for this flow.
214    *
215    * @param value
216    *    The new value for this property.
217    *    <br>Can be <jk>null</jk> to unset the property.
218    * @return This object
219    */
220   public OAuthFlow setAuthorizationUrl(String value) {
221      authorizationUrl = value;
222      return this;
223   }
224
225   /**
226    * Bean property setter:  <property>refreshUrl</property>.
227    *
228    * <p>
229    * The URL to be used for obtaining refresh tokens.
230    *
231    * @param value
232    *    The new value for this property.
233    *    <br>Can be <jk>null</jk> to unset the property.
234    * @return This object
235    */
236   public OAuthFlow setRefreshUrl(String value) {
237      refreshUrl = value;
238      return this;
239   }
240
241   /**
242    * Bean property setter:  <property>scopes</property>.
243    *
244    * <p>
245    * The available scopes for the OAuth2 security scheme.
246    *
247    * @param value
248    *    The new value for this property.
249    *    <br>Can be <jk>null</jk> to unset the property.
250    * @return This object
251    */
252   public OAuthFlow setScopes(Map<String,String> value) {
253      scopes.clear();
254      if (nn(value))
255         scopes.putAll(value);
256      return this;
257   }
258
259   /**
260    * Bean property setter:  <property>tokenUrl</property>.
261    * @param value
262    *    The new value for this property.
263    *    <br>Can be <jk>null</jk> to unset the property.
264    * @return This object
265    */
266   public OAuthFlow setTokenUrl(String value) {
267      tokenUrl = value;
268      return this;
269   }
270
271   @Override /* Overridden from OpenApiElement */
272   public OAuthFlow strict() {
273      super.strict();
274      return this;
275   }
276
277   @Override /* Overridden from OpenApiElement */
278   public OAuthFlow strict(Object value) {
279      super.strict(value);
280      return this;
281   }
282}