1 /**
2  * Methods to generate `security.d`, allowing the client to use the security schemes in the OpenAPI
3  * Specification.
4  */
5 module openapi_client.security;
6 
7 import std.array : appender, split, Appender;
8 import std.file : mkdir, mkdirRecurse, write;
9 import std.path : buildNormalizedPath, dirName;
10 import std.string : tr;
11 import std.stdio : writeln;
12 
13 import openapi : OasDocument, OasServer, OasServerVariable, OasSecurityScheme;
14 import openapi_client.util : wordWrapText, toUpperCamelCase;
15 
16 
17 /**
18  * Writes a utility class containing information about the REST API servers to contact.
19  */
20 void writeSecurityFiles(OasDocument oasDocument, string targetDir, string packageRoot) {
21   auto buffer = appender!string();
22   string moduleName = packageRoot ~ ".security";
23   with (buffer) {
24     put("// File automatically generated from OpenAPI spec.\n");
25     put("module " ~ moduleName ~ ";\n");
26     put("\n");
27     put("import openapi_client.apirequest : ApiRequest;\n");
28     put("import openapi;\n");
29     put("\n");
30     put("import std.base64 : Base64;\n");
31     put("import std.conv : to;\n");
32     put("import std.array : Appender;\n");
33     put("\n");
34     put("class Security {\n\n");
35     put("  /**\n");
36     put("   * The currently active security method to be applied to requests.\n");
37     put("   */\n");
38     put("  static void delegate(ApiRequest) applySecurityF = null;\n\n");
39     foreach (string securitySchemeName, OasSecurityScheme securityScheme;
40         oasDocument.components.securitySchemes) {
41       if (securityScheme.type == "http") {
42         if (securityScheme.scheme == "basic") {
43           put("  /**\n");
44           put("   * Enable and configure the use of HTTP Basic Authentication.\n");
45           put("   *\n");
46           put("   * See_Also: https://en.wikipedia.org/wiki/Basic_access_authentication\n");
47           put("   */\n");
48           put("  static void configure" ~ toUpperCamelCase(securitySchemeName)
49               ~ "(string username, string password) {\n");
50           put("    applySecurityF = (ApiRequest request) {\n");
51           put("      auto buffer = new Appender!string();\n");
52           put("      auto credentials = cast(immutable(ubyte)[])(username ~ \":\" ~ password);\n");
53           put("      Base64.encode(credentials, buffer);\n");
54           put("      request.setHeaderParam(\"Authorization\", \"Basic \" ~ buffer.data);\n");
55           put("    };\n");
56           put("  }\n\n");
57         } else if (securityScheme.scheme == "bearer") {
58           put("  /**\n");
59           put("   * Enable and configure the use of an OAuth2.0 Bearer token for HTTP Authentication.\n");
60           put("   *\n");
61           put("   * See_Also: https://datatracker.ietf.org/doc/html/rfc6750\n");
62           put("   */\n");
63           put("  static void configure" ~ toUpperCamelCase(securitySchemeName)
64               ~ "(string token) {\n");
65           put("    applySecurityF = (ApiRequest request) {\n");
66           put("      request.setHeaderParam(\"Authorization\", \"Bearer \" ~ token);\n");
67           put("    };\n");
68           put("  }\n\n");
69         } else {
70           writeln("Warning: Unsupported security scheme type=http, scheme=", securityScheme.scheme);
71         }
72       } else {
73         writeln("Warning: Unsupported security scheme type '", securityScheme.type, "'.");
74       }
75     }
76     put("  /**\n");
77     put("   * Apply the currently selected security method to a request.\n");
78     put("   */\n");
79     put("  static void apply(ApiRequest request) {\n");
80     put("    if (applySecurityF !is null)\n");
81     put("      applySecurityF(request);\n");
82     put("  }\n\n");
83     put("}\n");
84   }
85   string fileName = buildNormalizedPath(targetDir, tr(moduleName, ".", "/") ~ ".d");
86   mkdirRecurse(dirName(fileName));
87   write(fileName, buffer[]);
88 }