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