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.swagger;
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.CollectionUtils.copyOf;
023import static org.apache.juneau.internal.ConverterUtils.*;
024
025import java.util.*;
026
027import org.apache.juneau.*;
028import org.apache.juneau.common.utils.*;
029import org.apache.juneau.internal.*;
030import org.apache.juneau.marshaller.*;
031
032/**
033 * Allows the definition of a security scheme that can be used by the operations.
034 *
035 * <p>
036 * The Security Scheme Object defines a security scheme that can be used by the operations in Swagger 2.0. 
037 * Supported schemes are basic authentication, an API key (either as a header or as a query parameter) and OAuth2's
038 * common flows (implicit, password, application and access code).
039 *
040 * <h5 class='section'>Swagger Specification:</h5>
041 * <p>
042 * The Security Scheme Object is composed of the following fields:
043 * <ul class='spaced-list'>
044 *    <li><c>type</c> (string, REQUIRED) - The type of the security scheme. Values: <js>"basic"</js>, <js>"apiKey"</js>, <js>"oauth2"</js>
045 *    <li><c>description</c> (string) - A short description for security scheme
046 *    <li><c>name</c> (string) - The name of the header or query parameter to be used (for <js>"apiKey"</js> type)
047 *    <li><c>in</c> (string) - The location of the API key (for <js>"apiKey"</js> type). Values: <js>"query"</js>, <js>"header"</js>
048 *    <li><c>flow</c> (string) - The flow used by the OAuth2 security scheme (for <js>"oauth2"</js> type). Values: <js>"implicit"</js>, <js>"password"</js>, <js>"application"</js>, <js>"accessCode"</js>
049 *    <li><c>authorizationUrl</c> (string) - The authorization URL to be used for this flow (for <js>"oauth2"</js> type)
050 *    <li><c>tokenUrl</c> (string) - The token URL to be used for this flow (for <js>"oauth2"</js> type)
051 *    <li><c>scopes</c> (map of string) - The available scopes for the OAuth2 security scheme (for <js>"oauth2"</js> type)
052 * </ul>
053 *
054 * <h5 class='section'>Example:</h5>
055 * <p class='bjson'>
056 *    <jc>// Basic authentication sample</jc>
057 *    {
058 *       <js>"type"</js>: <js>"basic"</js>
059 *    }
060 *
061 *    <jc>// API key sample</jc>
062 *    {
063 *       <js>"type"</js>: <js>"apiKey"</js>,
064 *       <js>"name"</js>: <js>"api_key"</js>,
065 *       <js>"in"</js>: <js>"header"</js>
066 *    }
067 *
068 *    <jc>// Implicit OAuth2 sample</jc>
069 *    {
070 *       <js>"type"</js>: <js>"oauth2"</js>,
071 *       <js>"authorizationUrl"</js>: <js>"http://swagger.io/api/oauth/dialog"</js>,
072 *       <js>"flow"</js>: <js>"implicit"</js>,
073 *       <js>"scopes"</js>: {
074 *          <js>"write:pets"</js>: <js>"modify pets in your account"</js>,
075 *          <js>"read:pets"</js>: <js>"read your pets"</js>
076 *       }
077 *    }
078 * </p>
079 *
080 * <h5 class='section'>See Also:</h5><ul>
081 *    <li class='link'><a class="doclink" href="https://swagger.io/specification/v2/#security-scheme-object">Swagger 2.0 Specification &gt; Security Scheme Object</a>
082 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/2-0/authentication/">Swagger Authentication</a>
083 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanSwagger2">juneau-bean-swagger-v2</a>
084 * </ul>
085 */
086public class SecurityScheme extends SwaggerElement {
087
088   private static final String[] VALID_TYPES = {"basic", "apiKey", "oauth2"};
089
090   private String
091      type,
092      description,
093      name,
094      in,
095      flow,
096      authorizationUrl,
097      tokenUrl;
098   private Map<String,String> scopes;
099
100   /**
101    * Default constructor.
102    */
103   public SecurityScheme() {}
104
105   /**
106    * Copy constructor.
107    *
108    * @param copyFrom The object to copy.
109    */
110   public SecurityScheme(SecurityScheme copyFrom) {
111      super(copyFrom);
112
113      this.authorizationUrl = copyFrom.authorizationUrl;
114      this.description = copyFrom.description;
115      this.flow = copyFrom.flow;
116      this.in = copyFrom.in;
117      this.name = copyFrom.name;
118      this.scopes = copyOf(copyFrom.scopes);
119      this.tokenUrl = copyFrom.tokenUrl;
120      this.type = copyFrom.type;
121   }
122
123   /**
124    * Make a deep copy of this object.
125    *
126    * @return A deep copy of this object.
127    */
128   public SecurityScheme copy() {
129      return new SecurityScheme(this);
130   }
131
132   @Override /* Overridden from SwaggerElement */
133   public SecurityScheme strict() {
134      super.strict();
135      return this;
136   }
137
138   /**
139    * Sets strict mode on this bean.
140    *
141    * @param value
142    *    The new value for this property.
143    *    <br>Non-boolean values will be converted to boolean using <code>Boolean.<jsm>valueOf</jsm>(value.toString())</code>.
144    *    <br>Can be <jk>null</jk> (interpreted as <jk>false</jk>).
145    * @return This object.
146    */
147   @Override
148   public SecurityScheme strict(Object value) {
149      super.strict(value);
150      return this;
151   }
152
153   //-----------------------------------------------------------------------------------------------------------------
154   // Properties
155   //-----------------------------------------------------------------------------------------------------------------
156
157   /**
158    * Bean property getter:  <property>authorizationUrl</property>.
159    *
160    * <p>
161    * The authorization URL to be used for this flow.
162    *
163    * @return The property value, or <jk>null</jk> if it is not set.
164    */
165   public String getAuthorizationUrl() {
166      return authorizationUrl;
167   }
168
169   /**
170    * Bean property setter:  <property>authorizationUrl</property>.
171    *
172    * <p>
173    * The authorization URL to be used for this flow.
174    *
175    * @param value
176    *    The new value for this property.
177    *    <br>This SHOULD be in the form of a URL.
178    *    <br>Can be <jk>null</jk> to unset the property.
179    * @return This object.
180    */
181   public SecurityScheme setAuthorizationUrl(String value) {
182      authorizationUrl = value;
183      return this;
184   }
185
186   /**
187    * Bean property getter:  <property>description</property>.
188    *
189    * <p>
190    * A short description for security scheme.
191    *
192    * @return The property value, or <jk>null</jk> if it is not set.
193    */
194   public String getDescription() {
195      return description;
196   }
197
198   /**
199    * Bean property setter:  <property>description</property>.
200    *
201    * <p>
202    * A short description for security scheme.
203    *
204    * @param value
205    *    The new value for this property.
206    *    <br>Can be <jk>null</jk> to unset the property.
207    * @return This object.
208    */
209   public SecurityScheme setDescription(String value) {
210      description = value;
211      return this;
212   }
213
214   /**
215    * Bean property getter:  <property>flow</property>.
216    *
217    * <p>
218    * The flow used by the OAuth2 security scheme.
219    *
220    * @return The property value, or <jk>null</jk> if it is not set.
221    */
222   public String getFlow() {
223      return flow;
224   }
225
226   /**
227    * Bean property setter:  <property>flow</property>.
228    *
229    * <p>
230    * The flow used by the OAuth2 security scheme.
231    *
232    * @param value
233    *    The new value for this property.
234    *    <br>Valid values:
235    *    <ul>
236    *       <li><js>"implicit"</js>
237    *       <li><js>"password"</js>
238    *       <li><js>"application"</js>
239    *       <li><js>"accessCode"</js>
240    *    </ul>
241    *    <br>Can be <jk>null</jk> to unset the property.
242    * @return This object.
243    */
244   public SecurityScheme setFlow(String value) {
245      flow = value;
246      return this;
247   }
248
249   /**
250    * Bean property getter:  <property>in</property>.
251    *
252    * <p>
253    * The location of the API key.
254    *
255    * @return The property value, or <jk>null</jk> if it is not set.
256    */
257   public String getIn() {
258      return in;
259   }
260
261   /**
262    * Bean property setter:  <property>in</property>.
263    *
264    * <p>
265    * The location of the API key.
266    *
267    * @param value
268    *    The new value for this property.
269    *    <br>Valid values:
270    *    <ul>
271    *       <li><js>"query"</js>
272    *       <li><js>"header"</js>
273    *    </ul>
274    *    <br>Can be <jk>null</jk> to unset the property.
275    * @return This object.
276    */
277   public SecurityScheme setIn(String value) {
278      in = value;
279      return this;
280   }
281
282   /**
283    * Bean property getter:  <property>name</property>.
284    *
285    * <p>
286    * The name of the header or query parameter to be used.
287    *
288    * @return The property value, or <jk>null</jk> if it is not set.
289    */
290   public String getName() {
291      return name;
292   }
293
294   /**
295    * Bean property setter:  <property>name</property>.
296    *
297    * <p>
298    * The name of the header or query parameter to be used.
299    *
300    * @param value
301    *    The new value for this property.
302    *    <br>Can be <jk>null</jk> to unset the property.
303    * @return This object.
304    */
305   public SecurityScheme setName(String value) {
306      name = value;
307      return this;
308   }
309
310   /**
311    * Bean property getter:  <property>scopes</property>.
312    *
313    * <p>
314    * The available scopes for the OAuth2 security scheme.
315    *
316    * @return The property value, or <jk>null</jk> if it is not set.
317    */
318   public Map<String,String> getScopes() {
319      return scopes;
320   }
321
322   /**
323    * Bean property setter:  <property>scopes</property>.
324    *
325    * <p>
326    * The available scopes for the OAuth2 security scheme.
327    *
328    * @param value
329    *    The new value for this property.
330    *    <br>Can be <jk>null</jk> to unset the property.
331    * @return This object.
332    */
333   public SecurityScheme setScopes(Map<String,String> value) {
334      scopes = copyOf(value);
335      return this;
336   }
337
338   /**
339    * Bean property appender:  <property>scopes</property>.
340    *
341    * <p>
342    * The available scopes for the OAuth2 security scheme.
343    *
344    * @param key The scope key.  Must not be <jk>null</jk>.
345    * @param value The scope value.  Must not be <jk>null</jk>.
346    * @return This object.
347    */
348   public SecurityScheme addScope(String key, String value) {
349      assertArgNotNull("key", key);
350      assertArgNotNull("value", value);
351      scopes = mapBuilder(scopes).sparse().add(key, value).build();
352      return this;
353   }
354
355   /**
356    * Bean property getter:  <property>tokenUrl</property>.
357    *
358    * <p>
359    * The token URL to be used for this flow.
360    *
361    * @return The property value, or <jk>null</jk> if it is not set.
362    */
363   public String getTokenUrl() {
364      return tokenUrl;
365   }
366
367   /**
368    * Bean property setter:  <property>tokenUrl</property>.
369    *
370    * <p>
371    * The token URL to be used for this flow.
372    *
373    * @param value
374    *    The new value for this property.
375    *    <br>This SHOULD be in the form of a URL.
376    *    <br>Can be <jk>null</jk> to unset the property.
377    * @return This object.
378    */
379   public SecurityScheme setTokenUrl(String value) {
380      tokenUrl = value;
381      return this;
382   }
383
384   /**
385    * Bean property getter:  <property>type</property>.
386    *
387    * <p>
388    * The type of the security scheme.
389    *
390    * @return The property value, or <jk>null</jk> if it is not set.
391    */
392   public String getType() {
393      return type;
394   }
395
396   /**
397    * Bean property setter:  <property>type</property>.
398    *
399    * <p>
400    * The type of the security scheme.
401    *
402    * @param value
403    *    The new value for this property.
404    *    <br>Valid values:
405    *    <ul>
406    *       <li><js>"basic"</js>
407    *       <li><js>"apiKey"</js>
408    *       <li><js>"oauth2"</js>
409    *    </ul>
410    *    <br>Property value is required.
411    *    <br>Can be <jk>null</jk> to unset the property.
412    * @return This object.
413    */
414   public SecurityScheme setType(String value) {
415      if (isStrict() && ! contains(value, VALID_TYPES))
416         throw new BasicRuntimeException(
417            "Invalid value passed in to setType(String).  Value=''{0}'', valid values={1}",
418            value, Json5.of(VALID_TYPES)
419         );
420      type = value;
421      return this;
422   }
423
424   @Override /* Overridden from SwaggerElement */
425   public <T> T get(String property, Class<T> type) {
426      assertArgNotNull("property", property);
427      return switch (property) {
428         case "authorizationUrl" -> toType(getAuthorizationUrl(), type);
429         case "description" -> toType(getDescription(), type);
430         case "flow" -> toType(getFlow(), type);
431         case "in" -> toType(getIn(), type);
432         case "name" -> toType(getName(), type);
433         case "scopes" -> toType(getScopes(), type);
434         case "tokenUrl" -> toType(getTokenUrl(), type);
435         case "type" -> toType(getType(), type);
436         default -> super.get(property, type);
437      };
438   }
439
440   @Override /* Overridden from SwaggerElement */
441   public SecurityScheme set(String property, Object value) {
442      assertArgNotNull("property", property);
443      return switch (property) {
444         case "authorizationUrl" -> setAuthorizationUrl(Utils.s(value));
445         case "description" -> setDescription(Utils.s(value));
446         case "flow" -> setFlow(Utils.s(value));
447         case "in" -> setIn(Utils.s(value));
448         case "name" -> setName(Utils.s(value));
449         case "scopes" -> setScopes(mapBuilder(String.class,String.class).sparse().addAny(value).build());
450         case "tokenUrl" -> setTokenUrl(Utils.s(value));
451         case "type" -> setType(Utils.s(value));
452         default -> {
453            super.set(property, value);
454            yield this;
455         }
456      };
457   }
458
459   @Override /* Overridden from SwaggerElement */
460   public Set<String> keySet() {
461      var s = setBuilder(String.class)
462         .addIf(authorizationUrl != null, "authorizationUrl")
463         .addIf(description != null, "description")
464         .addIf(flow != null, "flow")
465         .addIf(in != null, "in")
466         .addIf(name != null, "name")
467         .addIf(scopes != null, "scopes")
468         .addIf(tokenUrl != null, "tokenUrl")
469         .addIf(type != null, "type")
470         .build();
471      return new MultiSet<>(s, super.keySet());
472   }
473
474}