@queue-it/fastly 3.6.0 → 4.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +156 -58
  2. package/package.json +23 -16
  3. package/src/contextProvider.ts +175 -0
  4. package/src/fastlyCryptoProvider.ts +8 -0
  5. package/src/helper.ts +11 -0
  6. package/{assembly/sdk → src}/helpers/crypto.ts +338 -340
  7. package/src/index.ts +3 -0
  8. package/src/integrationConfigProvider.ts +199 -0
  9. package/src/requestResponseHandler.ts +200 -0
  10. package/README-INTERNAL.md +0 -21
  11. package/asconfig.json +0 -17
  12. package/assembly/__tests__/CustomerIntegrationDecodingHandler.spec.ts +0 -1086
  13. package/assembly/__tests__/IntegrationConfigHelpersTest.spec.ts +0 -574
  14. package/assembly/__tests__/KnownUserTest.spec.ts +0 -1894
  15. package/assembly/__tests__/Mocks.ts +0 -321
  16. package/assembly/__tests__/QueueParameterHelper.spec.ts +0 -59
  17. package/assembly/__tests__/UserInQueueService.spec.ts +0 -418
  18. package/assembly/__tests__/UserInQueueStateCookieRepository.spec.ts +0 -337
  19. package/assembly/__tests__/as-pect.config.js +0 -57
  20. package/assembly/__tests__/as-pect.d.ts +0 -1
  21. package/assembly/contextProvider.ts +0 -123
  22. package/assembly/helper.ts +0 -23
  23. package/assembly/index.ts +0 -10
  24. package/assembly/integrationConfigProvider.ts +0 -32
  25. package/assembly/requestResponseHandler.ts +0 -92
  26. package/assembly/sdk/HttpContextProvider.ts +0 -24
  27. package/assembly/sdk/IntegrationConfig/CustomerIntegrationDecodingHandler.ts +0 -198
  28. package/assembly/sdk/IntegrationConfig/IntegrationConfigHelpers.ts +0 -232
  29. package/assembly/sdk/IntegrationConfig/IntegrationConfigModel.ts +0 -93
  30. package/assembly/sdk/KnownUser.ts +0 -396
  31. package/assembly/sdk/Models.ts +0 -105
  32. package/assembly/sdk/QueueITHelpers.ts +0 -263
  33. package/assembly/sdk/UserInQueueService.ts +0 -245
  34. package/assembly/sdk/UserInQueueStateCookieRepository.ts +0 -189
  35. package/assembly/sdk/helpers/Date.ts +0 -194
  36. package/assembly/sdk/helpers/Uri.ts +0 -308
  37. package/assembly/tsconfig.json +0 -6
  38. package/index.js +0 -5
  39. package/pipelines/ci.yaml +0 -28
