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.common.utils.Utils.*;
020import static org.apache.juneau.internal.ArrayUtils.contains;
021import static org.apache.juneau.internal.CollectionUtils.*;
022import static org.apache.juneau.internal.ConverterUtils.*;
023
024import java.util.*;
025
026import org.apache.juneau.*;
027import org.apache.juneau.common.utils.*;
028import org.apache.juneau.internal.*;
029
030/**
031 * Defines a security scheme that can be used by the operations.
032 *
033 * <p>
034 * The Security Scheme Object defines a security scheme that can be used by the operations. Supported schemes are 
035 * HTTP authentication, an API key (either as a header or as a query parameter), OAuth2's common flows (implicit, 
036 * password, client credentials and authorization code) as defined in RFC6749, and OpenID Connect Discovery.
037 *
038 * <h5 class='section'>OpenAPI Specification:</h5>
039 * <p>
040 * The Security Scheme Object is composed of the following fields:
041 * <ul class='spaced-list'>
042 *    <li><c>type</c> (string, REQUIRED) - The type of the security scheme. Values: <js>"apiKey"</js>, <js>"http"</js>, <js>"oauth2"</js>, <js>"openIdConnect"</js>
043 *    <li><c>description</c> (string) - A short description for security scheme (CommonMark syntax may be used)
044 *    <li><c>name</c> (string) - The name of the header, query or cookie parameter to be used (for <js>"apiKey"</js> type)
045 *    <li><c>in</c> (string) - The location of the API key (for <js>"apiKey"</js> type). Values: <js>"query"</js>, <js>"header"</js>, <js>"cookie"</js>
046 *    <li><c>scheme</c> (string) - The name of the HTTP Authorization scheme to be used in the Authorization header (for <js>"http"</js> type)
047 *    <li><c>bearerFormat</c> (string) - A hint to the client to identify how the bearer token is formatted (for <js>"http"</js> type with <js>"bearer"</js> scheme)
048 *    <li><c>flows</c> ({@link OAuthFlows}) - An object containing configuration information for the flow types supported (for <js>"oauth2"</js> type)
049 *    <li><c>openIdConnectUrl</c> (string) - OpenId Connect URL to discover OAuth2 configuration values (for <js>"openIdConnect"</js> type)
050 * </ul>
051 *
052 * <h5 class='section'>Example:</h5>
053 * <p class='bjava'>
054 *    <jc>// Create an API key security scheme</jc>
055 *    SecuritySchemeInfo <jv>scheme</jv> = <jk>new</jk> SecuritySchemeInfo()
056 *       .setType(<js>"apiKey"</js>)
057 *       .setDescription(<js>"API key authentication"</js>)
058 *       .setName(<js>"X-API-Key"</js>)
059 *       .setIn(<js>"header"</js>);
060 * </p>
061 * <p class='bjava'>
062 *    <jc>// Create an OAuth2 security scheme</jc>
063 *    SecuritySchemeInfo <jv>oauthScheme</jv> = <jk>new</jk> SecuritySchemeInfo()
064 *       .setType(<js>"oauth2"</js>)
065 *       .setDescription(<js>"OAuth2 authentication"</js>)
066 *       .setFlows(
067 *          <jk>new</jk> OAuthFlows()
068 *             .setAuthorizationCode(
069 *                <jk>new</jk> OAuthFlow()
070 *                   .setAuthorizationUrl(<js>"https://example.com/oauth/authorize"</js>)
071 *                   .setTokenUrl(<js>"https://example.com/oauth/token"</js>)
072 *                   .setScopes(
073 *                      JsonMap.<jsm>of</jsm>(
074 *                         <js>"read"</js>, <js>"Read access to resources"</js>,
075 *                         <js>"write"</js>, <js>"Write access to resources"</js>
076 *                      )
077 *                   )
078 *             )
079 *       );
080 * </p>
081 *
082 * <h5 class='section'>See Also:</h5><ul>
083 *    <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#security-scheme-object">OpenAPI Specification &gt; Security Scheme Object</a>
084 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/authentication/">OpenAPI Authentication</a>
085 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
086 * </ul>
087 */
088public class SecuritySchemeInfo extends OpenApiElement {
089
090   private static final String[] VALID_IN = {"query", "header", "cookie"};
091   private static final String[] VALID_TYPES = {"apiKey", "http", "oauth2", "openIdConnect"};
092
093   private String
094      type,
095      description,
096      name,
097      in,
098      scheme,
099      bearerFormat,
100      openIdConnectUrl;
101
102   private OAuthFlow flows;
103
104   /**
105    * Default constructor.
106    */
107   public SecuritySchemeInfo() {}
108
109   /**
110    * Copy constructor.
111    *
112    * @param copyFrom The object to copy.
113    */
114   public SecuritySchemeInfo(SecuritySchemeInfo copyFrom) {
115      super(copyFrom);
116
117      this.name = copyFrom.name;
118      this.in = copyFrom.in;
119      this.description = copyFrom.description;
120      this.type = copyFrom.type;
121      this.scheme = copyFrom.scheme;
122      this.bearerFormat = copyFrom.bearerFormat;
123      this.openIdConnectUrl = copyFrom.openIdConnectUrl;
124      this.flows = copyFrom.flows;
125   }
126
127   /**
128    * Make a deep copy of this object.
129    *
130    * @return A deep copy of this object.
131    */
132   public SecuritySchemeInfo copy() {
133      return new SecuritySchemeInfo(this);
134   }
135
136   @Override /* SwaggerElement */
137   protected SecuritySchemeInfo strict() {
138      super.strict();
139      return this;
140   }
141
142   @Override /* Overridden from OpenApiElement */
143   public SecuritySchemeInfo strict(Object value) {
144      super.strict(value);
145      return this;
146   }
147
148   /**
149    * Bean property getter:  <property>name</property>.
150    *
151    * <p>
152    * The name of the parameter.
153    *
154    * <h5 class='section'>Notes:</h5>
155    * <ul class='spaced-list'>
156    *    <li>
157    *       Parameter names are case sensitive.
158    *    <li>
159    *       If <code>in</code> is <js>"path"</js>, the <code>name</code> field MUST correspond to the associated path segment
160    *       from the <code>path</code> field in the paths object.
161    *    <li>
162    *       For all other cases, the name corresponds to the parameter name used based on the <code>in</code> property.
163    * </ul>
164    *
165    * @return The property value, or <jk>null</jk> if it is not set.
166    */
167   public String getName() {
168      return name;
169   }
170
171   /**
172    * Bean property setter:  <property>name</property>.
173    *
174    * <p>
175    * The name of the parameter.
176    *
177    * <h5 class='section'>Notes:</h5>
178    * <ul class='spaced-list'>
179    *    <li>
180    *       Parameter names are case sensitive.
181    *    <li>
182    *       If <code>in</code> is <js>"path"</js>, the <code>name</code> field MUST correspond to the associated path segment
183    *       from the <code>path</code> field in the paths object.
184    *    <li>
185    *       For all other cases, the name corresponds to the parameter name used based on the <code>in</code> property.
186    * </ul>
187    *
188    * @param value
189    *    The new value for this property.
190    *    <br>Property value is required.
191    *    <br>Can be <jk>null</jk> to unset the property.
192    * @return This object
193    */
194   public SecuritySchemeInfo setName(String value) {
195      name = value;
196      return this;
197   }
198
199   /**
200    * Bean property getter:  <property>in</property>.
201    *
202    * <p>
203    * The location of the parameter.
204    *
205    * @return The property value, or <jk>null</jk> if it is not set.
206    */
207   public String getIn() {
208      return in;
209   }
210
211   /**
212    * Bean property setter:  <property>in</property>.
213    *
214    * <p>
215    * The location of the parameter.
216    *
217    * @param value
218    *    The new value for this property.
219    *    <br>Valid values:
220    *    <ul>
221    *       <li><js>"query"</js>
222    *       <li><js>"header"</js>
223    *       <li><js>"path"</js>
224    *       <li><js>"formData"</js>
225    *       <li><js>"body"</js>
226    *    </ul>
227    *    <br>Property value is required.
228    *    <br>Can be <jk>null</jk> to unset the property.
229    * @return This object
230    */
231   public SecuritySchemeInfo setIn(String value) {
232      if (isStrict() && ! contains(value, VALID_IN))
233         throw new BasicRuntimeException(
234            "Invalid value passed in to setIn(String).  Value=''{0}'', valid values={1}",
235            value, VALID_IN
236         );
237      in = value;
238      return this;
239   }
240
241   /**
242    * Bean property getter:  <property>description</property>.
243    *
244    * <p>
245    * A brief description of the parameter.
246    * <br>This could contain examples of use.
247    *
248    * @return The property value, or <jk>null</jk> if it is not set.
249    */
250   public String getDescription() {
251      return description;
252   }
253
254   /**
255    * Bean property setter:  <property>description</property>.
256    *
257    * <p>
258    * A brief description of the parameter.
259    * <br>This could contain examples of use.
260    *
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 SecuritySchemeInfo setDescription(String value) {
267      description = value;
268      return this;
269   }
270
271   /**
272    * Bean property getter:  <property>schema</property>.
273    *
274    * <p>
275    * The schema defining the type used for the body parameter.
276    *
277    * @return The property value, or <jk>null</jk> if it is not set.
278    */
279   public String getScheme() {
280      return scheme;
281   }
282
283   /**
284    * Bean property setter:  <property>schema</property>.
285    *
286    * <p>
287    * The schema defining the type used for the body parameter.
288    *
289    * @param value
290    *    The new value for this property.
291    *    <br>Property value is required.
292    *    <br>Can be <jk>null</jk> to unset the property.
293    * @return This object
294    */
295   public SecuritySchemeInfo setScheme(String value) {
296      scheme = value;
297      return this;
298   }
299
300   /**
301    * Bean property getter:  <property>type</property>.
302    *
303    * <p>
304    * The type of the parameter.
305    *
306    * @return The property value, or <jk>null</jk> if it is not set.
307    */
308   public String getType() {
309      return type;
310   }
311
312   /**
313    * Bean property setter:  <property>type</property>.
314    *
315    * <p>
316    * The type of the parameter.
317    *
318    * @param value
319    *    The new value for this property.
320    *    <br>Valid values:
321    *    <ul>
322    *       <li><js>"string"</js>
323    *       <li><js>"number"</js>
324    *       <li><js>"integer"</js>
325    *       <li><js>"boolean"</js>
326    *       <li><js>"array"</js>
327    *       <li><js>"file"</js>
328    *    </ul>
329    *    <br>If type is <js>"file"</js>, the <code>consumes</code> MUST be either <js>"multipart/form-data"</js>, <js>"application/x-www-form-urlencoded"</js>
330    *       or both and the parameter MUST be <code>in</code> <js>"formData"</js>.
331    *    <br>Property value is required.
332    *    <br>Can be <jk>null</jk> to unset the property.
333    * @return This object
334    */
335   public SecuritySchemeInfo setType(String value) {
336      if (isStrict() && ! contains(value, VALID_TYPES))
337         throw new BasicRuntimeException(
338            "Invalid value passed in to setType(String).  Value=''{0}'', valid values={1}",
339            value, VALID_TYPES
340         );
341      type = value;
342      return this;
343   }
344
345   /**
346    * Bean property getter:  <property>format</property>.
347    *
348    * <p>
349    * The extending format for the previously mentioned type.
350    *
351    * @return The property value, or <jk>null</jk> if it is not set.
352    */
353   public String getBearerFormat() {
354      return bearerFormat;
355   }
356
357   /**
358    * Bean property setter:  <property>format</property>.
359    *
360    * <p>
361    * The extending format for the previously mentioned type.
362    *
363    * @param value The new value for this property.
364    *    <br>Can be <jk>null</jk> to unset the property.
365    * @return This object
366    */
367   public SecuritySchemeInfo setBearerFormat(String value) {
368      bearerFormat = value;
369      return this;
370   }
371
372   /**
373    * Bean property getter:  <property>items</property>.
374    *
375    * <p>
376    * Describes the type of items in the array.
377    *
378    * @return The property value, or <jk>null</jk> if it is not set.
379    */
380   public OAuthFlow getFlows() {
381      return flows;
382   }
383
384   /**
385    * Bean property setter:  <property>items</property>.
386    *
387    * <p>
388    * Describes the type of items in the array.
389    *
390    * @param value
391    *    The new value for this property.
392    *    <br>Property value is required if <code>type</code> is <js>"array"</js>.
393    *    <br>Can be <jk>null</jk> to unset the property.
394    * @return This object
395    */
396   public SecuritySchemeInfo setFlows(OAuthFlow value) {
397      flows = value;
398      return this;
399   }
400
401   /**
402    * Bean property getter:  <property>collectionFormat</property>.
403    *
404    * <p>
405    * Determines the format of the array if type array is used.
406    *
407    * @return The property value, or <jk>null</jk> if it is not set.
408    */
409   public String getOpenIdConnectUrl() {
410      return openIdConnectUrl;
411   }
412
413   /**
414    * Bean property setter:  <property>collectionFormat</property>.
415    *
416    * <p>
417    * Determines the format of the array if type array is used.
418    *
419    * @param value The new value for this property.
420    *    <br>Can be <jk>null</jk> to unset the property.
421    * @return This object
422    */
423   public SecuritySchemeInfo setOpenIdConnectUrl(String value) {
424      openIdConnectUrl = value;
425      return this;
426   }
427
428   @Override /* SwaggerElement */
429   public <T> T get(String property, Class<T> type) {
430      assertArgNotNull("property", property);
431      return switch (property) {
432         case "name" -> toType(getName(), type);
433         case "in" -> toType(getIn(), type);
434         case "description" -> toType(getDescription(), type);
435         case "scheme" -> toType(getScheme(), type);
436         case "flows" -> toType(getFlows(), type);
437         case "bearerFormat" -> toType(getBearerFormat(), type);
438         case "openIdConnectUrl" -> toType(getOpenIdConnectUrl(), type);
439         case "type" -> toType(getType(), type);
440         default -> super.get(property, type);
441      };
442   }
443
444   @Override /* SwaggerElement */
445   public SecuritySchemeInfo set(String property, Object value) {
446      assertArgNotNull("property", property);
447      return switch (property) {
448         case "bearerFormat" -> setBearerFormat(Utils.s(value));
449         case "description" -> setDescription(Utils.s(value));
450         case "flows" -> setFlows(toType(value, OAuthFlow.class));
451         case "in" -> setIn(Utils.s(value));
452         case "name" -> setName(Utils.s(value));
453         case "openIdConnectUrl" -> setOpenIdConnectUrl(Utils.s(value));
454         case "scheme" -> setScheme(Utils.s(value));
455         case "type" -> setType(Utils.s(value));
456         default -> {
457            super.set(property, value);
458            yield this;
459         }
460      };
461   }
462
463   @Override /* SwaggerElement */
464   public Set<String> keySet() {
465      var s = setBuilder(String.class)
466         .addIf(bearerFormat != null, "bearerFormat")
467         .addIf(description != null, "description")
468         .addIf(flows != null, "flows")
469         .addIf(in != null, "in")
470         .addIf(name != null, "name")
471         .addIf(openIdConnectUrl != null, "openIdConnectUrl")
472         .addIf(scheme != null, "scheme")
473         .addIf(type != null, "type")
474         .build();
475      return new MultiSet<>(s, super.keySet());
476   }
477}