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