Skip to content

Commit

Permalink
Feat: Ability to assign security schemas (#110)
Browse files Browse the repository at this point in the history
* Additional types of SecuritySchemeObject.

* Adjusting the scopes in SecuritySchemeObject and OAuthFlowObject.

* Ref: moving to a separate file.

* Distributing by types.

* Distributing OAuthFlowsObject.

* Add security schemas to the operations with references.

* Server level security and changes to config.

* Fix: not applying security to emission.

* Ref: globalSecurity, testing.

* Ref: simpler, simple config has only global security.

* Ref: consistency: config sets defaults, NS is always complete.

* Readme: feature, Changelog: 1.2.0.

* Removing logging in test.

* 1.2.0-beta1
  • Loading branch information
RobinTail authored Apr 27, 2024
1 parent 32b67d5 commit 550d54e
Show file tree
Hide file tree
Showing 15 changed files with 367 additions and 52 deletions.
44 changes: 44 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,50 @@

## Version 1

### v1.2.0

- Ability to describe security schemas on the server and namespace level:
- These security schemas go directly to the generated documentation.

```ts
// Single namespace
import { createSimpleConfig } from "zod-sockets";

const config = createSimpleConfig({
security: [
{
type: "httpApiKey",
description: "Server security schema",
in: "header",
name: "X-Api-Key",
},
],
});
```

```ts
// Multiple namespaces
import { Config } from "zod-sockets";

const config = new Config({
security: [
{
type: "httpApiKey",
description: "Server security schema",
in: "header",
name: "X-Api-Key",
},
],
}).addNamespace({
security: [
{
type: "userPassword",
description: "Namespace security schema",
},
],
});
```

### v1.1.0

- Supporting Node 22.
Expand Down
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -613,3 +613,47 @@ const action = factory
.example("input", ["example payload"])
.example("output", ["example acknowledgement"]);
```

### Adding security schemas to the documentation

You can describe the security schemas for the generated documentation both on
server and namespace levels.

```ts
// Single namespace
import { createSimpleConfig } from "zod-sockets";

const config = createSimpleConfig({
security: [
{
type: "httpApiKey",
description: "Server security schema",
in: "header",
name: "X-Api-Key",
},
],
});
```

```ts
// Multiple namespaces
import { Config } from "zod-sockets";

const config = new Config({
security: [
{
type: "httpApiKey",
description: "Server security schema",
in: "header",
name: "X-Api-Key",
},
],
}).addNamespace({
security: [
{
type: "userPassword",
description: "Namespace security schema",
},
],
});
```
2 changes: 1 addition & 1 deletion example/example-documentation.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
asyncapi: 3.0.0
info:
title: Example APP
version: 1.1.0
version: 1.2.0-beta1
contact:
name: Anna Bocharova
url: https://robintail.cz
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zod-sockets",
"version": "1.1.0",
"version": "1.2.0-beta1",
"description": "Socket.IO solution with I/O validation and the ability to generate AsyncAPI specification and a contract for consumers",
"type": "module",
"main": "dist/index.cjs",
Expand Down
85 changes: 85 additions & 0 deletions src/__snapshots__/documentation.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1979,3 +1979,88 @@ operations:
- $ref: "#/channels/Root/messages/rootAckForIncomingTest"
"
`;

exports[`Documentation > Security > should depict server and channel security 1`] = `
"asyncapi: 3.0.0
info:
title: Testing security
version: 3.4.5
id: "urn:example:com:"
defaultContentType: text/plain
servers:
test:
host: example.com
pathname: /
protocol: https
security:
- $ref: "#/components/securitySchemes/serverSecurity0"
channels:
Root:
address: /
title: Namespace /
bindings:
ws:
bindingVersion: 0.1.0
method: GET
headers:
type: object
properties:
connection:
type: string
const: Upgrade
upgrade:
type: string
const: websocket
query:
type: object
properties:
EIO:
type: string
const: "4"
description: The version of the protocol
transport:
type: string
enum:
- polling
- websocket
description: The name of the transport
sid:
type: string
description: The session identifier
required:
- EIO
- transport
externalDocs:
description: Engine.IO Protocol
url: https://socket.io/docs/v4/engine-io-protocol/
messages:
rootIncomingTest:
name: test
title: test
payload:
type: array
additionalItems: false
components:
securitySchemes:
serverSecurity0:
type: httpApiKey
description: Sample security schema
in: header
name: X-Api-Key
rootSecurity0:
type: userPassword
description: Namespace level security sample
operations:
RootRecvOperationTest:
action: receive
channel:
$ref: "#/channels/Root"
messages:
- $ref: "#/channels/Root/messages/rootIncomingTest"
title: test
summary: Incoming event test
description: The message consumed by the application within the / namespace
security:
- $ref: "#/components/securitySchemes/rootSecurity0"
"
`;
12 changes: 12 additions & 0 deletions src/async-api/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ServerObject,
} from "./model";
import yaml from "yaml";
import { SecuritySchemeObject } from "./security";

