saasufy-components 2.1.0 → 2.3.0
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.
- package/collection-adder.js +10 -4
- package/oauth-handler.js +98 -0
- package/oauth-link.js +84 -0
- package/package.json +1 -1
- package/utils.js +12 -0
package/collection-adder.js
CHANGED
|
@@ -32,6 +32,7 @@ class CollectionAdder extends SocketConsumer {
|
|
|
32
32
|
return [
|
|
33
33
|
'collection-type',
|
|
34
34
|
'collection-fields',
|
|
35
|
+
'field-labels',
|
|
35
36
|
'model-values',
|
|
36
37
|
'submit-button-label',
|
|
37
38
|
'hide-submit-button',
|
|
@@ -166,6 +167,10 @@ class CollectionAdder extends SocketConsumer {
|
|
|
166
167
|
fieldValues: modelFieldValues
|
|
167
168
|
} = this.getFieldDetails(this.getAttribute('model-values'));
|
|
168
169
|
|
|
170
|
+
let {
|
|
171
|
+
fieldValues: modelFieldLabels
|
|
172
|
+
} = this.getFieldDetails(this.getAttribute('field-labels'));
|
|
173
|
+
|
|
169
174
|
this.modelFieldValues = modelFieldValues;
|
|
170
175
|
|
|
171
176
|
if (this.collection) this.collection.destroy();
|
|
@@ -186,11 +191,12 @@ class CollectionAdder extends SocketConsumer {
|
|
|
186
191
|
inputType = inputTypeParts[1];
|
|
187
192
|
inputTypeParams = inputTypeParts[3] ? inputTypeParts[3].split(',') : [];
|
|
188
193
|
}
|
|
194
|
+
let inputLabel = modelFieldLabels[field] == null ? field : modelFieldLabels[field];
|
|
189
195
|
if (inputType === 'checkbox') {
|
|
190
196
|
items.push(
|
|
191
197
|
`<label>
|
|
192
198
|
${
|
|
193
|
-
|
|
199
|
+
inputLabel
|
|
194
200
|
}
|
|
195
201
|
<input class="collection-adder-input" type="${
|
|
196
202
|
inputType
|
|
@@ -205,7 +211,7 @@ class CollectionAdder extends SocketConsumer {
|
|
|
205
211
|
<input class="collection-adder-input" type="text" name="${
|
|
206
212
|
field
|
|
207
213
|
}" placeholder="${
|
|
208
|
-
|
|
214
|
+
inputLabel
|
|
209
215
|
}" />
|
|
210
216
|
<select class="collection-adder-select">
|
|
211
217
|
<option value="" selected disabled hidden>Options</option>
|
|
@@ -218,7 +224,7 @@ class CollectionAdder extends SocketConsumer {
|
|
|
218
224
|
`<textarea class="collection-adder-input" name="${
|
|
219
225
|
field
|
|
220
226
|
}" placeholder="${
|
|
221
|
-
|
|
227
|
+
inputLabel
|
|
222
228
|
}"></textarea>`
|
|
223
229
|
);
|
|
224
230
|
} else {
|
|
@@ -228,7 +234,7 @@ class CollectionAdder extends SocketConsumer {
|
|
|
228
234
|
}" name="${
|
|
229
235
|
field
|
|
230
236
|
}" placeholder="${
|
|
231
|
-
|
|
237
|
+
inputLabel
|
|
232
238
|
}" />`
|
|
233
239
|
);
|
|
234
240
|
}
|
package/oauth-handler.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { SocketConsumer } from './socket.js';
|
|
2
|
+
import { generateRandomHexString } from './utils.js';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_AUTH_TIMEOUT = 10000;
|
|
5
|
+
|
|
6
|
+
class OAuthHandler extends SocketConsumer {
|
|
7
|
+
static get observedAttributes() {
|
|
8
|
+
return [
|
|
9
|
+
'provider',
|
|
10
|
+
'code-param-name',
|
|
11
|
+
'state-param-name',
|
|
12
|
+
'state-session-storage-key',
|
|
13
|
+
'success-url',
|
|
14
|
+
'success-location-hash',
|
|
15
|
+
'navigate-event-path',
|
|
16
|
+
'auth-timeout'
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
successRedirect() {
|
|
21
|
+
let successURL = this.getAttribute('success-url');
|
|
22
|
+
let successLocationHash = this.getAttribute('success-location-hash');
|
|
23
|
+
let navigateEventPath = this.getAttribute('navigate-event-path');
|
|
24
|
+
|
|
25
|
+
if (successLocationHash) {
|
|
26
|
+
location.hash = successLocationHash;
|
|
27
|
+
} else if (successURL) {
|
|
28
|
+
location.href = successURL;
|
|
29
|
+
} else {
|
|
30
|
+
this.dispatchEvent(
|
|
31
|
+
new CustomEvent('navigate', {
|
|
32
|
+
detail: {
|
|
33
|
+
path: navigateEventPath
|
|
34
|
+
},
|
|
35
|
+
bubbles: true
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async connectedCallback() {
|
|
42
|
+
this.innerHTML = '';
|
|
43
|
+
|
|
44
|
+
let provider = this.getAttribute('provider');
|
|
45
|
+
if (!provider) {
|
|
46
|
+
throw new Error('The provider attribute of oauth-handler was not specified');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let sessionStorageKey = this.getAttribute('state-session-storage-key') || `oauth.${provider}.state`;
|
|
50
|
+
let expectedOAuthState = sessionStorage.getItem(sessionStorageKey);
|
|
51
|
+
if (!expectedOAuthState) return;
|
|
52
|
+
|
|
53
|
+
let codeParamName = this.getAttribute('code-param-name') || 'code';
|
|
54
|
+
let stateParamName = this.getAttribute('state-param-name') || 'state';
|
|
55
|
+
|
|
56
|
+
let authTimeout = Number(this.getAttribute('auth-timeout') || DEFAULT_AUTH_TIMEOUT);
|
|
57
|
+
|
|
58
|
+
let urlSearchParams = new URLSearchParams(location.search);
|
|
59
|
+
let code = urlSearchParams.get(codeParamName);
|
|
60
|
+
|
|
61
|
+
if (code) {
|
|
62
|
+
let state = urlSearchParams.get(stateParamName);
|
|
63
|
+
let socket = this.getSocket();
|
|
64
|
+
|
|
65
|
+
history.replaceState({}, document.title, location.pathname);
|
|
66
|
+
|
|
67
|
+
if (state === expectedOAuthState) {
|
|
68
|
+
try {
|
|
69
|
+
await socket.invoke('log-in-oauth', { provider, data: { code } });
|
|
70
|
+
} catch (error) {
|
|
71
|
+
this.innerHTML = `
|
|
72
|
+
<div class="error">OAuth authentication failed. ${error.message}</div>
|
|
73
|
+
`;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (socket.authState === 'authenticated') {
|
|
77
|
+
this.successRedirect();
|
|
78
|
+
} else {
|
|
79
|
+
try {
|
|
80
|
+
await socket.listener('authenticate').once(authTimeout);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
this.innerHTML = `
|
|
83
|
+
<div class="error">OAuth socket authentication timed out</div>
|
|
84
|
+
`;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this.successRedirect();
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
this.innerHTML = `
|
|
91
|
+
<div class="error">OAuth state query parameter was invalid</div>
|
|
92
|
+
`;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
window.customElements.define('oauth-handler', OAuthHandler);
|
package/oauth-link.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { renderTemplate, generateRandomHexString } from './utils.js';
|
|
2
|
+
|
|
3
|
+
class OAuthLink extends HTMLElement {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
this.isReady = false;
|
|
7
|
+
this.attachShadow({ mode: 'open' });
|
|
8
|
+
|
|
9
|
+
this.handleSlotChangeEvent = () => {
|
|
10
|
+
this.renderItem();
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static get observedAttributes() {
|
|
15
|
+
return [
|
|
16
|
+
'provider',
|
|
17
|
+
'client-id',
|
|
18
|
+
'state-size',
|
|
19
|
+
'state-session-storage-key'
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
24
|
+
if (!this.isReady) return;
|
|
25
|
+
this.render();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
connectedCallback() {
|
|
29
|
+
this.isReady = true;
|
|
30
|
+
this.render();
|
|
31
|
+
this.shadowRoot.addEventListener('slotchange', this.handleSlotChangeEvent);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
disconnectedCallback() {
|
|
35
|
+
this.shadowRoot.removeEventListener('slotchange', this.handleSlotChangeEvent);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
renderItem() {
|
|
39
|
+
let provider = this.getAttribute('provider');
|
|
40
|
+
if (!provider) {
|
|
41
|
+
throw new Error('The provider attribute of oauth-link was not specified');
|
|
42
|
+
}
|
|
43
|
+
let viewportNode = this.shadowRoot.querySelector('slot[name="viewport"]').assignedNodes()[0];
|
|
44
|
+
if (!viewportNode) return;
|
|
45
|
+
let itemTemplate = this.shadowRoot.querySelector('slot[name="item"]').assignedNodes()[0];
|
|
46
|
+
if (!itemTemplate) return;
|
|
47
|
+
|
|
48
|
+
let clientId = this.getAttribute('client-id');
|
|
49
|
+
let stateSize = Number(this.getAttribute('state-size') || 20);
|
|
50
|
+
let sessionStorageKey = this.getAttribute('state-session-storage-key') || `oauth.${provider}.state`;
|
|
51
|
+
let state = generateRandomHexString(stateSize);
|
|
52
|
+
sessionStorage.setItem(sessionStorageKey, state);
|
|
53
|
+
|
|
54
|
+
let itemString = renderTemplate(
|
|
55
|
+
itemTemplate.innerHTML,
|
|
56
|
+
{
|
|
57
|
+
oauth: {
|
|
58
|
+
clientId,
|
|
59
|
+
state
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
viewportNode.innerHTML = itemString;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
render() {
|
|
67
|
+
let urlSearchParams = new URLSearchParams(location.search);
|
|
68
|
+
let code = urlSearchParams.get('code');
|
|
69
|
+
|
|
70
|
+
if (code) {
|
|
71
|
+
this.shadowRoot.innerHTML = '';
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.shadowRoot.innerHTML = `
|
|
76
|
+
<slot name="item"></slot>
|
|
77
|
+
<slot name="viewport"></slot>
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
this.renderItem();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
window.customElements.define('oauth-link', OAuthLink);
|
package/package.json
CHANGED
package/utils.js
CHANGED
|
@@ -268,6 +268,18 @@ export function updateConsumerElements(parentElement, consumers, value) {
|
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
export function generateRandomHexString(byteLength) {
|
|
272
|
+
let byteArray = new Uint8Array(byteLength);
|
|
273
|
+
crypto.getRandomValues(byteArray);
|
|
274
|
+
return Array.from(byteArray, byte => {
|
|
275
|
+
let firstChar = byte >> 4;
|
|
276
|
+
firstChar = firstChar < 10 ? String(firstChar) : String.fromCharCode(firstChar + 87);
|
|
277
|
+
let secondChar = byte & 0x0f;
|
|
278
|
+
secondChar = secondChar < 10 ? String(secondChar) : String.fromCharCode(secondChar + 87);
|
|
279
|
+
return `${firstChar}${secondChar}`;
|
|
280
|
+
}).join('');
|
|
281
|
+
}
|
|
282
|
+
|
|
271
283
|
export function wait(duration) {
|
|
272
284
|
return new Promise((resolve) => setTimeout(resolve, duration));
|
|
273
285
|
}
|