package/README.md CHANGED
@@ -1,58 +1,156 @@
1
- # KnownUser.V3.Fastly
2
-
3
- The Queue-it Security Framework ensures that end-users are not able to access your online application without first going through the queue for any and all �protected� areas and paths on your sites.
4
- The queue system is implemented by adding a server-side (request-level) integration that protects your online application by redirecting users to a waiting room according to web traffic settings
5
- in the Queue-it GO Platform. After the integration is complete, queue system behavior and operations are managed in Queue-it�s Go Platform and/or via the Queue-it Admin API.
6
-
7
- This Fastly Queue-it Connector SDK (aka, Queue-its server-side KnownUser connector) uses a Compute@Edge service to integrate Queue-it's functionality into Fastly's network.
8
-
9
- A Wasm service is required to utilize this connector.
10
-
11
- >You can find the latest released version [here](https://github.com/queueit/KnownUser.V3.Fastly/releases/latest).
12
-
13
- ## Introduction
14
-
15
- When a user makes a request to your Fastly service our connector validates the request and if it is needed, it will redirect the user to the waiting room. After waiting in the waiting room,
16
- the queue engine will redirect the user back to your end attaching a query string parameter ( `queueittoken` ) containing some information about the user to the URL.
17
- The most important fields of the `queueittoken` are:
18
-
19
- - q - The user's unique queue identifier
20
- - ts - A timestamp of how long this redirect is valid
21
- - h - A hash of the token
22
-
23
- After the user returns from the queue, the connector will let the user continue his request to your backend ( without redirecting to the queue since the request has a valid queueittoken as query string) .
24
-
25
- ## Installation
26
-
27
- To install the connector you can use the Fastly Compute@Edge CLI or you can upload an archive file (TGZ format) in the Fastly service configuration page.
28
- Once deployed, the connector can be customized and updated with specific configurations (protection schema) managed and exported from the Queue-it GO Platform.
29
-
30
- - Step 1: Clone this repository and setup your fastly.toml file using the Fastly CLI.
31
- - Step 2: Set your Customer Id, API Key, Secret Id, Queue-it and origin backends in `requestResponseHandler.ts`.
32
- - Step 3: Create a Queue-it origin in your service configuration page.
33
- The address should be `{CustomerId}.queue-it.net`. Set **Override host** to the same value.
34
- - Step 4: Build and deploy the package.
35
- - Step 5: Create desired waiting room(s), triggers, and actions in GO. Then, save/publish the configuration.
36
-
37
- ## Providing the queue configuration
38
-
39
- The recommended way is to use the Go Queue-it self-service portal to setup the configuration.
40
- The configuration specifies a set of Triggers and Actions. A Trigger is an expression matching one, more or all URLs on your website.
41
- When a user enter your website and the URL matches a Trigger-expression the corresponding Action will be triggered.
42
- The Action specifies which waiting room the users should be send to.
43
- In this way you can specify which waiting room(s) should protect which page(s) on the fly without changing the server-side integration.
44
-
45
- ## Protecting AJAX calls
46
-
47
- If you need to protect AJAX calls beside page loads you need to add the below JavaScript tags to your pages:
48
-
49
- ```html
50
- <script type="text/javascript" src="//static.queue-it.net/script/queueclient.min.js"></script>
51
- <script
52
- data-queueit-intercept-domain="{YOUR_CURRENT_DOMAIN}"
53
- data-queueit-intercept="true"
54
- data-queueit-c="{YOUR_CUSTOMER_ID}"
55
- type="text/javascript"
56
- src="//static.queue-it.net/script/queueconfigloader.min.js">
57
- </script>
58
- ```
1
+ # KnownUser.V3.Fastly
2
+
3
+ The Queue-it Security Framework ensures that end-users are not able to access your online application without first
4
+ going through the queue for any and all `protected` areas and paths on your sites. The queue system is implemented by
5
+ adding a server-side (request-level) integration that protects your online application by redirecting users to a waiting
6
+ room according to web traffic settings in the Queue-it GO Platform. After the integration is complete, queue system
7
+ behavior and operations are managed in Queue-it's Go Platform and/or via the Queue-it Admin API.
8
+
9
+ This Fastly Queue-it Connector SDK (aka, Queue-it's server-side KnownUser connector) uses a Compute@Edge service to
10
+ integrate Queue-it's functionality into Fastly's network.
11
+
12
+ A Wasm service is required to utilize this connector.
13
+
14
+ > You can find the latest released version [here](https://github.com/queueit/KnownUser.V3.Fastly/releases/latest).
15
+
16
+ ## Introduction
17
+
18
+ When a user makes a request to your Fastly service our connector validates the request and if it is needed, it will
19
+ redirect the user to the waiting room. After waiting in the waiting room, the queue engine will redirect the user back
20
+ to your end attaching a query string parameter ( `queueittoken` ) containing some information about the user to the URL.
21
+ The most important fields of the `queueittoken` are:
22
+
23
+ - q - The user's unique queue identifier
24
+ - ts - A timestamp of how long this redirect is valid
25
+ - h - A hash of the token
26
+
27
+ After the user returns from the queue, the connector will let the user continue his request to your backend ( without
28
+ redirecting to the queue since the request has a valid queueittoken as query string) .
29
+
30
+ ## Installation
31
+
32
+ There are two methods of installation:
33
+
34
+ ### As a standalone service
35
+
36
+ - Go to the Fastly services page and create a new **Wasm** service.
37
+ - Go to Domains and fill in the domain that you want your service to be reachable at. You may need to register a CNAME
38
+ record if you have your own domain.
39
+ - Then click on *Origins* and add a new host that has the hostname of your origin server.
40
+ You need to edit the Host and name it **origin**.
41
+ - Create a second host that has the hostname of `{yourCustomerId}.queue-it.net` and name it **queue-it**.
42
+ Edit the host, go to advanced options and fill in the same hostname in **Override host**
43
+ - Go to **Resources -> Config stores** and create a new config store named `IntegrationConfiguration`.
44
+ - Add the following items in the config store (you can find these values in the Go Queue-It self-service platform):
45
+ - customerId: Your customer ID
46
+ - apiKey: The API key for your account
47
+ - secret: Your KnownUserV3 secret
48
+ - queueItOrigin: The name of the queue-it host, in this case it's `queue-it`
49
+ - Go to **Linked Services** and click on `Link Service`
50
+ - Select your service and click `Next`
51
+ - Click `Link and activate` to link the `Config store` to your service. It should generate a new version
52
+ - You can verify that the `Config store` has been linked by going to your service and see it in the section `Config stores`
53
+ - Download the latest package from the releases page and unarchive it.
54
+ - Edit the `fastly.toml` file and copy the ID of your service (you can see this by opening up the service in Fastly) and
55
+ replace **{YourServiceId}** with it.
56
+ - Archive the directory in the same format (tar.gz).
57
+ - Go to `Package` in the Fastly service page and upload the package.
58
+ - To finish up and deploy your service click on the **Activate** button.
59
+
60
+ ### Customizable service with the connector
61
+
62
+ - Go to the Fastly services page and create a new **Wasm** service and copy it's ID.
63
+ - Clone this repository and edit the fastly.toml file, make sure to set the `service_id` field to the ID you copied.
64
+ - Then click on *Origins* and add a new host that has the hostname of your origin server.
65
+ You can name the host **origin** or whatever you choose.
66
+ - Create a host that has the hostname of `{yourCustomerId}.queue-it.net` and name it **queue-it**.
67
+ Edit the host, go to advanced options and fill in the same hostname in **Override host**
68
+ - Go to **Resources -> Config stores** and create a new config store named `IntegrationConfiguration`.
69
+ - Add the following items in the config store (you can find these values in the Go Queue-It self-service platform):
70
+ - customerId: Your customer ID
71
+ - apiKey: The API key for your account
72
+ - secret: Your KnownUserV3 secret
73
+ - queueItOrigin: The name of the queue-it host, in this case it's `queue-it`
74
+ - Go to **Linked Services** and click on `Link Service`
75
+ - Select your service and click `Next`
76
+ - Click `Link and activate` to link the `Config store` to your service. It should generate a new version
77
+ - You can verify that the `Config store` has been linked by going to your service and see it in the section `Config stores`
78
+ - You need to add some code that uses this connector. Here is an example:
79
+
80
+ ```ts
81
+ import {Fastly} from "@fastly/as-compute";
82
+ import {onQueueITRequest, IntegrationDetails, onQueueITResponse} from "@queue-it/fastly";
83
+
84
+ const req = Fastly.getClientRequest();
85
+
86
+ // This is optional and can be null if it's specified in your Config Store
87
+ const integrationDetails = new IntegrationDetails(
88
+ "QueueItOriginName",
89
+ "CustomerId",
90
+ "SecretKey",
91
+ "ApiKey");
92
+ let res = onQueueITRequest(req, integrationDetails);
93
+
94
+ if (res != null) {
95
+ Fastly.respondWith(res!);
96
+ } else {
97
+ const myOrigin = 'Ticketania';
98
+ const cacheOverride = new Fastly.CacheOverride();
99
+ const res = Fastly.fetch(req, {
100
+ backend: myOrigin,
101
+ cacheOverride,
102
+ }).wait();
103
+ onQueueITResponse(res);
104
+ Fastly.respondWith(res);
105
+ }
106
+ ```
107
+
108
+ - Build and deploy the package running `fastly compute build` and `fastly compute deploy` in the same directory.
109
+ - Create desired waiting room(s), triggers, and actions in the Go Queue-It self-service platform.
110
+ Then, save/publish the configuration.
111
+
112
+ ## Enqueue Token Settings
113
+
114
+ The connector supports enqueue tokens, which are used to secure the queue redirect flow. These settings are read at runtime from the Fastly `IntegrationConfiguration` Config Store. They are optional — if not set, the defaults below apply.
115
+
116
+ | Key | Default | Description |
117
+ |-----|---------|-------------|
118
+ | `enqueueTokenEnabled` | `"true"` | Enables enqueue token generation. Set to `"false"` to disable. |
119
+ | `enqueueTokenValidityTime` | `"240000"` | Token validity time in milliseconds. |
120
+ | `enqueueTokenKeyEnabled` | `"false"` | Enables key-based enqueue tokens. Set to `"true"` to enable. |
121
+
122
+ To configure these, add them as items in the `IntegrationConfiguration` Config Store alongside the other settings (`customerId`, `apiKey`, `secret`, etc.) — either via the Fastly UI or the Fastly API.
123
+
124
+ ## Request Body Trigger Matching
125
+
126
+ The connector supports matching triggers based on the request body content. This is disabled by default. When enabled, up to 2048 characters of the request body are read and passed to the SDK for trigger evaluation.
127
+
128
+ To enable it, add the following key to the `IntegrationConfiguration` Config Store:
129
+
130
+ | Key | Default | Description |
131
+ |-----|---------|-------------|
132
+ | `requestBodyEnabled` | `"false"` | Set to `"true"` to enable request body trigger matching. |
133
+
134
+ ## Providing the queue configuration
135
+
136
+ The recommended way is to use the Go Queue-it self-service portal to setup the configuration. The configuration
137
+ specifies a set of Triggers and Actions. A Trigger is an expression matching one, more or all URLs on your website. When
138
+ a user enter your website and the URL matches a Trigger-expression the corresponding Action will be triggered. The
139
+ Action specifies which waiting room the users should be send to. In this way you can specify which waiting room(s)
140
+ should protect which page(s) on the fly without changing the server-side integration.
141
+
142
+ ## Protecting AJAX calls
143
+
144
+ If you need to protect AJAX calls beside page loads you need to add the below JavaScript tags to your pages:
145
+
146
+ ```html
147
+
148
+ <script type="text/javascript" src="//static.queue-it.net/script/queueclient.min.js"></script>
149
+ <script
150
+ data-queueit-intercept-domain="{YOUR_CURRENT_DOMAIN}"
151
+ data-queueit-intercept="true"
152
+ data-queueit-c="{YOUR_CUSTOMER_ID}"
153
+ type="text/javascript"
154
+ src="//static.queue-it.net/script/queueconfigloader.min.js">
155
+ </script>
156
+ ```
package/package.json CHANGED
@@ -1,28 +1,35 @@
1
1
  {
2
2
  "name": "@queue-it/fastly",
3
- "version": "3.6.0",
3
+ "version": "4.4.3",
4
4
  "description": "Queue-it connector for Fastly",
5
- "main": "index.js",
5
+ "main": "src/index.ts",
6
+ "author": "devs@queue-it.com",
7
+ "repository": "https://github.com/queueit/KnownUser.V3.Fastly",
8
+ "license": "MIT",
9
+ "files": [
10
+ "package.json",
11
+ "README.md",
12
+ "src/helpers",
13
+ "src/fastlyCryptoProvider.ts",
14
+ "src/contextProvider.ts",
15
+ "src/helper.ts",
16
+ "src/integrationConfigProvider.ts",
17
+ "src/requestResponseHandler.ts",
18
+ "src/index.ts"
19
+ ],
6
20
  "scripts": {
7
- "test": "npx asp --config assembly/__tests__/as-pect.config.js --verbose",
8
- "asbuild:untouched": "asc assembly/index.ts --target debug",
9
- "asbuild:optimized": "asc assembly/index.ts --target release",
10
- "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",
21
+ "typecheck": "tsc --noEmit",
22
+ "bundle": "esbuild src/index.ts --bundle --outfile=bin/index.js --platform=node --target=es2022 && js-compute-runtime bin/index.js bin/main.wasm",
11
23
  "build": "fastly compute build",
12
24
  "deploy": "fastly compute deploy"
13
25
  },
14
- "author": "devs@queue-it.com",
15
- "license": "MIT",
16
26
  "dependencies": {
17
- "@assemblyscript/loader": "^0.17.14",
18
- "@fastly/as-compute": "^0.2.1",
19
- "as-wasi": "^0.4.4",
20
- "assemblyscript-json": "^1.0.0",
21
- "assemblyscript-regex": "^1.6.3"
27
+ "@fastly/js-compute": "^3.0.0",
28
+ "@queue-it/connector-javascript": "^4.4.4",
29
+ "@queue-it/queue-token": "~1.0.4"
22
30
  },
23
31
  "devDependencies": {
24
- "@as-pect/cli": "^5.0.1",
25
- "@as-pect/core": "^5.0.1",
26
- "assemblyscript": "^0.17.14"
32
+ "esbuild": "^0.27.3",
33
+ "typescript": "^5.4.0"
27
34
  }
28
35
  }
@@ -0,0 +1,175 @@
1
+ import {
2
+ DefaultEnqueueTokenProvider,
3
+ IConnectorContextProvider,
4
+ ICryptoProvider,
5
+ IEnqueueTokenProvider,
6
+ INormalizationProvider,
7
+ DefaultNormalizationProvider,
8
+ IHttpRequest,
9
+ IHttpResponse,
10
+ } from '@queue-it/connector-javascript';
11
+ import { Token, Payload } from '@queue-it/queue-token';
12
+ import { FastlyCryptoProvider } from './fastlyCryptoProvider';
13
+
14
+ export function getHttpHandler(req: Request, clientIp: string = '', bodyString: string = ''): FastlyHttpContextProvider {
15
+ return new FastlyHttpContextProvider(req, clientIp, bodyString);
16
+ }
17
+
18
+ export class FastlyHttpContextProvider implements IConnectorContextProvider {
19
+ isError: boolean = false;
20
+ private readonly _httpRequest: FastlyHttpRequest;
21
+ private readonly _httpResponse: FastlyHttpResponse;
22
+ private _enqueueTokenProvider: IEnqueueTokenProvider | null = null;
23
+ private readonly _normalizationProvider: INormalizationProvider;
24
+ private readonly _cryptoProvider: ICryptoProvider;
25
+
26
+ constructor(fReq: Request, clientIp: string = '', bodyString: string = '') {
27
+ this._httpRequest = new FastlyHttpRequest(fReq, clientIp, bodyString);
28
+ this._httpResponse = new FastlyHttpResponse();
29
+ this._normalizationProvider = new DefaultNormalizationProvider();
30
+ this._cryptoProvider = new FastlyCryptoProvider();
31
+ }
32
+
33
+ getHttpRequest(): IHttpRequest {
34
+ return this._httpRequest;
35
+ }
36
+
37
+ getHttpResponse(): IHttpResponse {
38
+ return this._httpResponse;
39
+ }
40
+
41
+ getCryptoProvider(): ICryptoProvider {
42
+ return this._cryptoProvider;
43
+ }
44
+
45
+ setEnqueueTokenProvider(
46
+ customerId: string,
47
+ secretKey: string,
48
+ validityTime: number,
49
+ clientIp: string,
50
+ withKey: boolean
51
+ ): void {
52
+ this._enqueueTokenProvider = new DefaultEnqueueTokenProvider(
53
+ customerId,
54
+ secretKey,
55
+ validityTime,
56
+ clientIp,
57
+ withKey,
58
+ Token,
59
+ Payload
60
+ );
61
+ }
62
+
63
+ setCustomEnqueueTokenProvider(provider: IEnqueueTokenProvider): void {
64
+ this._enqueueTokenProvider = provider;
65
+ }
66
+
67
+ getEnqueueTokenProvider(): IEnqueueTokenProvider | null {
68
+ return this._enqueueTokenProvider;
69
+ }
70
+
71
+ getNormalizationProvider(): INormalizationProvider {
72
+ return this._normalizationProvider;
73
+ }
74
+
75
+ getResponseHeaders(): Headers {
76
+ return this._httpResponse.getHeaders();
77
+ }
78
+ }
79
+
80
+ class FastlyHttpRequest implements IHttpRequest {
81
+ private _parsedCookieDic: Record<string, string> | null = null;
82
+
83
+ constructor(private baseReq: Request, private _clientIp: string = '', private _bodyString: string = '') {}
84
+
85
+ getUserAgent(): string {
86
+ return this.getHeader('user-agent');
87
+ }
88
+
89
+ getHeader(name: string): string {
90
+ if (name.toLowerCase() === 'x-queueit-clientip')
91
+ return this.getUserHostAddress();
92
+
93
+ return this.baseReq.headers.get(name) || '';
94
+ }
95
+
96
+ getAbsoluteUri(): string {
97
+ return this.baseReq.url;
98
+ }
99
+
100
+ getUserHostAddress(): string {
101
+ return this._clientIp || this.baseReq.headers.get('Fastly-Client-IP') || '';
102
+ }
103
+
104
+ getCookieValue(name: string): string | undefined {
105
+ if (!this._parsedCookieDic) {
106
+ this._parsedCookieDic = this._parseCookies(this.getHeader('cookie'));
107
+ }
108
+
109
+ const cookieValue = this._parsedCookieDic[name];
110
+ if (cookieValue) {
111
+ try {
112
+ return decodeURIComponent(cookieValue);
113
+ } catch {
114
+ return;
115
+ }
116
+ }
117
+
118
+ return;
119
+ }
120
+
121
+ getRequestBodyAsString(): string {
122
+ return this._bodyString;
123
+ }
124
+
125
+ private _parseCookies(cookieValue: string): Record<string, string> {
126
+ const parsedCookie: Record<string, string> = {};
127
+ cookieValue.split(';').forEach(function (cookie) {
128
+ if (cookie) {
129
+ const parts = cookie.split('=');
130
+ if (parts.length >= 2) parsedCookie[parts[0].trim()] = parts[1].trim();
131
+ }
132
+ });
133
+ return parsedCookie;
134
+ }
135
+ }
136
+
137
+ class FastlyHttpResponse implements IHttpResponse {
138
+ private readonly headers: Headers;
139
+
140
+ constructor() {
141
+ this.headers = new Headers();
142
+ }
143
+
144
+ setCookie(
145
+ cookieName: string,
146
+ cookieValue: string,
147
+ domain: string,
148
+ expiration: number,
149
+ httpOnly: boolean,
150
+ isSecure: boolean
151
+ ): void {
152
+ const expirationDate = new Date(expiration * 1000);
153
+
154
+ let setCookieString = `${cookieName}=${encodeURIComponent(cookieValue)}; expires=${expirationDate.toUTCString()};`;
155
+
156
+ if (domain) {
157
+ setCookieString += ` domain=${domain};`;
158
+ }
159
+
160
+ if (httpOnly) {
161
+ setCookieString += ' HttpOnly;';
162
+ }
163
+
164
+ if (isSecure) {
165
+ setCookieString += ' Secure;';
166
+ }
167
+
168
+ setCookieString += ' path=/';
169
+ this.headers.append('set-cookie', setCookieString);
170
+ }
171
+
172
+ getHeaders(): Headers {
173
+ return this.headers;
174
+ }
175
+ }
@@ -0,0 +1,8 @@
1
+ import { ICryptoProvider } from '@queue-it/connector-javascript';
2
+ import { hmacString } from './helpers/crypto';
3
+
4
+ export class FastlyCryptoProvider implements ICryptoProvider {
5
+ getSha256Hash(secretKey: string, plaintext: string): string {
6
+ return hmacString(secretKey, plaintext);
7
+ }
8
+ }
package/src/helper.ts ADDED
@@ -0,0 +1,11 @@
1
+ export class QueueITHelper {
2
+ static readonly KUP_VERSION: string = "fastly-4.4.3";
3
+
4
+ static addKUPlatformVersion(redirectQueueUrl: string): string {
5
+ return redirectQueueUrl + "&kupver=" + QueueITHelper.KUP_VERSION;
6
+ }
7
+ }
8
+
9
+ export interface RequestLogger {
10
+ log(message: string): void;
11
+ }