export class AsyncApiBuilder {
protected readonly document: AsyncApiObject;
Expand Down Expand Up @@ -39,6 +40,17 @@ export class AsyncApiBuilder {
return this;
}

public addSecurityScheme(name: string, scheme: SecuritySchemeObject): this {
this.document.components = {
...this.document.components,
securitySchemes: {
...this.document.components?.securitySchemes,
[name]: scheme,
},
};
return this;
}

getSpec(): AsyncApiObject {
return this.document;
}
Expand Down
47 changes: 3 additions & 44 deletions src/async-api/model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SecuritySchemeObject } from "./security";
import { WS } from "./websockets";

/**
Expand All @@ -18,7 +19,7 @@ export interface ServerObject {
title?: string;
description?: string;
variables?: Record<string, ServerVariableObject>;
security?: SecuritySchemeObject[];
security?: Array<SecuritySchemeObject | ReferenceObject>;
tags?: TagObject[];
externalDocs?: ExternalDocumentationObject;
bindings?: Bindings<WS.Server>;
Expand Down Expand Up @@ -264,7 +265,7 @@ export interface OperationTraitObject {
title?: string;
summary?: string;
description?: string;
security?: SecuritySchemeObject[];
security?: Array<SecuritySchemeObject | ReferenceObject>;
tags?: TagObject[];
externalDocs?: ExternalDocumentationObject;
bindings?: Bindings<WS.Operation>;
Expand Down Expand Up @@ -324,48 +325,6 @@ export interface TagObject {
externalDocs?: ExternalDocumentationObject;
}

export interface SecuritySchemeObject {
type:
| "userPassword"
| "apiKey"
| "X509"
| "symmetricEncryption"
| "asymmetricEncryption"
| "http"
| "oauth2"
| "openIdConnect";
description?: string;
/** @desc for httpApiKey */
name?: string;
/** @desc Valid values are "user" and "password" for apiKey and "query", "header" or "cookie" for httpApiKey. */
in?: "user" | "password" | "query" | "header" | "cookie";
/** @desc for http */
scheme?: string;
/** @desc for http */
bearerFormat?: string;
/** @desc for oauth2 */
flows?: OAuthFlowsObject;
/** @desc for openIdConnect */
openIdConnectUrl?: string;
}

export interface OAuthFlowsObject {
implicit?: OAuthFlowObject;
password?: OAuthFlowObject;
clientCredentials?: OAuthFlowObject;
authorizationCode?: OAuthFlowObject;
}

export interface OAuthFlowObject {
/** @desc for implicit and authorizationCode */
authorizationUrl?: string;
/** @desc for password, clientCredentials and authorizationCode */
tokenUrl?: string;
refreshUrl?: string;
/** @desc A map between the scope name and a short description for it. */
scopes: Record<string, string>;
}

/** @since 3.0.0 partially extends SchemaObject; schema prop removed */
export interface ParameterObject
extends Pick<SchemaObject, "enum" | "default" | "description" | "examples"> {
Expand Down
82 changes: 82 additions & 0 deletions src/async-api/security.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
interface FlowCommons {
/** @desc The URL to be used for obtaining refresh tokens. */
refreshUrl?: string;
/** @desc A map between the scope name and a short description for it. */
availableScopes: Record<string, string>;
}

interface AuthHavingFlow {
/** @desc The authorization URL to be used for this flow. */
authorizationUrl: string;
}

interface TokenHavingFlow {
/** @desc The token URL to be used for this flow. */
tokenUrl: string;
}

export interface OAuthFlowsObject {
implicit?: FlowCommons & AuthHavingFlow;
password?: FlowCommons & TokenHavingFlow;
clientCredentials?: FlowCommons & TokenHavingFlow;
authorizationCode?: FlowCommons & AuthHavingFlow & TokenHavingFlow;
}

interface HttpApiKeySecurity {
type: "httpApiKey";
/** @desc The name of the header, query or cookie parameter to be used. */
name: string;
in: "query" | "header" | "cookie";
}

interface ApiKeySecurity {
type: "apiKey";
in: "user" | "password";
}

interface HttpSecurity {
type: "http";
/** @link https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml */
scheme?: string;
/** @example "Bearer" */
bearerFormat?: string;
}

interface ScopesHavingSecurity {
/** @desc List of the needed scope names. An empty array means no scopes are needed. */
scopes?: string[];
}

interface OAuth2Security extends ScopesHavingSecurity {
type: "oauth2";
flows: OAuthFlowsObject;
}

interface OpenIdConnectSecurity extends ScopesHavingSecurity {
type: "openIdConnect";
/** @desc OpenId Connect URL to discover OAuth2 configuration values */
openIdConnectUrl: string;
}

interface OtherSecurity {
type:
| "userPassword"
| "X509"
| "symmetricEncryption"
| "asymmetricEncryption"
| "plain"
| "scramSha256"
| "scramSha512"
| "gssapi";
}

export type SecuritySchemeObject = {
description?: string;
} & (
| HttpApiKeySecurity
| ApiKeySecurity
| HttpSecurity
| OAuth2Security
| OpenIdConnectSecurity
| OtherSecurity
);
Loading

0 comments on commit 550d54e

Please sign in to comment.