@things-factory/auth-ui 7.0.1-rc.1 → 7.0.1-rc.11
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/client/auth-style-sign.ts +2 -1
- package/client/components/abstract-auth-page.ts +7 -2
- package/client/components/abstract-password-reset.ts +2 -0
- package/client/components/abstract-sign.ts +33 -74
- package/client/components/profile-component.ts +27 -87
- package/client/components/user-role-editor.ts +6 -2
- package/client/entries/auth/activate.ts +2 -1
- package/client/entries/auth/checkin.ts +5 -2
- package/client/entries/auth/result.ts +2 -1
- package/client/entries/auth/signin.ts +4 -0
- package/client/entries/auth/signup.ts +3 -0
- package/client/pages/role/role-management.ts +7 -4
- package/dist-client/auth-style-sign.js +2 -1
- package/dist-client/auth-style-sign.js.map +1 -1
- package/dist-client/components/abstract-auth-page.d.ts +1 -0
- package/dist-client/components/abstract-auth-page.js +6 -2
- package/dist-client/components/abstract-auth-page.js.map +1 -1
- package/dist-client/components/abstract-password-reset.js +2 -0
- package/dist-client/components/abstract-password-reset.js.map +1 -1
- package/dist-client/components/abstract-sign.d.ts +1 -1
- package/dist-client/components/abstract-sign.js +29 -62
- package/dist-client/components/abstract-sign.js.map +1 -1
- package/dist-client/components/profile-component.d.ts +1 -1
- package/dist-client/components/profile-component.js +25 -76
- package/dist-client/components/profile-component.js.map +1 -1
- package/dist-client/components/user-role-editor.js +6 -2
- package/dist-client/components/user-role-editor.js.map +1 -1
- package/dist-client/entries/auth/activate.js +2 -1
- package/dist-client/entries/auth/activate.js.map +1 -1
- package/dist-client/entries/auth/checkin.js +5 -2
- package/dist-client/entries/auth/checkin.js.map +1 -1
- package/dist-client/entries/auth/result.js +2 -1
- package/dist-client/entries/auth/result.js.map +1 -1
- package/dist-client/entries/auth/signin.d.ts +1 -0
- package/dist-client/entries/auth/signin.js +3 -0
- package/dist-client/entries/auth/signin.js.map +1 -1
- package/dist-client/entries/auth/signup.js +3 -0
- package/dist-client/entries/auth/signup.js.map +1 -1
- package/dist-client/pages/role/role-management.js +7 -4
- package/dist-client/pages/role/role-management.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -3,7 +3,8 @@ import { css } from 'lit'
|
|
|
3
3
|
export const AUTH_STYLE_SIGN = css`
|
|
4
4
|
:host {
|
|
5
5
|
display: flex;
|
|
6
|
-
background-color: var(--
|
|
6
|
+
background-color: var(--md-sys-color-primary);
|
|
7
|
+
color: var(--md-sys-color-on-primary);
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
:host *:focus {
|
|
@@ -84,6 +84,10 @@ export abstract class AbstractAuthPage extends localize(i18next)(LitElement) {
|
|
|
84
84
|
description: string
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
get autocompletable() {
|
|
88
|
+
return false
|
|
89
|
+
}
|
|
90
|
+
|
|
87
91
|
render() {
|
|
88
92
|
const { disableUserFavoredLanguage, languages } = this.data || {}
|
|
89
93
|
var { icon, title, description } = this.applicationMeta
|
|
@@ -104,6 +108,7 @@ export abstract class AbstractAuthPage extends localize(i18next)(LitElement) {
|
|
|
104
108
|
id="form"
|
|
105
109
|
action="${this.actionUrl}"
|
|
106
110
|
method="post"
|
|
111
|
+
autocomplete=${this.autocompletable ? "on": "off"}
|
|
107
112
|
@keypress=${e => {
|
|
108
113
|
if (e.key == 'Enter') this._onSubmit(e)
|
|
109
114
|
}}
|
|
@@ -184,7 +189,7 @@ export abstract class AbstractAuthPage extends localize(i18next)(LitElement) {
|
|
|
184
189
|
label=${String(i18next.t('field.email'))}
|
|
185
190
|
required
|
|
186
191
|
.value=${email}
|
|
187
|
-
autocomplete="
|
|
192
|
+
autocomplete="off"
|
|
188
193
|
autocapitalize="off"
|
|
189
194
|
><md-icon slot="leading-icon">mail</md-icon></md-filled-text-field
|
|
190
195
|
>
|
|
@@ -194,7 +199,7 @@ export abstract class AbstractAuthPage extends localize(i18next)(LitElement) {
|
|
|
194
199
|
name="password"
|
|
195
200
|
type="password"
|
|
196
201
|
label=${String(i18next.t('field.password'))}
|
|
197
|
-
autocomplete="
|
|
202
|
+
autocomplete="off"
|
|
198
203
|
required
|
|
199
204
|
><md-icon slot="leading-icon">vpn_key</md-icon></md-filled-text-field
|
|
200
205
|
>
|
|
@@ -86,6 +86,7 @@ export abstract class AbstractPasswordReset extends AbstractAuthPage {
|
|
|
86
86
|
.pattern=${this.passwordPattern}
|
|
87
87
|
helper=${this.passwordHelp}
|
|
88
88
|
helperPersistent
|
|
89
|
+
autocomplete="off"
|
|
89
90
|
@input=${e => {
|
|
90
91
|
var val = e.target.value
|
|
91
92
|
this.confirmPass.setAttribute('pattern', val.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '[$&]'))
|
|
@@ -100,6 +101,7 @@ export abstract class AbstractPasswordReset extends AbstractAuthPage {
|
|
|
100
101
|
name="confirm-password"
|
|
101
102
|
type="password"
|
|
102
103
|
label=${String(i18next.t('field.confirm password'))}
|
|
104
|
+
autocomplete="off"
|
|
103
105
|
required
|
|
104
106
|
></md-filled-text-field>
|
|
105
107
|
</div>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { html, nothing } from 'lit'
|
|
2
|
-
import base64url from 'base64url'
|
|
3
|
-
|
|
4
1
|
import '@operato/i18n/ox-i18n.js'
|
|
5
2
|
|
|
3
|
+
import { html, nothing } from 'lit'
|
|
4
|
+
import { startAuthentication } from '@simplewebauthn/browser'
|
|
5
|
+
|
|
6
6
|
import { i18next } from '@operato/i18n'
|
|
7
7
|
import { notify } from '@operato/layout'
|
|
8
8
|
|
|
@@ -45,6 +45,7 @@ export abstract class AbstractSign extends AbstractAuthPage {
|
|
|
45
45
|
|
|
46
46
|
get formfields() {
|
|
47
47
|
const email = this.data?.email || ''
|
|
48
|
+
const autocompletable = this.autocompletable
|
|
48
49
|
|
|
49
50
|
return html`
|
|
50
51
|
<input id="redirectTo" type="hidden" name="redirectTo" .value=${this.redirectTo || '/'} />
|
|
@@ -56,7 +57,7 @@ export abstract class AbstractSign extends AbstractAuthPage {
|
|
|
56
57
|
label=${String(i18next.t('field.email'))}
|
|
57
58
|
required
|
|
58
59
|
.value=${email}
|
|
59
|
-
autocomplete
|
|
60
|
+
autocomplete=${autocompletable ? "username" : "off"}
|
|
60
61
|
autocapitalize="off"
|
|
61
62
|
@input=${(e: Event) => {
|
|
62
63
|
const target = e.target as HTMLInputElement
|
|
@@ -74,7 +75,7 @@ export abstract class AbstractSign extends AbstractAuthPage {
|
|
|
74
75
|
name="password"
|
|
75
76
|
type="password"
|
|
76
77
|
label=${String(i18next.t('field.password'))}
|
|
77
|
-
autocomplete
|
|
78
|
+
autocomplete=${autocompletable ? "current-password" : "off"}
|
|
78
79
|
required
|
|
79
80
|
><md-icon slot="leading-icon">vpn_key</md-icon></md-filled-text-field
|
|
80
81
|
>
|
|
@@ -85,84 +86,42 @@ export abstract class AbstractSign extends AbstractAuthPage {
|
|
|
85
86
|
<ox-i18n msgid="field.${this.pageName}"> </ox-i18n>
|
|
86
87
|
</md-elevated-button>
|
|
87
88
|
${isAvailableWebauthn
|
|
88
|
-
? html` <md-icon class="fingerprint" raised @click=${e => this.
|
|
89
|
+
? html` <md-icon class="fingerprint" raised @click=${e => this.authenticateUser()}> fingerprint </md-icon>`
|
|
89
90
|
: nothing}
|
|
90
91
|
</div>
|
|
91
92
|
`
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
async
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
})) as PublicKeyCredential
|
|
119
|
-
|
|
120
|
-
if (!credential) {
|
|
121
|
-
notify({
|
|
122
|
-
level: 'error',
|
|
123
|
-
message: 'can not get user credential'
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const assertionResponse = {
|
|
130
|
-
id: credential.id,
|
|
131
|
-
response: {
|
|
132
|
-
clientDataJSON: base64url.encode(Buffer.from(credential.response.clientDataJSON)),
|
|
133
|
-
authenticatorData: base64url.encode((credential.response as any).authenticatorData),
|
|
134
|
-
signature: base64url.encode((credential.response as any).signature),
|
|
135
|
-
userHandle: (credential.response as any).userHandle
|
|
136
|
-
? base64url.encode((credential.response as any).userHandle)
|
|
137
|
-
: null
|
|
95
|
+
async authenticateUser() {
|
|
96
|
+
try {
|
|
97
|
+
const options = await fetch('/auth/signin-webauthn/challenge').then(res => res.json())
|
|
98
|
+
const assertionResp = await startAuthentication(options)
|
|
99
|
+
const verification = await fetch('/auth/signin-webauthn', {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
headers: {
|
|
102
|
+
'Content-Type': 'application/json'
|
|
103
|
+
},
|
|
104
|
+
body: JSON.stringify(assertionResp)
|
|
105
|
+
}).then(res => res.json())
|
|
106
|
+
|
|
107
|
+
if (verification.verified) {
|
|
108
|
+
const { redirectURL } = verification
|
|
109
|
+
|
|
110
|
+
if (redirectURL) {
|
|
111
|
+
window.location.href = redirectURL
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
notify({
|
|
115
|
+
level: 'error',
|
|
116
|
+
message: verification.message
|
|
117
|
+
})
|
|
138
118
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (credential.authenticatorAttachment) {
|
|
142
|
-
assertionResponse.authenticatorAttachment = credential.authenticatorAttachment as AuthenticatorAttachment
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const signinResponse = await fetch('/auth/signin-webauthn', {
|
|
146
|
-
method: 'POST',
|
|
147
|
-
headers: {
|
|
148
|
-
'Content-Type': 'application/json',
|
|
149
|
-
Accept: 'application/json'
|
|
150
|
-
},
|
|
151
|
-
body: JSON.stringify(assertionResponse),
|
|
152
|
-
credentials: 'include'
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
if (!signinResponse.ok) {
|
|
119
|
+
|
|
120
|
+
} catch (error) {
|
|
156
121
|
notify({
|
|
157
122
|
level: 'error',
|
|
158
|
-
message:
|
|
123
|
+
message: i18next.t('error.authn verification failed')
|
|
159
124
|
})
|
|
160
|
-
} else {
|
|
161
|
-
const { redirectURL } = await signinResponse.json()
|
|
162
|
-
|
|
163
|
-
if (redirectURL) {
|
|
164
|
-
window.location.href = redirectURL
|
|
165
|
-
}
|
|
166
125
|
}
|
|
167
126
|
}
|
|
168
127
|
}
|
|
@@ -7,6 +7,7 @@ import './my-login-history'
|
|
|
7
7
|
import base64url from 'base64url'
|
|
8
8
|
import { css, html, LitElement, nothing } from 'lit'
|
|
9
9
|
import { customElement, property, query, state } from 'lit/decorators.js'
|
|
10
|
+
import { startRegistration } from '@simplewebauthn/browser'
|
|
10
11
|
|
|
11
12
|
import { i18next, localize } from '@operato/i18n'
|
|
12
13
|
import { notify, openPopup } from '@operato/layout'
|
|
@@ -164,7 +165,7 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
|
|
|
164
165
|
|
|
165
166
|
${isAvailableWebauthn
|
|
166
167
|
? html`
|
|
167
|
-
<md-text-button @click=${() => this.
|
|
168
|
+
<md-text-button @click=${() => this.registerUser()}
|
|
168
169
|
>${i18next.t('button.security-key registration')}</md-text-button
|
|
169
170
|
>
|
|
170
171
|
`
|
|
@@ -244,96 +245,35 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
|
|
|
244
245
|
})
|
|
245
246
|
}
|
|
246
247
|
|
|
247
|
-
async
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
credentials: 'include'
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
if (!challenge.ok) {
|
|
259
|
-
notify({
|
|
260
|
-
level: 'error',
|
|
261
|
-
message: await challenge.text()
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
return
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const options = await challenge.json()
|
|
268
|
-
|
|
269
|
-
const credential = (await navigator.credentials.create({
|
|
270
|
-
publicKey: {
|
|
271
|
-
rp: {
|
|
272
|
-
name: this.applicationMeta.title
|
|
273
|
-
},
|
|
274
|
-
user: {
|
|
275
|
-
id: Uint8Array.from(base64url.toBuffer(options.user.id)).buffer,
|
|
276
|
-
name: options.user.name,
|
|
277
|
-
displayName: options.user.displayName
|
|
248
|
+
async registerUser() {
|
|
249
|
+
try {
|
|
250
|
+
const options = await fetch('/auth/register-webauthn/challenge').then(res => res.json())
|
|
251
|
+
const attResp = await startRegistration(options)
|
|
252
|
+
const verification = await fetch('/auth/verify-registration', {
|
|
253
|
+
method: 'POST',
|
|
254
|
+
headers: {
|
|
255
|
+
'Content-Type': 'application/json'
|
|
278
256
|
},
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
257
|
+
body: JSON.stringify(attResp)
|
|
258
|
+
}).then(res => res.json())
|
|
259
|
+
|
|
260
|
+
if (verification.verified) {
|
|
261
|
+
notify({
|
|
262
|
+
level: 'info',
|
|
263
|
+
message: i18next.t('text.user credential registered successfully')
|
|
264
|
+
})
|
|
265
|
+
} else {
|
|
266
|
+
console.error(await verification.text())
|
|
267
|
+
|
|
268
|
+
notify({
|
|
269
|
+
level: 'error',
|
|
270
|
+
message: i18next.t('error.user credential registeration not allowed')
|
|
271
|
+
})
|
|
293
272
|
}
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
if (!credential) {
|
|
273
|
+
} catch (error) {
|
|
297
274
|
notify({
|
|
298
275
|
level: 'error',
|
|
299
|
-
message: '
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
return
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const response = credential.response as AuthenticatorAttestationResponse
|
|
306
|
-
|
|
307
|
-
var body = {
|
|
308
|
-
response: {
|
|
309
|
-
clientDataJSON: Buffer.from(response.clientDataJSON).toString('base64'),
|
|
310
|
-
attestationObject: Buffer.from(response.attestationObject).toString('base64')
|
|
311
|
-
} as any
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (response.getTransports) {
|
|
315
|
-
body.response.transports = response.getTransports()
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const signinResponse = await fetch('/auth/signin-webauthn', {
|
|
319
|
-
method: 'POST',
|
|
320
|
-
headers: {
|
|
321
|
-
'Content-Type': 'application/json',
|
|
322
|
-
Accept: 'application/json'
|
|
323
|
-
},
|
|
324
|
-
body: JSON.stringify(body),
|
|
325
|
-
credentials: 'include'
|
|
326
|
-
})
|
|
327
|
-
|
|
328
|
-
if (!signinResponse.ok) {
|
|
329
|
-
notify({
|
|
330
|
-
level: 'error',
|
|
331
|
-
message: await signinResponse.text()
|
|
332
|
-
})
|
|
333
|
-
} else {
|
|
334
|
-
notify({
|
|
335
|
-
level: 'info',
|
|
336
|
-
message: i18next.t('text.user credential registered successfully')
|
|
276
|
+
message: i18next.t('error.user credential registeration failed')
|
|
337
277
|
})
|
|
338
278
|
}
|
|
339
279
|
}
|
|
@@ -16,6 +16,7 @@ import { store } from '@operato/shell'
|
|
|
16
16
|
import { OxPrompt } from '@operato/popup/ox-prompt.js'
|
|
17
17
|
import { ButtonContainerStyles } from '@operato/styles'
|
|
18
18
|
import { RoleSelector } from './role-selector'
|
|
19
|
+
import { InheritedValueType } from '@operato/data-grist'
|
|
19
20
|
|
|
20
21
|
@customElement('user-role-editor')
|
|
21
22
|
class UserRoleEditor extends connect(store)(LitElement) {
|
|
@@ -275,8 +276,8 @@ class UserRoleEditor extends connect(store)(LitElement) {
|
|
|
275
276
|
async fetchAvailableRoles() {
|
|
276
277
|
const response = await client.query({
|
|
277
278
|
query: gql`
|
|
278
|
-
query {
|
|
279
|
-
roles {
|
|
279
|
+
query roles($inherited: InheritedValueType) {
|
|
280
|
+
roles(inherited: $inherited) {
|
|
280
281
|
items {
|
|
281
282
|
id
|
|
282
283
|
domain {
|
|
@@ -300,6 +301,9 @@ class UserRoleEditor extends connect(store)(LitElement) {
|
|
|
300
301
|
}
|
|
301
302
|
}
|
|
302
303
|
`,
|
|
304
|
+
variables: {
|
|
305
|
+
inherited: InheritedValueType.Include
|
|
306
|
+
},
|
|
303
307
|
context: gqlContext()
|
|
304
308
|
})
|
|
305
309
|
|
|
@@ -20,7 +20,8 @@ export class AuthActivate extends localize(i18next)(LitElement) {
|
|
|
20
20
|
width: 100vw;
|
|
21
21
|
height: 100vh;
|
|
22
22
|
height: 100dvh;
|
|
23
|
-
background-color: var(--
|
|
23
|
+
background-color: var(--md-sys-color-primary);
|
|
24
|
+
color: var(--md-sys-color-on-primary);
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
.wrap {
|
|
@@ -21,12 +21,14 @@ export class AuthCheckIn extends localize(i18next)(LitElement) {
|
|
|
21
21
|
display: flex;
|
|
22
22
|
flex-direction: column;
|
|
23
23
|
margin: auto;
|
|
24
|
-
background-color: var(--
|
|
24
|
+
background-color: var(--md-sys-color-primary);
|
|
25
|
+
color: var(--md-sys-color-on-primary);
|
|
25
26
|
height: 100vh;
|
|
26
27
|
height: 100dvh;
|
|
27
28
|
}
|
|
28
29
|
.header {
|
|
29
30
|
background-color: var(--md-sys-color-primary);
|
|
31
|
+
color: var(--md-sys-color-on-primary);
|
|
30
32
|
height: var(--checkin-header-height);
|
|
31
33
|
}
|
|
32
34
|
.content {
|
|
@@ -38,6 +40,7 @@ export class AuthCheckIn extends localize(i18next)(LitElement) {
|
|
|
38
40
|
margin: var(--margin-wide) 0;
|
|
39
41
|
padding: 0;
|
|
40
42
|
background-color: var(--md-sys-color-surface);
|
|
43
|
+
color: var(--md-sys-color-on-surface);
|
|
41
44
|
border-radius: var(--border-radius);
|
|
42
45
|
border: var(--border-dim-color);
|
|
43
46
|
list-style: none;
|
|
@@ -47,13 +50,13 @@ export class AuthCheckIn extends localize(i18next)(LitElement) {
|
|
|
47
50
|
margin-bottom: -1px;
|
|
48
51
|
padding: var(--padding-default) var(--padding-wide);
|
|
49
52
|
font-size: 18px;
|
|
50
|
-
color: var(--md-sys-color-secondary);
|
|
51
53
|
text-align: left;
|
|
52
54
|
|
|
53
55
|
cursor: pointer;
|
|
54
56
|
}
|
|
55
57
|
li:hover {
|
|
56
58
|
background-color: var(--md-sys-color-primary-container);
|
|
59
|
+
color: var(--md-sys-color-on-primary-container);
|
|
57
60
|
}
|
|
58
61
|
li span {
|
|
59
62
|
display: block;
|
|
@@ -18,7 +18,8 @@ export class AuthResult extends localize(i18next)(LitElement) {
|
|
|
18
18
|
width: 100vw;
|
|
19
19
|
height: 100vh;
|
|
20
20
|
height: 100dvh;
|
|
21
|
-
background-color: var(--
|
|
21
|
+
background-color: var(--md-sys-color-primary);
|
|
22
|
+
color: var(--md-sys-color-on-primary);
|
|
22
23
|
}
|
|
23
24
|
.wrap {
|
|
24
25
|
display: block;
|
|
@@ -47,6 +47,7 @@ export class AuthSignup extends AbstractSign {
|
|
|
47
47
|
required
|
|
48
48
|
.value=${email}
|
|
49
49
|
autocapitalize="off"
|
|
50
|
+
autocomplete="off"
|
|
50
51
|
@input=${(e: Event) => {
|
|
51
52
|
const target = e.target as HTMLInputElement
|
|
52
53
|
if (target.validity.typeMismatch) {
|
|
@@ -67,6 +68,7 @@ export class AuthSignup extends AbstractSign {
|
|
|
67
68
|
helper=${this.passwordHelp || ''}
|
|
68
69
|
helperPersistent
|
|
69
70
|
required
|
|
71
|
+
autocomplete="off"
|
|
70
72
|
@input=${e => {
|
|
71
73
|
var val = e.target.value
|
|
72
74
|
this.confirmPass.setAttribute('pattern', val.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '[$&]'))
|
|
@@ -80,6 +82,7 @@ export class AuthSignup extends AbstractSign {
|
|
|
80
82
|
id="confirm-password"
|
|
81
83
|
name="confirm-password"
|
|
82
84
|
type="password"
|
|
85
|
+
autocomplete="off"
|
|
83
86
|
label=${String(i18next.t('field.confirm password'))}
|
|
84
87
|
required
|
|
85
88
|
>
|
|
@@ -10,7 +10,7 @@ import { connect } from 'pwa-helpers/connect-mixin.js'
|
|
|
10
10
|
|
|
11
11
|
import { client, gqlContext } from '@operato/graphql'
|
|
12
12
|
import { i18next } from '@operato/i18n'
|
|
13
|
-
import { PageView, store } from '@operato/shell'
|
|
13
|
+
import { InheritedValueType, PageView, store } from '@operato/shell'
|
|
14
14
|
|
|
15
15
|
@customElement('role-management')
|
|
16
16
|
class RoleManagement extends connect(store)(PageView) {
|
|
@@ -105,8 +105,8 @@ class RoleManagement extends connect(store)(PageView) {
|
|
|
105
105
|
async fetchRoles() {
|
|
106
106
|
const response = await client.query({
|
|
107
107
|
query: gql`
|
|
108
|
-
query roles($sortings: [Sorting!]) {
|
|
109
|
-
roles(sortings: $sortings) {
|
|
108
|
+
query roles($sortings: [Sorting!], $inherited: InheritedValueType) {
|
|
109
|
+
roles(sortings: $sortings, inherited: $inherited) {
|
|
110
110
|
items {
|
|
111
111
|
id
|
|
112
112
|
name
|
|
@@ -120,7 +120,10 @@ class RoleManagement extends connect(store)(PageView) {
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
`,
|
|
123
|
-
variables: {
|
|
123
|
+
variables: {
|
|
124
|
+
sortings: [{ name: 'name' }],
|
|
125
|
+
inherited: InheritedValueType.Only
|
|
126
|
+
},
|
|
124
127
|
context: gqlContext()
|
|
125
128
|
})
|
|
126
129
|
|
|
@@ -2,7 +2,8 @@ import { css } from 'lit';
|
|
|
2
2
|
export const AUTH_STYLE_SIGN = css `
|
|
3
3
|
:host {
|
|
4
4
|
display: flex;
|
|
5
|
-
background-color: var(--
|
|
5
|
+
background-color: var(--md-sys-color-primary);
|
|
6
|
+
color: var(--md-sys-color-on-primary);
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
:host *:focus {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-style-sign.js","sourceRoot":"","sources":["../client/auth-style-sign.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"auth-style-sign.js","sourceRoot":"","sources":["../client/auth-style-sign.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6LjC,CAAA","sourcesContent":["import { css } from 'lit'\n\nexport const AUTH_STYLE_SIGN = css`\n :host {\n display: flex;\n background-color: var(--md-sys-color-primary);\n color: var(--md-sys-color-on-primary);\n }\n\n :host *:focus {\n outline: none;\n }\n\n :host * {\n box-sizing: border-box;\n }\n\n .wrap {\n display: block;\n width: 450px;\n min-width: 350px;\n margin: 0 auto;\n padding-bottom: 100px;\n text-align: center;\n }\n\n .auth-brand {\n color: var(--md-sys-color-on-primary);\n }\n\n .auth-brand img {\n margin: 15% auto 5px auto;\n width: 100px;\n border: 3px solid var(--md-sys-color-on-primary);\n border-radius: 25px;\n box-shadow: var(--box-shadow);\n }\n .name {\n display: block;\n font: var(--auth-brand-name);\n text-shadow: var(--auth-brand-name-shadow);\n }\n .auth-brand .welcome-msg {\n font: var(--auth-brand-welcome-msg);\n }\n .auth-form {\n display: grid;\n grid-gap: var(--margin-default);\n grid-template-columns: 1fr 1fr;\n }\n\n form {\n grid-column: 1 / -1;\n display: grid;\n grid-template-columns: 1fr 1fr;\n grid-gap: var(--margin-default);\n align-items: center;\n }\n\n h3 {\n grid-column: 1 / -1;\n margin: 50px 0 0 0;\n font: var(--auth-title-font);\n color: var(--auth-title-color, var(--md-sys-color-on-primary));\n text-transform: uppercase;\n }\n\n .field {\n grid-column: 1 / -1;\n text-align: left;\n }\n\n .submit-buttons-container {\n grid-column: 1 / -1;\n text-align: center;\n\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .fingerprint {\n color: var(--md-sys-color-on-primary);\n border: 1.5px solid var(--md-sys-color-on-primary);\n border-radius: 20%;\n width: 36px;\n height: 36px;\n }\n\n .field md-filled-text-field {\n grid-column: 1 / -1;\n width: 100%;\n }\n\n md-text-button,\n md-elevated-button {\n grid-column: 1 / -1;\n flex: 1;\n }\n\n .wrap .link {\n text-decoration: none;\n justify-self: flex-start;\n }\n\n .wrap .link md-text-button {\n --md-text-button-label-text-color: var(--md-sys-color-on-primary);\n --md-text-button-focus-label-text-color: var(--md-sys-color-on-primary);\n --md-text-button-hover-label-text-color: var(--md-sys-color-on-primary);\n }\n\n .wrap .link md-icon {\n color: var(--md-sys-color-on-primary);\n }\n\n #locale-area {\n display: flex;\n grid-column: 1 / -1;\n padding: 0 var(--padding-default);\n }\n\n #locale-area > label {\n display: flex;\n align-items: center;\n color: var(--md-sys-color-on-primary);\n --md-icon-size: 16px;\n }\n\n #locale-selector {\n font-size: 16px;\n width: 100%;\n }\n\n #locale-selector {\n --i18n-selector-field-border: none;\n --i18n-selector-field-background-color: none;\n --i18n-selector-field-font-size: 14px;\n --i18n-selector-field-color: var(--md-sys-color-on-primary);\n }\n\n .lottie-container {\n width: 100%;\n height: 300px;\n position: absolute;\n left: 0;\n bottom: 0;\n pointer-events: none;\n }\n .lottie-container lottie-player {\n position: absolute;\n bottom: -6%;\n width: 100%;\n height: auto;\n }\n\n @media (max-width: 450px) {\n .wrap {\n width: 85%;\n min-width: 320px;\n }\n .auth-form {\n grid-template-columns: 1fr;\n }\n .auth-brand img {\n margin: 12% auto 5px auto;\n width: 75px;\n }\n h3 {\n margin: 15px 0 0 0;\n }\n .lottie-container {\n overflow: hidden;\n height: 200px;\n pointer-events: none;\n }\n .lottie-container lottie-player {\n width: 1200px;\n left: -30%;\n }\n }\n\n @media screen and (min-width: 1400px) {\n .wrap {\n padding-bottom: 150px;\n }\n }\n @media screen and (min-width: 2500px) {\n .wrap {\n padding-bottom: 280px;\n }\n }\n`\n"]}
|
|
@@ -16,6 +16,7 @@ export declare abstract class AbstractAuthPage extends AbstractAuthPage_base {
|
|
|
16
16
|
redirectTo?: string;
|
|
17
17
|
formEl: HTMLFormElement;
|
|
18
18
|
private _applicationMeta?;
|
|
19
|
+
get autocompletable(): boolean;
|
|
19
20
|
render(): import("lit-html").TemplateResult<1>;
|
|
20
21
|
firstUpdated(): void;
|
|
21
22
|
updated(changed: any): void;
|
|
@@ -14,6 +14,9 @@ import { ScrollbarStyles } from '@operato/styles';
|
|
|
14
14
|
import { isSafari } from '@operato/utils';
|
|
15
15
|
import { AUTH_STYLE_SIGN } from '../auth-style-sign.js';
|
|
16
16
|
export class AbstractAuthPage extends localize(i18next)(LitElement) {
|
|
17
|
+
get autocompletable() {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
17
20
|
render() {
|
|
18
21
|
const { disableUserFavoredLanguage, languages } = this.data || {};
|
|
19
22
|
var { icon, title, description } = this.applicationMeta;
|
|
@@ -33,6 +36,7 @@ export class AbstractAuthPage extends localize(i18next)(LitElement) {
|
|
|
33
36
|
id="form"
|
|
34
37
|
action="${this.actionUrl}"
|
|
35
38
|
method="post"
|
|
39
|
+
autocomplete=${this.autocompletable ? "on" : "off"}
|
|
36
40
|
@keypress=${e => {
|
|
37
41
|
if (e.key == 'Enter')
|
|
38
42
|
this._onSubmit(e);
|
|
@@ -107,7 +111,7 @@ export class AbstractAuthPage extends localize(i18next)(LitElement) {
|
|
|
107
111
|
label=${String(i18next.t('field.email'))}
|
|
108
112
|
required
|
|
109
113
|
.value=${email}
|
|
110
|
-
autocomplete="
|
|
114
|
+
autocomplete="off"
|
|
111
115
|
autocapitalize="off"
|
|
112
116
|
><md-icon slot="leading-icon">mail</md-icon></md-filled-text-field
|
|
113
117
|
>
|
|
@@ -117,7 +121,7 @@ export class AbstractAuthPage extends localize(i18next)(LitElement) {
|
|
|
117
121
|
name="password"
|
|
118
122
|
type="password"
|
|
119
123
|
label=${String(i18next.t('field.password'))}
|
|
120
|
-
autocomplete="
|
|
124
|
+
autocomplete="off"
|
|
121
125
|
required
|
|
122
126
|
><md-icon slot="leading-icon">vpn_key</md-icon></md-filled-text-field
|
|
123
127
|
>
|