From 4e640118a8158c1108b66aafcf6f5e67fc69d7e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ju=CC=88rg=20Lehni?= Date: Sun, 18 Oct 2026 01:55:25 +0200 Subject: [PATCH] Server, Admin: Require confirmed access for security changes - Add app.auth.reconfirmAccessAfter: logins and 'post confirm-access' confirm the access of the session for this long, default '10m' - Answer 403 with confirmationRequired to password and username changes, passkey registrations and removals, and turning TOTP on and off without it, which no longer takes a code - Confirm with the password and the code of users with TOTP, counting wrong codes, or with a passkey, keeping registration and authentication challenges apart - Reject passwords in the patch and put actions of UsersController, which only change through 'post password', and hashes in all of them - Ask to confirm the access in the admin when requests answer confirmationRequired and send them again, and always offer the Security dialog for the password --- CHANGELOG.md | 3 + docs/api/admin/admin.md | 19 +- docs/api/server/application.md | 1 + docs/api/server/controllers.md | 42 +- docs/guide/admin/views.md | 10 +- docs/guide/server/controllers.md | 88 +++- packages/admin/src/DitoAdmin.js | 3 +- .../admin/src/components/DitoAccount.test.js | 196 +++++++- packages/admin/src/components/DitoAccount.vue | 6 +- packages/admin/src/components/DitoRoot.vue | 41 ++ packages/admin/src/types/DitoTypeButton.vue | 11 +- packages/admin/src/utils/Session.test.js | 90 +++- packages/admin/src/utils/Session.ts | 59 ++- packages/admin/src/utils/security.js | 33 +- packages/admin/src/utils/services.ts | 16 +- packages/admin/types/index.d.ts | 6 + .../server/src/controllers/UsersController.js | 293 ++++++++--- packages/server/types/index.d.ts | 14 + tests/e2e/account/admin/index.spec.ts | 22 +- tests/e2e/account/views.ts | 3 +- .../admin/__screenshots__/totp-turn-off.png | Bin 58787 -> 59116 bytes .../e2e/passkeys/admin/security.screenshot.ts | 4 +- tests/e2e/passkeys/admin/totp.spec.ts | 5 +- .../admin/__screenshots__/account-menu.png | Bin 55762 -> 56992 bytes tests/server/confirmed-access.test.ts | 466 ++++++++++++++++++ tests/server/passkeys.test.ts | 6 +- tests/server/password-reset.test.ts | 25 +- tests/server/totp.test.ts | 39 +- tests/server/users-controller.test.ts | 169 ++++++- tests/types/server/application.test-d.ts | 8 + 30 files changed, 1405 insertions(+), 273 deletions(-) create mode 100644 tests/server/confirmed-access.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d7908ec31..4b62583a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ version, see the upgrade guides in `docs/`. - **server:** Dito.js handles logins and sessions itself instead of through koa-passport, which `@ditojs/server` no longer depends on. `ctx.req.user` and custom Passport strategies are gone, `app.passport` has no effect, and `UserMixin.login()` reads the credentials only from the request body. Existing sessions stay valid. - **server:** Changing a user's password ends their other sessions. +- **server:** The `patch` and `put` actions of `UsersController` no longer accept passwords, which only change through `'post password'`: apps that change passwords in a form through `PATCH /users/self` use the admin's Security dialog or `'post password'` instead. Admins can't set the passwords of other users through them any more, only password resets can. +- **server:** The actions of `UsersController` no longer accept the `hash` of the password, and changing the username through `patch` and `put` needs confirmed access. - **server:** `UsersController` only treats users of its own model class as logged in, not users of its sub-classes. - **server:** `Controller.setup()` and `UserMixin.sessionQuery()` are internal: apps override `initialize()` and set `sessionScope` instead. - **admin:** Dialogs validate their data on submit, like forms, and only resolve once it's valid. @@ -20,6 +22,7 @@ version, see the upgrade guides in `docs/`. - **server, admin:** Two-factor authentication with TOTP, with `app.auth.totp` and `login: { totp: true }`, see [Two-factor authentication](docs/guide/server/controllers.md#two-factor-authentication). - **server, admin:** Password resets by email, with `app.auth.passwordReset` and `login: { passwordReset: true }`, see [Password reset](docs/guide/server/controllers.md#password-reset). - **server, admin:** Users change their password through `'post password'` of `UsersController`, and the Security dialog of the account menu. +- **server, admin:** Security changes need confirmed access, from a recent login or `'post confirm-access'` within `app.auth.reconfirmAccessAfter`. Turning off TOTP takes it instead of a code. See [Confirmed access](docs/guide/server/controllers.md#confirmed-access). - **server:** Authorize functions are called with their controller as `this`, in relations the `RelationController`. - **server:** `CredentialMixin` and `CredentialModel` store the credentials of all user models in one table, set in `app.auth.credentials`. - **admin:** `settings.minDuration` keeps dialogs visible for a minimum time. diff --git a/docs/api/admin/admin.md b/docs/api/admin/admin.md index 26bebf557..d9d78dcd4 100644 --- a/docs/api/admin/admin.md +++ b/docs/api/admin/admin.md @@ -45,8 +45,8 @@ new DitoAdmin(element, options) | `options.dito` | [`GlobalConfig`](#globalconfig) | The global configuration. Defaults to `globalThis.dito`, which `AdminController` provides. | | `options.login.additionalComponents` | `Components` | Components added to the login dialog after `username` and `password`. Their values are sent with the credentials. | | `options.login.redirectAfterLogin` | `string` | A URL to load after a successful login, instead of resolving the views in place. | -| `options.login.passkeys` | `boolean` | Adds a Sign In with Passkey button to the login dialog, and a Security item to the account menu, whose dialog changes the user's password through [`api.users.password`](#users), and lists, adds and removes the user's passkeys, named after their providers. Needs passkeys on the server, see [Passkeys](../../guide/server/controllers.md#passkeys), and the peer dependency `@simplewebauthn/browser`. | -| `options.login.totp` | `boolean` | Adds a Security item to the account menu, whose dialog changes the user's password, sets up two-factor authentication with TOTP, with a QR code for the authenticator app and the backup codes, and turns it off again. Needs TOTP on the server, see [Two-factor authentication](../../guide/server/controllers.md#two-factor-authentication), and the peer dependency `lean-qr`. The login dialog asks for the code whenever the server requires one. | +| `options.login.passkeys` | `boolean` | Adds a Sign In with Passkey button to the login dialog, and lists, adds and removes the user's passkeys, named after their providers, in the Security dialog of the account menu, which always changes the user's password through [`api.users.password`](#users). Needs passkeys on the server, see [Passkeys](../../guide/server/controllers.md#passkeys), and the peer dependency `@simplewebauthn/browser`. | +| `options.login.totp` | `boolean` | Sets up two-factor authentication with TOTP in the Security dialog of the account menu, with a QR code for the authenticator app and the backup codes, and turns it off again. Needs TOTP on the server, see [Two-factor authentication](../../guide/server/controllers.md#two-factor-authentication), and the peer dependency `lean-qr`. The login dialog asks for the code whenever the server requires one. | | `options.login.passwordReset` | `boolean` | Adds a Forgot password? button to the login dialog, which asks for the username and posts it to [`api.users.passwordReset`](#users), for the server to email a link. The link opens the admin with `#password-reset=` and the token, which asks for the new password, posts it to `api.users.passwordResetConfirm`, asking again with the server's validation errors, and removes the token from the URL, before asking to log in. Needs password resets on the server, see [Password reset](../../guide/server/controllers.md#password-reset). | | `options.spinner` | `{ size?: string, color?: string } \| null` | Size and color of the loading spinner in the header. `null` hides it. | | `options.account.settings` | `Form \| Promise \| () => …` | The form of the account settings, which the account menu opens at `/account/settings`, to edit the logged-in user through [`api.users.self`](#users). Also a promise or a function returning it, e.g. a dynamic `import()`. Its `label` is the view's label, default `'Settings'`. See [Account settings](../../guide/admin/views.md#account-settings). | @@ -160,23 +160,26 @@ api: { passkeys: { path: 'passkeys' }, loginCode: { path: 'login-code', method: 'post' }, totp: { path: 'totp' }, - totpConfirm: { path: 'totp-confirm', method: 'post' } + totpConfirm: { path: 'totp-confirm', method: 'post' }, + confirmAccess: { path: 'confirm-access', method: 'post' } } } ``` `users.path` is the collection of the user model; `users: 'users'` is short for `users: { path: 'users' }`. `login`, `logout`, `session`, `self`, -`password`, `loginCode`, the password reset resources of `login.passwordReset`, -the passkey resources of `login.passkeys` and the TOTP resources of -`login.totp` are resources nested under it, shown here with +`password`, `loginCode`, `confirmAccess`, the password reset resources of +`login.passwordReset`, the passkey resources of `login.passkeys` and the TOTP +resources of `login.totp` are resources nested under it, shown here with their defaults, so the admin logs in through `POST users/login`. The server's `UsersController` provides them. The admin reads `user` from the responses of `login` and `session`, and `success` from the response of `logout`. The [account settings](#parameters) load the logged-in user from `self` and patch -it there, after which the admin fetches the session's user again. See [AdminContext](#session) for -the session that uses them. +it there, after which the admin fetches the session's user again. Requests +that answer `confirmationRequired` ask for the password, the TOTP code or a +passkey, post them to `confirmAccess` and are sent again. See +[AdminContext](#session) for the session that uses them. ### defaults diff --git a/docs/api/server/application.md b/docs/api/server/application.md index 77f589eed..c6054a2ed 100644 --- a/docs/api/server/application.md +++ b/docs/api/server/application.md @@ -144,6 +144,7 @@ app: { | `passwordReset` | `object` | Enables password resets by email in [`UsersController`](controllers.md#userscontroller), which sends the emails through [`sendPasswordReset()`](controllers.md#sendpasswordreset). Needs `credentials`, `credentials.secret` and a `session` stored on the server, else the setup fails. | | `passwordReset.confirmUrl` | `string` | The URL of the page that the emails link to, with a `{token}` placeholder for the token, best in the fragment, e.g. `'https://example.com/admin/#password-reset={token}'` for the admin's [`login.passwordReset`](../admin/admin.md). One URL for the whole app: all users controllers send links to it. | | `passwordReset.expires` | `number \| string` | How long the links are valid, in milliseconds or as a duration string, e.g. `'30m'`. Default `'1h'`. | +| `reconfirmAccessAfter` | `number \| string` | How long logins and `'post confirm-access'` confirm the access for the security changes of [`UsersController`](controllers.md#userscontroller), in milliseconds or as a duration string. Default `'10m'`. | Passkeys need the optional peer dependency `@simplewebauthn/server`, TOTP needs `otpauth`. diff --git a/docs/api/server/controllers.md b/docs/api/server/controllers.md index 993b8e7af..b64542d6d 100644 --- a/docs/api/server/controllers.md +++ b/docs/api/server/controllers.md @@ -804,7 +804,21 @@ inherited actions, see [`allow`](#allow): a subclass that sets | `'post logout'` | `{ success, authenticated }` | | `'get session'` | `{ authenticated, user }` | | `'get self'` | The authenticated user through `member.get`, or `null`. | -| `'post password'` | Changes the password of the authenticated user to `password` in the body, after verifying `currentPassword`: `{ success }`, or 400 if `currentPassword` is wrong. The new password is validated by the user model, e.g. its `minLength`. | +| `'post password'` | Changes the password of the authenticated user to `password` in the body, after verifying `currentPassword`: `{ success }`, or 400 if `currentPassword` is wrong. The new password is validated by the user model, e.g. its `minLength`. Needs confirmed access. | +| `'post confirm-access'` | Confirms the access of the authenticated user with `{ password }`, plus `code` for users with TOTP, or with `{ passkey }`, the response of `startAuthentication()` for the options of `'post passkey-login-options'`: `{ success }`, `{ success: false, codeRequired: true }` without a code, or 401 with `'Password is incorrect'`, `'Code is incorrect'` or `'Passkey is incorrect'`. | + +Logins and `'post confirm-access'` confirm the access of the session for +[`app.auth.reconfirmAccessAfter`](./application.md#configappauth). Without +it, `'post password'`, `'post passkey-options'`, `'post passkeys'`, +`'delete passkeys/:id'`, `'post totp'`, `'post totp-confirm'` and +`'delete totp'` answer 403 with +`{ message: 'Confirmation required', confirmationRequired: true }`, see +[Confirmed access](../../guide/server/controllers.md#confirmed-access), and so +do `patch` and `put` actions that change the username. +`hasConfirmedAccess(ctx)` does the same for the `authorize` of other actions. +The `patch`, `put` and `post` actions answer 400 for bodies with the `hash`, +and `patch` and `put` also for bodies with the password. With `relate`, `post` +treats users with ids like updates. With [`app.auth.passkeys`](./application.md#configappauth), users log in with passkeys and manage their own ones through these collection actions, which @@ -838,7 +852,7 @@ collection actions, which aren't routed without it: | `'get totp'` | `{ enabled, backupCodes }`, with the number of the remaining backup codes. | | `'post totp'` | A new secret, kept in the session until it is confirmed: `{ uri, secret }`, the `otpauth://` URI for the QR code of authenticator apps, and the secret in base32 to enter by hand. 409 if TOTP is already set up. | | `'post totp-confirm'` | Stores the secret with `{ code }`, a code of the new secret, and creates 10 backup codes: `{ backupCodes }`, only shown in this response. | -| `'delete totp'` | Turns TOTP off with `{ code }`, a TOTP code or a backup code, and deletes the backup codes: `{ success }`. | +| `'delete totp'` | Turns TOTP off and deletes the backup codes: `{ success }`. Needs confirmed access. | For users with TOTP, `'post login'` with the right password doesn't log in, but responds with status 200 and `{ success: false, codeRequired: true, @@ -846,7 +860,7 @@ authenticated: false, user: null }`, and keeps a pending login in the session, see [`login()`](./model.md#static-login), with a new session id. `'post login-code'` completes it within 5 minutes. Wrong codes answer `'Code is incorrect'` with status 401, also in `'post totp-confirm'` and -`'delete totp'`, and missing or expired pending logins and setups status 400. +`'post confirm-access'`, and missing or expired pending logins and setups status 400. Ten wrong codes in a row lock the account for 15 minutes, across pending logins and sessions: the tenth and all codes during the lock answer `'Too many attempts'` with status 400. Ten wrong codes in `'post totp-confirm'` @@ -870,20 +884,18 @@ user exists or not, and doesn't wait for the email, but the time it takes can still differ, as it stores a token for existing users. Users get at most one email a minute. Limit the rate of requests per client in the proxy in front of the app. Each link works once, until it expires, and setting the password, also -through `'post password'` or a member `patch` or `update`, invalidates the -user's other links. Only users of the controller's `modelClass` are found, -through their `sessionScope`. A subclass that sets `collection.allow` has to -list the password reset actions too, else they aren't routed, and only -controllers that route `'post password-reset'` need to override -`sendPasswordReset()`. All users controllers of the app share the one -`confirmUrl` of `app.auth.passwordReset`. +through `'post password'`, invalidates the user's other links. Only users of +the controller's `modelClass` are found, through their `sessionScope`. A +subclass that sets `collection.allow` has to list the password reset actions +too, else they aren't routed, and only controllers that route +`'post password-reset'` need to override `sendPasswordReset()`. All users +controllers of the app share the one `confirmUrl` of `app.auth.passwordReset`. Any change of a user's `hash` ends the sessions of the user. The session of the -request that changes the password stays logged in after `'post password'`, -`'post password-reset-confirm'` and the member `patch` and `update` of -`UsersController`, e.g. `PATCH /users/self`. Elsewhere, e.g. in other -controllers, custom actions or collection patches, it ends too, unless the -action logs the user in again with `ctx.login(user)` afterwards. +request that changes the password stays logged in after `'post password'` and +`'post password-reset-confirm'`. Elsewhere, e.g. in other controllers or custom +actions, it ends too, unless the action logs the user in again with +`ctx.login(user)` afterwards. TOTP is only asked for by [`UserMixin.login()`](./model.md#static-login), and thus by `'post login'`. `ctx.login(user)` logs in without it, by design, e.g. diff --git a/docs/guide/admin/views.md b/docs/guide/admin/views.md index dbac06766..fa62492c5 100644 --- a/docs/guide/admin/views.md +++ b/docs/guide/admin/views.md @@ -172,16 +172,16 @@ export default { type: 'email', label: 'Email', required: true - }, - // Only sent when a new password is entered: - password: { - type: 'password', - label: 'New Password' } } } ``` +Passwords don't change through `PATCH /api/users/self`, but through +`'post password'` of the `UsersController`, which the Security dialog of the +account menu uses, see +[Confirmed access](../server/controllers.md#confirmed-access). + On the server, `PATCH /users/self` goes through the `patch` member action of the `UsersController`, so the users need to be allowed to patch themselves: `member = { allow: ['get', 'patch'] }`, which only allows users to patch diff --git a/docs/guide/server/controllers.md b/docs/guide/server/controllers.md index 3a19362da..582e3de25 100644 --- a/docs/guide/server/controllers.md +++ b/docs/guide/server/controllers.md @@ -581,8 +581,9 @@ export class Users extends UsersController { - `GET /users/self` returns the logged-in user. - In member routes, `self` stands for the logged-in user, e.g. `PATCH /users/self` runs the member's `patch` action on them, if it is - allowed. The admin's [account settings](../admin/views.md#account-settings) - use it. + allowed, but doesn't change passwords, see + [Confirmed access](#confirmed-access). The admin's + [account settings](../admin/views.md#account-settings) use it. `authorize` on the controller doesn't apply to login, logout and session, which authenticate, so users can always log in; `self` follows it. Member @@ -710,7 +711,8 @@ const { user } = response Ten wrong codes in a row lock the account for 15 minutes. `GET /users/totp` tells whether TOTP is set up and how many backup codes are left, and -`DELETE /users/totp` with `{ code }` turns it off. Only password logins through +`DELETE /users/totp` turns it off, with +[confirmed access](#confirmed-access). Only password logins through `login()` ask for the code, while `ctx.login(user)` logs in without it, e.g. to impersonate users. The admin's login dialog asks for the code, and its `login.totp` option sets up and turns off @@ -778,6 +780,86 @@ the request to its login dialog, with `confirmUrl` pointing to the admin, e.g. `'https://example.com/admin/#password-reset={token}'`, see [`DitoAdmin`](../../api/admin/admin.md#parameters). +### Confirmed access + +Security changes need confirmed access, so that a session alone, e.g. a stolen +one, can't change how the account logs in: changing the password through +`'post password'` or the username, adding and removing passkeys, and turning +TOTP on and off. Logging in confirms the access, also with a passkey or with +the TOTP code, for the time of `reconfirmAccessAfter`: + +```js +// config/index.js +export default { + app: { + auth: { + reconfirmAccessAfter: '10m' // The default + } + } +} +``` + +Later, these actions answer status 403 with `confirmationRequired`, and the +user confirms the access again with their password, plus the code of their +authenticator app or a backup code if they use TOTP, or with a passkey: + +```js +let response = await post('confirm-access', { password }) +if (response.codeRequired) { + response = await post('confirm-access', { password, code: '123456' }) +} +// Or with a passkey, through the options of passkey logins: +const optionsJSON = await post('passkey-login-options') +await post('confirm-access', { + passkey: await startAuthentication({ optionsJSON }) +}) +``` + +Wrong codes count towards the lock of the account, as in logins. Passwords +only change through `'post password'`: the `patch` and `put` actions of users +controllers answer 400 for bodies with a password or its `hash`, while `post` +still creates users with a password. Changing the username through them needs +confirmed access, as password resets go to it. The admin asks to confirm the +access whenever an action answers `confirmationRequired`, and then repeats the +action. + +Protect other changes the same way, e.g. the `email` that the app sends the +password resets to, or custom actions with +`authorize: { 'post export': ctx => this.hasConfirmedAccess(ctx) }`: + +```js +export class Users extends UsersController { + modelClass = User + + member = { + allow: ['get', 'patch'], + + async patch(ctx, modify) { + const { email } = ctx.request.body + if (email !== undefined) { + // Compare with the user that is patched, not the logged-in one: + const user = await this.modelClass.query().findById(ctx.memberId) + // `hasConfirmedAccess()` throws 403 with `confirmationRequired` without + // confirmed access, and returns `false` for users of other models: + if (email !== user?.email && !this.hasConfirmedAccess(ctx)) { + throw new AuthorizationError() + } + } + return super.patch(ctx, modify) + } + } +} +``` + +Passwords set through other routes of the app aren't covered: relations and +graphs that include user models, other controllers, and new users created +through an authorized collection `post`. Keep such routes to admins. Deleting +users through the member `delete`, e.g. `DELETE /users/self`, doesn't need +confirmed access either: apps that allow it can protect it with +`authorize: { delete: ctx => this.hasConfirmedAccess(ctx) && '$self' }` in +`member`, keeping `'$self'`, which the setting replaces, so that users only +delete themselves. + ## Admin The `AdminController` serves the admin interface at its path. In development diff --git a/packages/admin/src/DitoAdmin.js b/packages/admin/src/DitoAdmin.js index 247595367..a148944c7 100644 --- a/packages/admin/src/DitoAdmin.js +++ b/packages/admin/src/DitoAdmin.js @@ -66,7 +66,8 @@ export default class DitoAdmin { passkeyLogin: 'post', passkeyOptions: 'post', loginCode: 'post', - totpConfirm: 'post' + totpConfirm: 'post', + confirmAccess: 'post' } for (const [name, method] of Object.entries(methods)) { users[name] = getResource(users[name] || hyphenate(name), { diff --git a/packages/admin/src/components/DitoAccount.test.js b/packages/admin/src/components/DitoAccount.test.js index 48df8986e..490296407 100644 --- a/packages/admin/src/components/DitoAccount.test.js +++ b/packages/admin/src/components/DitoAccount.test.js @@ -26,8 +26,8 @@ describe('DitoAccount', () => { const logout = vi.spyOn(session, 'logout').mockResolvedValue() const { button, items } = await openMenu(admin) expect(button.text()).toBe('tester') - expect(items.map(item => item.text())).toEqual(['Sign Out']) - await items[0].trigger('click') + expect(items.map(item => item.text())).toEqual(['Security', 'Sign Out']) + await items[1].trigger('click') expect(logout).toHaveBeenCalledOnce() expect(button.attributes('aria-expanded')).toBe('false') }) @@ -78,7 +78,11 @@ describe('DitoAccount', () => { it('opens the settings of the user from the menu', async () => { const { admin, request } = await mountWithSettings() const { items } = await openMenu(admin) - expect(items.map(item => item.text())).toEqual(['Settings', 'Sign Out']) + expect(items.map(item => item.text())).toEqual([ + 'Settings', + 'Security', + 'Sign Out' + ]) await items[0].trigger('click') await flushPromises() expect(admin.router.currentRoute.value.path).toBe('/account/settings') @@ -119,8 +123,10 @@ describe('DitoAccount', () => { describe('security', () => { // A server with passkeys, which adds and deletes them, and changes the - // password 'secret': + // password 'secret'. Without `isConfirmed`, the changes need the access + // confirmed with the password first. `login` sets the admin's options: const mountWithPasskeys = async ({ + login = { passkeys: true }, passkeys = [ { id: 1, @@ -128,10 +134,33 @@ describe('DitoAccount', () => { createdAt: '2026-01-02T12:00:00Z', lastUsedAt: null } - ] + ], + isConfirmed = true } = {}) => { const request = vi.fn(({ method = 'get', url, data }) => { - if (url === '/users/session') { + const isChange = method !== 'get' && url !== '/users/confirm-access' + if (isChange && !isConfirmed) { + throw Object.assign(new Error('Request failed'), { + response: { + status: 403, + data: { + message: 'Confirmation required', + confirmationRequired: true + } + } + }) + } else if (url === '/users/confirm-access') { + isConfirmed = data.password === 'secret' + if (!isConfirmed) { + throw Object.assign(new Error('Request failed'), { + response: { + status: 401, + data: { message: 'Password is incorrect' } + } + }) + } + return { data: { success: true } } + } else if (url === '/users/session') { return { data: { user: { id: 1, username: 'tester' } } } } else if (url === '/users/passkeys' && method === 'get') { return { data: passkeys } @@ -168,7 +197,7 @@ describe('DitoAccount', () => { views, api: { users: { path: 'users' } }, request, - options: { login: { passkeys: true } } + options: { login } }) const { items } = await openMenu(admin) expect(items.map(item => item.text())).toEqual(['Security', 'Sign Out']) @@ -266,6 +295,109 @@ describe('DitoAccount', () => { ) }) + const confirmAccess = async (admin, password) => { + const dialog = getLastDialog(admin) + expect(dialog.attributes('aria-label')).toBe('Confirm Access') + await enterValue(dialog.find('input[name="password"]'), password) + await dialog.find('button[type="submit"]').trigger('click') + await flushPromises() + } + + describe('confirmed access', () => { + it('confirms the access, and then adds the passkey', async () => { + const { admin, request, getRows } = await mountWithPasskeys({ + isConfirmed: false + }) + await findButton(admin, 'Add Passkey').trigger('click') + await flushPromises() + await confirmAccess(admin, 'secret') + expect(request).toHaveBeenCalledWith( + expect.objectContaining({ + method: 'post', + url: '/users/confirm-access', + data: { password: 'secret' } + }) + ) + expect( + request.mock.calls.filter( + ([{ url }]) => url === '/users/passkey-options' + ) + ).toHaveLength(2) + expect(getRows()).toEqual([ + '1PasswordJanuary 2, 2026Never', + 'PasskeyJanuary 3, 2026Never' + ]) + }) + + it('asks again after a wrong password', async () => { + const { admin, getRows } = await mountWithPasskeys({ + isConfirmed: false + }) + await findButton(admin, 'Add Passkey').trigger('click') + await flushPromises() + await confirmAccess(admin, 'wrong') + expect(admin.wrapper.text()).toContain('Password is incorrect') + await confirmAccess(admin, 'secret') + expect(getRows()).toHaveLength(2) + }) + + it('confirms the access, and then deletes the passkey', async () => { + const { admin, request, getRows } = await mountWithPasskeys({ + isConfirmed: false + }) + await admin.wrapper + .find('.dito-dialog .dito-list .dito-button--delete') + .trigger('click') + await flushPromises() + await findButton(admin, 'Delete').trigger('click') + await flushPromises() + await confirmAccess(admin, 'secret') + expect( + request.mock.calls.filter( + ([{ method, url }]) => ( + method === 'delete' && url === '/users/passkeys/1' + ) + ) + ).toHaveLength(2) + expect(getRows()).toEqual(['No passkeys yet.']) + }) + + it('stops quietly when the confirmation is cancelled', async () => { + const { admin, request, getRows } = await mountWithPasskeys({ + isConfirmed: false + }) + const notify = vi.spyOn(admin.context, 'notify') + const cancel = async () => { + await flushPromises() + const dialog = getLastDialog(admin) + expect(dialog.attributes('aria-label')).toBe('Confirm Access') + await findButton(admin, 'Cancel').trigger('click') + await flushPromises() + } + await findButton(admin, 'Add Passkey').trigger('click') + await cancel() + // A button without its own error handling: + await findButton(admin, 'Change').trigger('click') + await flushPromises() + const dialog = getLastDialog(admin) + for (const [name, value] of Object.entries({ + currentPassword: 'secret', + password: 'changed', + confirmation: 'changed' + })) { + await enterValue(dialog.find(`input[name="${name}"]`), value) + } + await dialog.find('button[type="submit"]').trigger('click') + await cancel() + expect(notify).not.toHaveBeenCalled() + expect(startRegistration).not.toHaveBeenCalled() + expect(request).not.toHaveBeenCalledWith( + expect.objectContaining({ url: '/users/confirm-access' }) + ) + expect(getRows()).toHaveLength(1) + }) + }) + describe('password', () => { const changePassword = async (admin, values) => { await findButton(admin, 'Change').trigger('click') @@ -279,6 +411,39 @@ describe('DitoAccount', () => { return dialog } + it('changes the password without passkeys or TOTP', async () => { + const { admin, request } = await mountWithPasskeys({ + login: {}, + isConfirmed: false + }) + const notify = vi.spyOn(admin.context, 'notify') + const security = getLastDialog(admin) + expect(security.findAll('.dito-object')).toHaveLength(1) + expect(security.find('.dito-list').exists()).toBe(false) + await changePassword(admin, { + currentPassword: 'secret', + password: 'changed', + confirmation: 'changed' + }) + await confirmAccess(admin, 'secret') + expect( + request.mock.calls.filter(([{ url }]) => url === '/users/password') + ).toHaveLength(2) + expect(request).toHaveBeenLastCalledWith( + expect.objectContaining({ + method: 'post', + url: '/users/password', + data: { currentPassword: 'secret', password: 'changed' } + }) + ) + expect(notify).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'success', + text: 'Your password was changed.' + }) + ) + }) + it('shows a placeholder instead of the password', async () => { const { admin } = await mountWithPasskeys() const row = getLastDialog(admin).find('.dito-object') @@ -391,7 +556,6 @@ describe('DitoAccount', () => { enabled = true return { data: { backupCodes } } } else if (url === '/users/totp' && method === 'delete') { - checkCode(data.code) enabled = false return { data: { success: true } } } @@ -491,20 +655,22 @@ describe('DitoAccount', () => { expect(getState()).toContain('Not set up') }) - it('turns off TOTP with a code', async () => { + it('turns off TOTP after asking, without a code', async () => { const { admin, request, getState } = await mountWithTotp({ enabled: true }) expect(getState()).toContain('Enabled · 10 backup codes left') await findButton(admin, 'Turn Off').trigger('click') await flushPromises() - await submitCode(admin, '123456') + const dialog = getLastDialog(admin) + expect(dialog.text()).toContain( + 'Do you really want to turn off two-factor authentication?' + ) + expect(dialog.find('input[name="code"]').exists()).toBe(false) + await dialog.find('button[type="submit"]').trigger('click') + await flushPromises() expect(request).toHaveBeenCalledWith( - expect.objectContaining({ - method: 'delete', - url: '/users/totp', - data: { code: '123456' } - }) + expect.objectContaining({ method: 'delete', url: '/users/totp' }) ) expect(getState()).toContain('Not set up') }) diff --git a/packages/admin/src/components/DitoAccount.vue b/packages/admin/src/components/DitoAccount.vue index f43fd0869..c7dcb59b7 100644 --- a/packages/admin/src/components/DitoAccount.vue +++ b/packages/admin/src/components/DitoAccount.vue @@ -35,10 +35,8 @@ export default { items: computed(() => [ settingsRoute.value && { value: 'settings', label: 'Settings' }, - (props.passkeys || props.totp) && { - value: 'security', - label: 'Security' - }, + // Users change their password there, also without passkeys or TOTP: + { value: 'security', label: 'Security' }, { value: 'logout', label: 'Sign Out' } ].filter(Boolean) ), diff --git a/packages/admin/src/components/DitoRoot.vue b/packages/admin/src/components/DitoRoot.vue index 2d543bd18..0c22d73cc 100644 --- a/packages/admin/src/components/DitoRoot.vue +++ b/packages/admin/src/components/DitoRoot.vue @@ -292,6 +292,47 @@ export default { }, settings: { label: 'Set New Password' } }) + }, + + // Return the password to confirm the access with, `'passkey'` to + // confirm it with a passkey, or `null` if the user cancels: + requestAccessConfirmation() { + const hasPasskeys = ( + props.options.login?.passkeys && + !!window.PublicKeyCredential + ) + return showDialog({ + components: { + password: { + type: 'password', + label: 'Password', + info: 'Confirm your access to change your security settings.', + autocomplete: 'current-password', + autofocus: true, + required: true + } + }, + buttons: { + confirm: { + type: 'submit', + text: 'Confirm' + }, + + ...(hasPasskeys && { + passkey: { + type: 'button', + text: 'Use Passkey', + onClick: ({ closeDialog }) => closeDialog('passkey') + } + }), + + cancel: { + type: 'button', + text: 'Cancel' + } + }, + settings: { label: 'Confirm Access' } + }) } } ) diff --git a/packages/admin/src/types/DitoTypeButton.vue b/packages/admin/src/types/DitoTypeButton.vue index 320e126ce..d01f09342 100644 --- a/packages/admin/src/types/DitoTypeButton.vue +++ b/packages/admin/src/types/DitoTypeButton.vue @@ -105,10 +105,13 @@ const DitoTypeButton = { await services.submit?.() } } catch (error) { - const context = field.createContext({ error }) - const res = await emitEvent('error', { context }) - if (res === undefined && !context.wasNotified) { - services.adminContext.notify({ type: 'error', text: error }) + // Aborted requests, e.g. cancelled confirmations, end quietly: + if (error?.name !== 'AbortError') { + const context = field.createContext({ error }) + const res = await emitEvent('error', { context }) + if (res === undefined && !context.wasNotified) { + services.adminContext.notify({ type: 'error', text: error }) + } } } finally { isRunning.value = false diff --git a/packages/admin/src/utils/Session.test.js b/packages/admin/src/utils/Session.test.js index f6e9a08a1..0129a933c 100644 --- a/packages/admin/src/utils/Session.test.js +++ b/packages/admin/src/utils/Session.test.js @@ -21,6 +21,7 @@ function createSession({ loginCodes = [], passwordResets = [], newPasswords = [], + accessConfirmations = [], resolvedViews = [true], redirectAfterLogin = null } = {}) { @@ -33,7 +34,8 @@ function createSession({ passkeyLogin: { method: 'post', path: 'passkey-login' }, loginCode: { method: 'post', path: 'login-code' }, passwordReset: { method: 'post', path: 'password-reset' }, - passwordResetConfirm: { method: 'post', path: 'password-reset-confirm' } + passwordResetConfirm: { method: 'post', path: 'password-reset-confirm' }, + confirmAccess: { method: 'post', path: 'confirm-access' } }, resources: { any: resource => `/${resource.path}` }, getApiUrl: ({ url }) => url, @@ -59,6 +61,9 @@ function createSession({ requestLoginCode: vi.fn(async () => loginCodes.shift() ?? null), requestPasswordReset: vi.fn(async () => passwordResets.shift() ?? null), requestNewPassword: vi.fn(async () => newPasswords.shift() ?? null), + requestAccessConfirmation: vi.fn( + async () => accessConfirmations.shift() ?? null + ), notify: vi.fn() } const detach = session.attachUserInterface(userInterface) @@ -603,4 +608,87 @@ describe('Session', () => { consoleError.mockRestore() }) }) + + describe('confirmAccess()', () => { + const success = { success: true } + + it('confirms the access with the password', async () => { + const { session, api } = createSession({ + responses: { 'post /confirm-access': success }, + accessConfirmations: [{ password: 'secret' }] + }) + expect(await session.confirmAccess()).toBe(true) + expect(api.request).toHaveBeenLastCalledWith({ + method: 'post', + url: '/confirm-access', + data: { password: 'secret' } + }) + }) + + it('asks users with TOTP for the code after the password', async () => { + let attempt = 0 + const { session, api, userInterface } = createSession({ + responses: { + 'post /confirm-access': () => + ++attempt === 1 ? { success: false, codeRequired: true } : success + }, + accessConfirmations: [{ password: 'secret' }], + loginCodes: [{ code: '123456' }] + }) + expect(await session.confirmAccess()).toBe(true) + expect(userInterface.requestLoginCode).toHaveBeenCalledOnce() + expect(api.request).toHaveBeenLastCalledWith({ + method: 'post', + url: '/confirm-access', + data: { password: 'secret', code: '123456' } + }) + }) + + it('ends when the code is cancelled', async () => { + const { session, userInterface } = createSession({ + responses: { + 'post /confirm-access': { success: false, codeRequired: true } + }, + accessConfirmations: [{ password: 'secret' }, { password: 'secret' }] + }) + expect(await session.confirmAccess()).toBe(false) + expect(userInterface.requestLoginCode).toHaveBeenCalledOnce() + expect(userInterface.requestAccessConfirmation).toHaveBeenCalledOnce() + }) + + it('confirms the access with a passkey', async () => { + const { session, api } = createSession({ + responses: { + 'post /passkey-login-options': { challenge: 'challenge' }, + 'post /confirm-access': success + }, + accessConfirmations: ['passkey'] + }) + expect(await session.confirmAccess()).toBe(true) + expect(api.request).toHaveBeenLastCalledWith({ + method: 'post', + url: '/confirm-access', + data: { passkey: { id: 'passkey' } } + }) + }) + + it('asks again after failures, until the user cancels', async () => { + const { session, userInterface } = createSession({ + responses: { + 'post /confirm-access': Object.assign(new Error('Failed'), { + response: { + status: 401, + data: { message: 'Password is incorrect' } + } + }) + }, + accessConfirmations: [{ password: 'wrong' }] + }) + expect(await session.confirmAccess()).toBe(false) + expect(userInterface.requestAccessConfirmation).toHaveBeenCalledTimes(2) + expect(userInterface.notify).toHaveBeenCalledWith( + expect.objectContaining({ text: 'Password is incorrect' }) + ) + }) + }) }) diff --git a/packages/admin/src/utils/Session.ts b/packages/admin/src/utils/Session.ts index 92d760117..b1fdbddf5 100644 --- a/packages/admin/src/utils/Session.ts +++ b/packages/admin/src/utils/Session.ts @@ -14,6 +14,7 @@ interface UserInterface { requestLoginCode(): Promise<{ code: string } | null> requestPasswordReset(): Promise | null> requestNewPassword(): Promise<{ password: string } | null> + requestAccessConfirmation(): Promise<{ password: string } | 'passkey' | null> notify(options: NotifyOptions): void } @@ -50,13 +51,16 @@ export class Session { // `requestLoginData()` returns the credentials, `'passkey'` to log in with a // passkey, `'passwordReset'` to reset the password, or `null` when cancelled. // `requestLoginCode()` returns the TOTP or backup code of users with TOTP, - // `requestPasswordReset()` the username to reset the password of, and - // `requestNewPassword()` the new password, or `null` when cancelled. + // `requestPasswordReset()` the username to reset the password of, + // `requestNewPassword()` the new password, and `requestAccessConfirmation()` + // the password or `'passkey'` to confirm the access with, or `null` when + // cancelled. attachUserInterface({ requestLoginData, requestLoginCode, requestPasswordReset, requestNewPassword, + requestAccessConfirmation, notify }: UserInterface) { const userInterface = { @@ -64,6 +68,7 @@ export class Session { requestLoginCode, requestPasswordReset, requestNewPassword, + requestAccessConfirmation, notify } this.#userInterface = userInterface @@ -105,9 +110,10 @@ export class Session { const { users } = this.#api let response if (loginData === 'passkey') { - const options = await this.#request(users.passkeyLoginOptions) - const passkey = await startAuthentication({ optionsJSON: options.data }) - response = await this.#request(users.passkeyLogin, passkey) + response = await this.#request( + users.passkeyLogin, + await this.#getPasskey() + ) } else { response = await this.#request(users.login, loginData) } @@ -157,6 +163,43 @@ export class Session { } } + // Security changes need a recent login, or a confirmation of the access with + // the password and the code of users with TOTP, or with a passkey. Ask again + // after each failure, and resolve to whether the access was confirmed: + async confirmAccess(): Promise { + const data = await this.#userInterface?.requestAccessConfirmation() + if (data) { + try { + const { users } = this.#api + const body = + data === 'passkey' ? { passkey: await this.#getPasskey() } : data + let response = await this.#request(users.confirmAccess, body) + if (response.data.codeRequired) { + const codeData = await this.#userInterface?.requestLoginCode() + if (codeData) { + response = await this.#request(users.confirmAccess, { + ...body, + ...codeData + }) + } else { + // Cancelling the code ends the confirmation, like the password: + return false + } + } + if (response.data.success) { + return true + } + } catch (error) { + if (!isPasskeyPromptCancelled(error)) { + this.#notifyError(error) + } + } + return this.confirmAccess() + } else { + return false + } + } + async #requestPasswordReset() { const data = await this.#userInterface?.requestPasswordReset() if (data) { @@ -253,6 +296,12 @@ export class Session { } } + // Answer a challenge of the server with a passkey of the browser: + async #getPasskey() { + const options = await this.#request(this.#api.users.passkeyLoginOptions) + return startAuthentication({ optionsJSON: options.data }) + } + #request(resource: any, data: any = null) { const url = this.#api.getApiUrl({ url: this.#api.resources.any(resource) }) return this.#api.request({ method: resource.method, url, data }) diff --git a/packages/admin/src/utils/security.js b/packages/admin/src/utils/security.js index f41b9f705..b8289099e 100644 --- a/packages/admin/src/utils/security.js +++ b/packages/admin/src/utils/security.js @@ -1,6 +1,7 @@ import { startRegistration } from '@simplewebauthn/browser' import DitoQrCode from '../components/DitoQrCode.vue' import { isPasskeyPromptCancelled } from './Session.ts' +import { confirm } from './dialogs.js' // Dialogs are as wide as their content, up to their `width` setting, so // components as wide as the setting fill them: @@ -207,26 +208,20 @@ export function showSecurityDialog(adminContext, { passkeys, totp }) { turnOff: { text: 'Turn Off', if: ({ item }) => item.enabled, - async onClick(context) { - const { request, reload } = context - const result = await submitCode( - context, + // Without confirmed access, requests ask for the code first: + async onClick({ request, reload }) { + const isConfirmed = await confirm( + { adminContext }, { - components: { code: createCodeComponent() }, - buttons: { - cancel: {}, - turnOff: { type: 'submit', text: 'Turn Off' } - }, - settings: { label: 'Turn Off Two-Factor Authentication' } - }, - code => - request({ - resource: users.totp, - method: 'delete', - data: { code } - }) + message: ( + 'Do you really want to turn off two-factor ' + + 'authentication?' + ), + verb: 'turn off' + } ) - if (result) { + if (isConfirmed) { + await request({ resource: users.totp, method: 'delete' }) await reload() } } @@ -244,7 +239,7 @@ export function showSecurityDialog(adminContext, { passkeys, totp }) { }) } -// The code field of the login and the security dialogs: +// The code field of the login dialog, also when confirming the access: export function createCodeComponent() { return { type: 'text', diff --git a/packages/admin/src/utils/services.ts b/packages/admin/src/utils/services.ts index 11888039b..a55906095 100644 --- a/packages/admin/src/utils/services.ts +++ b/packages/admin/src/utils/services.ts @@ -99,7 +99,21 @@ export async function sendRequest( if (checkUser) { await session.ensureUser() } - const response = await api.request({ method, url, query, data, signal }) + const send = () => api.request({ method, url, query, data, signal }) + const response = await send().catch(async error => { + // Security changes answer `confirmationRequired` without a recent login, + // so we confirm the access and send them again. Cancelled confirmations + // end the request quietly, like aborted ones: + if (checkUser && error.response?.data?.confirmationRequired) { + if (await session.confirmAccess()) { + return send() + } else { + throw new DOMException('The confirmation was cancelled.', 'AbortError') + } + } else { + throw error + } + }) // Fetch the own user again when it changed, through its member or `self`: if (checkUser && method === 'patch') { const { users } = api diff --git a/packages/admin/types/index.d.ts b/packages/admin/types/index.d.ts index b0b0f6b87..5751ed653 100644 --- a/packages/admin/types/index.d.ts +++ b/packages/admin/types/index.d.ts @@ -334,6 +334,12 @@ export interface ApiConfig { */ totp?: { path?: string } totpConfirm?: { path?: string; method?: HttpMethod } + /** + * Posted to with the password, the code of users with TOTP or a passkey, + * when security changes answer `confirmationRequired`, with the default + * path `'confirm-access'`. + */ + confirmAccess?: { path?: string; method?: HttpMethod } } /** * Custom resource path handlers, keyed by resource type. Merged diff --git a/packages/server/src/controllers/UsersController.js b/packages/server/src/controllers/UsersController.js index 9026d1e07..880d6ce1f 100644 --- a/packages/server/src/controllers/UsersController.js +++ b/packages/server/src/controllers/UsersController.js @@ -6,7 +6,7 @@ import { randomBytes } from 'node:crypto' import aaguids from 'aaguids' -import { isString } from '@ditojs/utils' +import { asArray, isString } from '@ditojs/utils' import { ModelController } from './ModelController.js' import { getUserIdentifier, @@ -26,7 +26,8 @@ import { AuthorizationError, ControllerError, NotFoundError, - ResponseError + ResponseError, + ValidationError } from '../errors/index.js' export class UsersController extends ModelController { @@ -42,17 +43,19 @@ export class UsersController extends ModelController { 'post passkey-login': true, 'post password-reset': true, 'post password-reset-confirm': true, - // Users manage their own password and passkeys: - 'post password': isAuthenticated, + // Users manage their own password and passkeys, and need confirmed + // access to change them: + 'post confirm-access': isAuthenticated, + 'post password': hasConfirmedAccess, 'get passkeys': isAuthenticated, - 'post passkey-options': isAuthenticated, - 'post passkeys': isAuthenticated, - 'delete passkeys/:id': isAuthenticated, + 'post passkey-options': hasConfirmedAccess, + 'post passkeys': hasConfirmedAccess, + 'delete passkeys/:id': hasConfirmedAccess, // ...and their TOTP: 'get totp': isAuthenticated, - 'post totp': isAuthenticated, - 'post totp-confirm': isAuthenticated, - 'delete totp': isAuthenticated + 'post totp': hasConfirmedAccess, + 'post totp-confirm': hasConfirmedAccess, + 'delete totp': hasConfirmedAccess }, 'post login'(ctx) { @@ -110,6 +113,36 @@ export class UsersController extends ModelController { } }, + // Confirm the access of the logged-in user with their password, and the + // code of users with TOTP, or with a passkey: + 'post confirm-access': { + parameters: { + password: { type: 'string' }, + code: { type: 'string' }, + passkey: { type: 'object' } + }, + + async handler(ctx, { password, code, passkey }) { + const { user } = ctx.state + if (this.passkeys && passkey) { + if (!(await verifyPasskey(this, ctx, passkey))?.$is(user)) { + throw new AuthenticationError('Passkey is incorrect') + } + } else if (!(password && (await user.$verifyPassword(password)))) { + throw new AuthenticationError('Password is incorrect') + } else if (this.totp && (await hasTotp(this.modelClass, ctx))) { + // Like password logins, ask users with TOTP for a code: + if (!code) { + return { success: false, codeRequired: true } + } else if (!(await this.verifyCode(ctx, user, code))) { + throw new AuthenticationError('Code is incorrect') + } + } + storeConfirmedAccess(this, ctx, user) + return { success: true } + } + }, + 'post password': { parameters: { currentPassword: { type: 'string', required: true }, @@ -262,58 +295,16 @@ export class UsersController extends ModelController { rpID: this.passkeys.id, userVerification: 'required' }) - storeChallenge(ctx, options) + storeChallenge(ctx, 'authentication', options) return options }, 'post passkey-login'(ctx) { return this.handleLogin(ctx, async () => { - const { verifyAuthenticationResponse } = this.webAuthn - const response = ctx.request.body - const challenge = consumeChallenge(ctx) - const credential = await this.modelClass - .credentialQuery(ctx.transaction) - .findOne({ type: 'passkey', key: response.id ?? null }) - const user = credential - ? await this.modelClass - .sessionQuery(ctx.transaction) - .findById(credential.userId) - : null + const user = await verifyPasskey(this, ctx, ctx.request.body) if (user) { - const { publicKey, counter, transports } = credential.data - const { verified, authenticationInfo } = - await verifyAuthenticationResponse({ - response, - expectedChallenge: challenge, - expectedOrigin: this.passkeys.origins, - expectedRPID: this.passkeys.id, - credential: { - id: credential.key, - publicKey: Buffer.from(publicKey, 'base64url'), - counter, - transports - } - }).catch(err => { - // Don't tell clients why, but keep the reason for the logs: - throw new AuthenticationError({ - message: 'Passkey is incorrect', - cause: err - }) - }) - if (verified) { - await credential.$patch( - { - data: { - ...credential.data, - counter: authenticationInfo.newCounter - }, - lastUsedAt: new Date() - }, - ctx.transaction - ) - await ctx.login(user) - return user - } + await ctx.login(user) + return user } throw new AuthenticationError('Passkey is incorrect') }) @@ -347,7 +338,7 @@ export class UsersController extends ModelController { userVerification: 'required' } }) - storeChallenge(ctx, options) + storeChallenge(ctx, 'registration', options) return options }, @@ -355,7 +346,7 @@ export class UsersController extends ModelController { const { verifyRegistrationResponse } = this.webAuthn const { verified, registrationInfo } = await verifyRegistrationResponse({ response: ctx.request.body, - expectedChallenge: consumeChallenge(ctx), + expectedChallenge: consumeChallenge(ctx, 'registration'), expectedOrigin: this.passkeys.origins, expectedRPID: this.passkeys.id }).catch(err => { @@ -492,17 +483,12 @@ export class UsersController extends ModelController { }, async 'delete totp'(ctx) { - const { code } = ctx.request.body - if (await this.verifyCode(ctx, ctx.state.user, code)) { - await getTotpQuery( - this.modelClass, - ctx.state.user, - ctx.transaction - ).delete() - return { success: true } - } else { - throw new AuthenticationError('Code is incorrect') - } + await getTotpQuery( + this.modelClass, + ctx.state.user, + ctx.transaction + ).delete() + return { success: true } } } @@ -515,6 +501,7 @@ export class UsersController extends ModelController { const { session, auth } = this.app.config.app this.passkeys = auth?.passkeys ?? null this.totp = auth?.totp ?? null + this.reconfirmAccessAfter = getDuration(auth?.reconfirmAccessAfter ?? '10m') this.passwordReset = auth?.passwordReset ? { ...auth.passwordReset, @@ -645,16 +632,32 @@ export class UsersController extends ModelController { } } + // @override + async executeAndFetch(action, ctx, modify, body = ctx.request.body) { + await validateUserChanges( + this, + ctx, + action, + asArray(body).map(user => [this.getModelId(user), user]) + ) + return super.executeAndFetch(action, ctx, modify, body) + } + + // @override + async relateAndFetch(ctx, modify) { + await validateUserChanges( + this, + ctx, + 'relate', + asArray(ctx.request.body).map(user => [this.getModelId(user), user]) + ) + return super.relateAndFetch(ctx, modify) + } + // @override async executeAndFetchById(action, ctx, modify, body = ctx.request.body) { - const result = await super.executeAndFetchById(action, ctx, modify, body) - const { passwordProperty } = this.modelClass.definition.options - if (body[passwordProperty] !== undefined) { - // Users change their password through patches too, e.g. in - // `PATCH /users/self`: - await handlePasswordChange(this, ctx, result) - } - return result + await validateUserChanges(this, ctx, action, [[ctx.memberId, body]]) + return super.executeAndFetchById(action, ctx, modify, body) } isAuthenticated(ctx) { @@ -666,6 +669,18 @@ export class UsersController extends ModelController { ) } + // Security changes need a login or a 'post confirm-access' of the user within + // `app.auth.reconfirmAccessAfter`. Without one, we answer 403 with + // `confirmationRequired` instead of denying the access: + hasConfirmedAccess(ctx) { + if (this.isAuthenticated(ctx)) { + checkConfirmedAccess(ctx) + return true + } else { + return false + } + } + async handleLogin(ctx, login) { let user let error @@ -674,6 +689,7 @@ export class UsersController extends ModelController { user = await login() if (user) { await user.$patch({ lastLogin: new Date() }, ctx.transaction) + storeConfirmedAccess(this, ctx, user) } else { // `UserMixin.login()` asks users with TOTP for a code first: codeRequired = true @@ -799,6 +815,10 @@ function isAuthenticated(ctx) { return this.isAuthenticated(ctx) } +function hasConfirmedAccess(ctx) { + return this.hasConfirmedAccess(ctx) +} + function getPasskeyQuery(modelClass, ctx) { return modelClass.credentialQuery(ctx.transaction).where({ type: 'passkey', @@ -806,17 +826,20 @@ function getPasskeyQuery(modelClass, ctx) { }) } -function storeChallenge(ctx, { challenge }) { - ctx.session.passkeyChallenge = { +// Registrations and authentications keep their challenges apart, so that +// confirming the access with a passkey doesn't end a pending registration: +function storeChallenge(ctx, type, { challenge }) { + ctx.session[`${type}Challenge`] = { challenge, expires: Date.now() + authTimeout } } // Each challenge can only be answered once, before it expires: -function consumeChallenge(ctx) { - const { challenge, expires } = ctx.session.passkeyChallenge ?? {} - delete ctx.session.passkeyChallenge +function consumeChallenge(ctx, type) { + const key = `${type}Challenge` + const { challenge, expires } = ctx.session[key] ?? {} + delete ctx.session[key] if (challenge && expires > Date.now()) { return challenge } else { @@ -827,6 +850,56 @@ function consumeChallenge(ctx) { } } +// Verify the response of `startAuthentication()` against the challenge in the +// session, and resolve to the user of the passkey, or `null`: +async function verifyPasskey(controller, ctx, response) { + const { modelClass, passkeys, webAuthn } = controller + const challenge = consumeChallenge(ctx, 'authentication') + const credential = await modelClass + .credentialQuery(ctx.transaction) + .findOne({ type: 'passkey', key: response.id ?? null }) + const user = credential + ? await modelClass.sessionQuery(ctx.transaction).findById(credential.userId) + : null + if (user) { + const { publicKey, counter, transports } = credential.data + const { verified, authenticationInfo } = await webAuthn + .verifyAuthenticationResponse({ + response, + expectedChallenge: challenge, + expectedOrigin: passkeys.origins, + expectedRPID: passkeys.id, + credential: { + id: credential.key, + publicKey: Buffer.from(publicKey, 'base64url'), + counter, + transports + } + }) + .catch(err => { + // Don't tell clients why, but keep the reason for the logs: + throw new AuthenticationError({ + message: 'Passkey is incorrect', + cause: err + }) + }) + if (verified) { + await credential.$patch( + { + data: { + ...credential.data, + counter: authenticationInfo.newCounter + }, + lastUsedAt: new Date() + }, + ctx.transaction + ) + return user + } + } + return null +} + // The attempt that uses up the limit answers it too, so that clients don't ask // for another code: function createTooManyAttemptsError() { @@ -913,6 +986,62 @@ async function handlePasswordChange(controller, ctx, user) { updatePasswordFingerprint(ctx, user) } +// Passwords are set when users are created, but only change through +// 'post password', which needs confirmed access and the current password, and +// their hashes are never set directly. Usernames only change with confirmed +// access, as password resets go to them. `users` are `[id, user]` entries: +async function validateUserChanges(controller, ctx, action, users) { + const { modelClass } = controller + const { usernameProperty, passwordProperty } = modelClass.definition.options + for (const [id, user] of users) { + // Relating users with ids updates them: + const isInsert = action === 'insert' || action === 'relate' && id == null + const property = + user?.hash !== undefined + ? 'hash' + : !isInsert && user?.[passwordProperty] !== undefined + ? passwordProperty + : null + if (property) { + throw new ValidationError({ + type: 'BodyValidation', + message: `Unable to change '${property}' outside of 'post password'` + }) + } + const username = user?.[usernameProperty] + if (!isInsert && username !== undefined) { + const stored = await modelClass.query(ctx.transaction).findById(id) + if (stored?.[usernameProperty] !== username) { + checkConfirmedAccess(ctx) + } + } + } +} + +// Answer 403 with `confirmationRequired` to users without a login or a +// 'post confirm-access' within `app.auth.reconfirmAccessAfter`: +function checkConfirmedAccess(ctx) { + const { user, expires } = ctx.session?.confirmedAccess ?? {} + if ( + !ctx.state.user || + user !== getUserIdentifier(ctx.state.user) || + !(expires > Date.now()) + ) { + throw new AuthorizationError({ + message: 'Confirmation required', + status: 403, + confirmationRequired: true + }) + } +} + +function storeConfirmedAccess(controller, ctx, user) { + ctx.session.confirmedAccess = { + user: getUserIdentifier(user), + expires: Date.now() + controller.reconfirmAccessAfter + } +} + function getPasswordResetQuery(modelClass, user, trx) { return modelClass.credentialQuery(trx).where({ type: 'password-reset', diff --git a/packages/server/types/index.d.ts b/packages/server/types/index.d.ts index d98778398..ba6ab24c3 100644 --- a/packages/server/types/index.d.ts +++ b/packages/server/types/index.d.ts @@ -252,6 +252,14 @@ export interface ApplicationConfig { */ expires?: number | string } + /** + * How long logins and `'post confirm-access'` confirm the access of + * sessions for the security changes of `UsersController`, in + * milliseconds or as a duration string. + * + * @defaultValue `'10m'` + */ + reconfirmAccessAfter?: number | string } /** * Set signed cookie keys. @@ -2876,6 +2884,12 @@ export class UsersController< * and the user is an instance of the controller's model. */ isAuthenticated(ctx: KoaContext): boolean + /** + * Like `isAuthenticated()`, but throws a 403 error with + * `confirmationRequired` for users without confirmed access, see + * `app.auth.reconfirmAccessAfter`. For the `authorize` of security changes. + */ + hasConfirmedAccess(ctx: KoaContext): boolean /** * Sends the user the email with the `url` that resets their password, with * `app.auth.passwordReset`. Apps override it. diff --git a/tests/e2e/account/admin/index.spec.ts b/tests/e2e/account/admin/index.spec.ts index 304a882b1..593e7d063 100644 --- a/tests/e2e/account/admin/index.spec.ts +++ b/tests/e2e/account/admin/index.spec.ts @@ -31,7 +31,7 @@ test.describe('account settings', () => { await expect(dialog.getByLabel('Username')).toBeFocused() }) - test('edit the logged-in user', async ({ page, browser, url }) => { + test('edit the logged-in user', async ({ page, url }) => { await login(page, url, ['ada', 'secret']) await expect(getAccountButton(page)).toHaveText('ada') @@ -43,25 +43,17 @@ test.describe('account settings', () => { await expect(page).toHaveURL(`${url}/admin/account/settings`) // The form shows the user's data, without the password or its hash: - const { hash } = (await User.query().findOne('username', 'ada'))! expect(self).toMatchObject({ username: 'ada', email: 'ada@example.com' }) expect(self).not.toHaveProperty('hash') expect(self).not.toHaveProperty('password') const username = page.getByLabel('Username', { exact: true }) const email = page.getByLabel('Email', { exact: true }) - const password = page.getByLabel('New Password', { exact: true }) await expect(username).toHaveValue('ada') await expect(email).toHaveValue('ada@example.com') - // Password fields without a value show a mask until they are focused: - await expect(password).toHaveValue('****************') - await password.focus() - await expect(password).toHaveValue('') - expect(await password.inputValue()).not.toBe(hash) // Save the changes: await username.fill('lovelace') await email.fill('lovelace@example.com') - await password.fill('changed') const selfPatched = page.waitForResponse(isSelfResponse('PATCH')) await page.getByRole('button', { name: 'Save' }).click() expect((await selfPatched).status()).toBe(200) @@ -69,19 +61,7 @@ test.describe('account settings', () => { // The record changed, and the account menu shows it right away: const user = (await User.query().findOne('username', 'lovelace'))! expect(user.email).toBe('lovelace@example.com') - expect(user.hash).not.toBe(hash) - expect(await user.$verifyPassword('changed')).toBe(true) await expect(getAccountButton(page)).toHaveText('lovelace') - - // The new password works for a fresh login: - const context = await browser.newContext() - try { - const freshPage = await context.newPage() - await login(freshPage, url, ['lovelace', 'changed']) - await expect(getAccountButton(freshPage)).toHaveText('lovelace') - } finally { - await context.close() - } }) test('cancel the settings for the root', async ({ page, url }) => { diff --git a/tests/e2e/account/views.ts b/tests/e2e/account/views.ts index 692b81228..f100ecbbe 100644 --- a/tests/e2e/account/views.ts +++ b/tests/e2e/account/views.ts @@ -10,8 +10,7 @@ export const account = { label: 'Account', components: { username: { type: 'text', label: 'Username' }, - email: { type: 'email', label: 'Email' }, - password: { type: 'password', label: 'New Password' } + email: { type: 'email', label: 'Email' } } } } as const diff --git a/tests/e2e/passkeys/admin/__screenshots__/totp-turn-off.png b/tests/e2e/passkeys/admin/__screenshots__/totp-turn-off.png index 9d3689bae9105a2131fcd5d1d53c209f8a04c763..815f2db6246cc84cf3d0f3407e7536772fd3fccd 100644 GIT binary patch literal 59116 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 literal 58787 zcmeEubx>99+wKBI5Ku$}1SF&zB&9Md#sMCx;cjfm0kzJ^dyr&BSiKYr;)m2orrW8i~V$JeeFZrr-&Zm#7r z^qnV8(_9zJy;Gg9&0T#iW|MZ0x2AAL;w~>Q2^bWyzzD# zZz)FDN{$aHP&PgBbTSCSQoh4~^0=<0hRj}|z;=7`x>0~5i!=R>K}v!O^}39;_4?`F z;_%QAH4V+;;$qYzflPhR-QPcgaER*&QPYW#bWb?gu0IG5ut)69R-vOomJ8^2OPYm} zkE)Z}DkI(sb8z&Ce5~BArz`O7{q#;%%4#g0*Cr~~l!7U)zj^9z{qg(q)_s19!oDZ5 zFKX@V)SQFMixF0Out3IH-kn|&8z;&8UU9@`e;Nd>-Hcxt)aHs28+ti-c$_tO9Kep& zdEflvtWDSVT>fIwg>Bz-RrPmyEF1i{-_ZpWG~CBeNWPyIJ?l0-no`e^x;O83A&Y%d zq~Fj#=;|1}ZTp(~?#+RGSEsB@)r(_$6P%IPlZP9F*3T4O8SfW{HiVDja6K2<7{~~~ zE#l(hx_|$ES6A1PziSa|!)`Oq-@OpeB6{Peo?N?>g2%U{9D>*FAV!)jc zFmX9!I)38E&F@m{*k#33W<7q}?P@W0C#R|ORNOgXxGhp7%MB(Q-qUo~;;PPZKKjnY z}jw5FFvr|8s1@Dc)K2p=b^%hu9KeTzP8w>_q4lnx; zM?w}Sgf#lEP5;ydv{o(16*oGX!qpsDZ8j#o`y42r==aqvL_cA0u}Wf$+&VX#-&E2t zeGur_h7b8ojJn@HiB9hACMxV?C&ss}b5kttYmq(Rs(RC1dAEtBXR=~@B|I~i-J}@d z+MIqE+qFq9REC|U<07&()M?`RbA$KMtmAh&O+B7m_1Sp3gXN+L;#nyT%9++Im~#>u zVk&h1M$-771|g;G=hxds&0AA=iBI<`ikZN=XipH@o*!A6&1Gh0{)82W?Ko{u%q+^u zNqJ2*E*4)+DXh3f#Vfsj1xtN$9LqOY%<;!wVa?9GmEor1qvMr={?&WML{aj+=L@yLl)g?vAw6-;?FV6OrSw^#y~3>9=b#x6z@2*uVw$K;+qkKsMKJ89Nf>W$1G?iZhq3 z{tHuwvw5&y@V-JzPuZp_{7m$^bq2| zY;fILnBboELbj50mrbu2M%fL2^JMvAxHau%Li_6)COZd)g+n({t)< zTP*ycYIA@~2X)@Cv|4|-l&AVG7oCxvkc)gSh0TY06XuCb(Y|NMNt@>NnhTY(;kw(k zz>#1l;@Yf%cRY zeKWyDqg@BbNW!!+W@Z8Hco>4kNJg=3dwTd*c2qr*axoNd=X`6$kZ-4_yW6Sgf&G>P zU01npZx0I-e$%$7aG`qAA8*z1tto~D-L~$hZ2V&IfpeIYhxESL`qzmH;T>^<4~%&I z-7JK*QnQW8(b#8Sm0#GYsLUJ8#WyG47^{+9dXJ>0V`iSziHPh5(-lu|HW^YR728XG zr~hhHRAb?^f1a9}{xN;S(k~U>s(eNP_gyWIL|fZ~z+wx1hgmC;VS>>~YMU$&HcS~< zCDhTARpDid8>yP4%|gfOOIL#SZN7NPE-$Y6Iy>3w?~;zY43yZGfAcMqa7vKiZMvUX z#EW!%P_;SBe^l=+Jw4@wm)%u45#!Y4y{tmmH+tk@Aqj?+Zj58^&_&3vH?+~gPuWlc zvl;soXk0zZKUh0-kmV|$P+oj}(6Jg}*l<2uM~?YCfn^6#83xA0#*0^yDU{50uD0>} zyMB|lkfzIV?!Cv?49v1lkq<(PnL5c|Ew+>&FoQ6oZO2mioSUUwSR-+Q)|SdmvVC9mW};&19X+I+Y@j!-w0_vZWeOh`bhd1f)wOS&@|)|O8IB` zGMf`gDrG-eeBLiii@#3Rf2?D@9vdGY-x14n74F1=ZCL$gtB$w+TwztvsF^TRI`ZZE zlk~W0%cJ#~AbeYTJ&+qUYHhABmi^anvNRymJADzk-#T*Hz?yzGwVATrJ8zZt&cWR+BaS-C#@t+%Z8!l#>gXR>l*B0+C(SzPK>a20Me zCYWu{@8_MRj-Nh{(X*Mi{~&rsA1P13&`eh}m6Qki)QJrAZ>8xV%UAxXKSiiTv*Y6A;n?MTJxQV_r6SMr zH5`p*5)~#|sk1GvMb{i;k0+JH^h%ftgk8R;OWc@@wXoIUT#OmLkaeDfcTDUeiRZQ$ zZJj2iG!49wQWi&V;t^Jz(P?;U<4W7pO~JfPlV>(A#`BH6LQX*?^ji26VjP!_&oqpm zPf?`AFjo~`3Up7B)V@nu29RjH$@iK<1Xf`?#mB`}7F>XK6DofBM-KiKxCy(B!q(nP{TVQG31i?OK!`*3NMus~Cc%^0D;w zTI4U|YLV+PT_uIJlEtuVpIY@~*a1TGV7TdpNXLlYlGcm0VWIUU!#Kfb}t9d*BiAMS+Dh?s>MYBu_ znL)OTC{eeUu)upBQc~H|GySsfRko{{9ah zt7THjU#l2`f%pFYK|*Qa?%%5sjQ?9G?>_^LIr2$-3hBj5D=T2OxBrgd;S0R~OE~jC z8^Qk^wyyJj@6x~C29TvmpU$ooBT4jfrAc&3h00oCtLZOjh9qhAZswcrWL6s*ezNr) zV-?MFNos6qOX053ulPG7NRn+A>Sk_isoAij7-i3Fpgno(y8ParsOd^)xpiEL^y7Vy z7Eu5El(E^rJBEZPT8;B&>rch+?KD=uRhjG4JW{wv=jp3=+B+GpKN0lA*7Gi@)9mNQ zqQX**AI0inO%oqWPn12prr5nP0Tiw<@h^%PDkR+D_*Ftv|Fp4tU@xYRY(VzT57{zJ z-x~Lps?CMZRX5u8U_X>~G2#9QG4|B!tt#&`}-?u z$@FXU+6M_9shjmx_15Y$Oz(f6;m0LRTH%-zkWp?Iw3NnX+Q=D|h zW3lk(+{BY8$Cn?(hnU)gfio*yWK)1OXg$!;NM>%=EiBCAGj~3NFTzeHkgo6l=Az=s zGyX<|v97Dkc(DlkX?O2&!Xr+v)izr>ZYSAW-+mnzApPdOi$=Ro%s2aN1rCQK$dN4B zxa7mW2gSDk3d=jTy8HH1$j+BH zX)L1DqI3fJJRI=nA*<8j>S3DpvvF+PTm@exjuF;GgcfjvS87@mglYGS_(;g-_?YOQ z{f+Q+G%N)_b%W1NH6_|fOSz$l0?~^%&khG{iuJaG9Sb zS9(sprxeqD%79F6k|!bj<&DDjS8)u!6zwbZozaJ8) zp^q6}cX>|MR^2-h|7pX7D8Fl{p(W_}h|WWbnwgGWP?XN#f%%I1FF72i1KF(Tm=+wT z$k-g*M;13s=i@BM+hm^65$JxAs(;UomJEvuA zB=_>|Sn;%Z-=5cO-1fy@Qf2*J;-zh!i{AIsUmNvxq!>;f4&?BdtPR;-;9%d~Id#}u zPmXQ$o_ZlI9Te8wBH)4K;+(2lXupAv9~TrvXt$^KynrHQvAoH!&e*=#Wr~ob_CMlB z@@>F6_6ggcewwf?rNqijZlA8N^W;T0&*>{4w)U)u_A(VdP$7S>=jD<1K;`pVGP@&q##IH6IHe)s6)~Ha#(B1p;BUq>M5$DV)!x- zf*vm|R!CN1K)dfi8^uUk0|UyHHc)!@6-bwapvA(YQc0|dABYA?5(;~ys)kfR=~mR= z(f#OYX$SA0J%GM*G%m|3c!&d$QUjI2(&*BFIgnBDmGw7mNL+Pkk@j_1 z%(SB5k2HsHTJ4`&(7-)V1jGYWx(DO#*X2MEXV-UI%m4wpGljg6QXnb<4Ci<2yIZna zK*@&zY{*EEB1YG$U*la${-tXNbsM_hdw;O5J$=%}57d^_+A|0<9zs0i8By;f3C+*$ z<}c}Ru|k{zU?iOIk1sT3?^GSV^TLLHFP(f<&ieRLtcMH6wU2KyNQeW;N1}d+(RolE z%c)?%RGroU8hCm$?odl|GK=q;DjbV zgT+(v#NC~5AfN6xTLqb?Oqt|8lTXHIeiL9l{b~cL5&1ICW}DBNIDhVJkkvl@jgAhj zhtS^xYU;2tY|K> zl{(0sx;sGt@jQAgU}-}@X@8ZNj5n#!K!}|ZaimeFe#c8MW4tDmsTBGyj0&bdqf`iN z>bkOr94=M)tA^|!U!g(CUda!VpL~>-{rVxN!DVoWGR~5xlk1_$ljM6)PdFIyy7ry- zcbKXDE;}skkTezf35B74xX}6wTMHh8sFn3F>$dL~*ya9;WqKy8VWo-NNK-9Hxe0YU zQH&%5jFG*rh3edR$pW3ORMbgWK78LD)Sx|A!LiQYv!7sk7FB9JL>Pt(ZbS?sa3m@5 ztL6`&5;_$m25U5KT8vo7%R;^ToJ~m= z%`3h@pv0w^Jz6A}a(Y#My=CjvdQ!mBN1Aw7d$v)3kJ999>z}=*mjHY1(shvK%P45D zc3g*@+a2d)sZQ>7Hk+@rDmpqW1D5N3hIrerd41pS=~8g1DWVrGtP0mmJLF~ICv99q zgmM>-lak<|rb{YlvMTC8Mz2s)llCVQuoOmM^VIEs(e>C0rPG^qu~&FF zpG7+N`i7stnV^c5^oI7O`oYtI`jppt1k6XYjNuI0?@bD+xLNzzH4Qw?(ID&F|Ln4I zwx->%K1oz@__BrpyFlQ!-J1Q1Cl#%s#|`lS6OpwVGr>f&6;h5AH$-pVDyd4XycP0` zDanUY=NxxDGjJSoxluzRMo&KPa)V`0?d1!Ga6QmJ>nXww)JS!i+0kTlCP_h(Lq?*L z_u5zPxnz8KGgDjS9ru>aLFV%%o%Rb8)~N?Ov-EBIWBlEv9`~+FURFZscTtJ5s9@`` ztK+l8++du69daz%lPyK)CB7n>_@epJQ+uV4tg{xjq>(M*i{FUIs)v}6; zcH_mi^78WTPlI7VTGmuiQPIc2#}BqMVs}2!5T@C9$3c)} z2*?E1avj4gVf2VshbuM-bX!x^1fbO;vrn7rhONO<{#sklp9Phfg+OM`rQubAKiXQI zJL-Q-PyYgf2538D(hmZ2+2E-!<SC;!HD`9*xSo3DrwCDO&sLhQAiN}}< z-DXZ4e$Bfw!JUixSQ_eM8mrzL8xptK5&P$k)-XCCx+=b>)BEk2nq>=afPr>J&T zwCyCYAQwLhE$AMst;u^dZ|zLK^wVKiVOO1GsXYd$ zvN?AWACbhgsieshfwg6xF3Sm*g$@_p7_2D7=y zQ_<3kQKi^6Z(b8_5PSm`{^&5;02DAVs|12fx%YI3T&x*F8J^htlmEfX_i=-|~mc zZu-e#m&p=U3kiTl$=!F98v25fd>QrQ?*fm%opYGHrDui2dr`9{ACQZkUiLWB+7fgh zI2bNbgtZ2nL*fJ$dx^%innUvM=^C;XQ!$!_8U@k)bcop@=%}lGdMoq1K;KZ+a8wu} z`1bdAvjqKRuf+IxK5%UVM^t%V95;LU{5e~h0)CYB0(j%uauRTg%&Mn}7p=u*x(3Dj zPW9D>9w%F=2pMaS3-DKos9)G>^FPi)_d7>*N_ZF8?Dq{7*bel2D=*>_egweQ;^=-+ zXmJ&j3c0>9GehR97Z7n6Twd%nHJ!|Q8XF&uVn95c`)awrl)~6a;$eKF=30reX9`W@~Cr_OiM2BoLLBUOXg@cuOg~di-h3#VEaXEAr)RosIf8O^N>cPx0}t zGcMyZ?u!xES3ADCRd1MX6kc7Nbnk1Io6mS1^m0wrUH`c_*__DIFeowlu~j|Cj$>bo zj6a$4HEc5|dpn$?fG52BJ@7U`j{KMXAvq4@%8Jr-~%8QQv(h8!75uQa7TT z#47{WGf-E79d9xb1N%-qhvAeG5zp+2NNLCPh{B_7RT5-uq<6&BMck>sPdr+xE18^+@iN zUH#Val)WYk=zDj)VPRm9zNM950Lx>YsJ2h@x;z>;JUBQ&HlMAeU4!cax*M2x!^6X4 z(c-;e{URqFht|OKML;jF1-87#VR6KPD285v6zrGY>7R=ov)^>jig$N+&3h9?j(?c| zY1{XW$?c%I&XVMotAq7c^L?vis(B7nd8>_Rkm~WZibt zb15#U{Ar#QfbfWV_(XE~Iaga92yQbSTGf0^N-yPQSx3;)Hm%&>-|tHingdg;ciV{{ z9Q|UE>e0Ef+%_}ogGZ=>(E&zGQ11#=LLuA9Fk0&aCeEX>T-9UmWq*(=G*U#@0FnTqG!(z3F$ z;u;ckSO^R+HpQkV)~U2cPSdSC*mq>nW)l-Le6JULm;62ioo4)M$7gP~m2`VtpYGz8JBZ8D?t9>wSK>({%l3RMRky4jBAOR~l1% z&e!%;_}H^b&kj~VqtL6QCj4hL7DqL%YpVT>Qa=02Yg_L$4y`NR`v7hXfIX0Gd27$g zv54tixyI*K-}hU>;_CN>;abgdq7B$>3eU|_RV9(5+LhG$#Ymy0Ecx`2q#IpY`4tt; zOC6uebkx*FM)Fk6>JFoPFKNWYnk@QLK>)%_FtqlcP*Z36UjJDiMs_E3OS=fC3#gBof7|mNEOiwXJZR~q14%S3KQ^u~*_yNMnjr1s8F-)%4Ec}_Khdfd_%K#^vY^Tvw-G(d z1_}<>vNBVNyZoW|z(GZ&Q+ABA&T*PA`yiXXhXX`lqLQNT;Z?cFb=dDxRX}Bk0V!jG z&r!Yn(}s7xgPZL>;@0!jX3*<~p8hZ>ma;A#W|p$R_(00r>S5p#c{QBWJL3>NN~AO5 z0ZsQc`1Hel#P9}GNFjez@vHoKpjzC0xMk3e2ebWdc*1f-z;_}BZrhEoW3~NqHGS^3 z+%;=Hxs;`YRaf^0IwXlw4c6aTyg~B%a1C8``K(A>=(wD_HO_#dXceiNaq(fuh=CPT z%Kak$gQ6Ow%z>JekNa9W5-X!(xcXwthb~`CW~xmH3#SEOWQAm!+*bg->Aa~Rh&OqV zk#HEgtqv(0qQ0ewk-&Dg?E|X0*TK|n7G*E(n~)2Nc3TI$Hmz(&)ak=Ct=jRds}8t6 zX29ySF9IvQaske?iX6_65=yGKVB4}9n_`p<1OIKgKTA7xy$wln=s}Qm+b=dO{b;6? zy?{ui&cwZdRBqdvS+9XtgqgKdpxih)IYp5RX=rHBLr|{$r%#_geE7h`#DtHJpZQ|* z+c@Wjh$rESvUjWZtjxv-EtO4Ot&68D$9KrHC%Yt7mqkejb-g))AU{78VTu@e<;Gxf z!V=ak=Z@>K`}vBw=B^eO0#wG(urN6#rKr$QkSa#1hlXCARfY%MmXpAO)2L-0CcKLh;+#cmTZNq`NHs$9{PwRa#sI;2_ zI-Aj56~1QIXzWfB*>42#nfz&SG>(pI!BTfIprarG5IaVH>I6JUsB*8m+pjPy3q-cX zR%`M4G;Bt$WTOoo80hH%0+)Z0nz|$-d$aGgL)vY*n{Vx0QX{=*!jx6lCvp}J)UC5G z_#hrTz`5?he8fJJUN7Zy`*8l8yNZvCd*XNxGLqasRKtnkwpPlrBhS3Tnb)R(j2@u~ zA5%(yMW_XN)CHb~(wrT66IH&wC}VQ9?vV2mAR=pJDcC6~C2ypTKLtb*if zim}1R{`pG)>HiU3LS`{%sM_$b$NPJUs97NBP+M;YW3wP6MXki{ z6%cf-7rKy$<*NB!Y9#ZnflyM8n|we_oDdru8yBapp@B_c3F?BWsi~Tp+M73pPX5O_ zrW;wCH3#Xix{5R&!i#_lnK37SSKALkYg1_H2LFI}CSIMQJaY%{9C9&6o~v@66S|v_ z_~uN#W(p7U^o!-^#>P1hT~t6WQc-z)vIQ*NT(_f*HDIv1T!KW;yZH>tt?|~k;V0BQ zJ~@2KK?FQdATC&#n;bPu9VRRs1V$t_6Pau8kcB7tgliKw;NNEbt{XUx9cY`=F(gf6w4ofGc&m<-m%*?g-fn4Z#_t8^j zHMQ7}9|x3&SmLI2Yx{$Og1Ed1GFAk;t38eZrvLklz*lc(Hz{|q?DbTUvrH^V9;Iro zM_gFEZbjPn+BP9(MLQP{Dx_8m0gX22s$*tk42g&!yc08haCzxaOj`wC~D2 zxBjNP=$nR%LNj32V*ltKhoZZ6-VcsXerWC02~d7ROJ|xYw@}j4OSbG-SX?~Wo?)?$ z_;VQ@I#dDrM7pSn%;G?yQfQo+!5G5YDU~F{sP_|qGFw?nhj48yUpqcOaaN4aqq_pcv z!Y6TYah5%r2ibLv3s>>}ZQIgYA<+;N{ zFj+=sW^6qE!=OWehgSO2d|L@M8}{1Bd@rWvmNb-v_X2Ut1HbUtjBSEorhecrA~v@A z6&K1?KLpFBy#MEec3@zpnp<^#qiS?_SlGQOuvh?Jhyp15>({S^g#}b0hi`Zuts|@Q z^Yg8H6L}qd`<^c1dYnCmhxnY%2ZNBy$<8J*1kwc>GaFk(NXV|P?OdaG7!e!nc)~)+ zb(1mexMB?eODU-!o%!EC0`Npnja*$_cV-dxHS_P|*>tgWmn#7p2M>gZdY{XG;mVc@ zBeF*vX#k+{?gpl;tn5-1*WC>O^*#_V0PI}paj+a180Z85H=sJ6N28jms;U4;Tmw|7 z1~6+}oW9Q)@&(MjQ%fP$=K1QcIxX#rA2C@|v96?r_nf24+;OVIT?Wyihf*L0j-Ks! z8qK9;rRZuO+63iejwy*<9iB8@>$CSH@hcewPJ{dGpFbD9S`oWu;xMWx$WcmThZ`&n z1KtI&^XR^yv`-KPIr$avYWnWA62H)xah(FTr+ZW+6rD8rbJhUKZVhGyCmIN(xQuD{ znVpUL_Y~E+Zk_jV_j_GzRdd2Erov_U4^A>+660v=E^ z4PF#Iaa!(xHtKg8V}kZasIquS^gDmb=J0U^!}dEF;0^n-XUqWU`v8&#+$LlB>MU~? zfRo-k{yu~g^(G60aGt~H6}mkM04onc%%YWOu}ZW*GiXY3@n*h6RCo`16NK3%@qUVn z%VzJe!85)cBtRf94~`(KN(g19~mi3W|z~uURIfvG3MA}fPe{Lk{z!NOXp}hW;m_%?dP%+b8&M6 z59uRfVuq|@mf{-8BKoG?k4mHA@?(#^KEcYa^6{Wmijgq4i@A-M(e{!ucQbhPD^&B|F9mp zNK2&%l-~rs3Dhzl^<+`TALs4$_hnpvW@~auAZF|PmN5A=&06zlXU=zRD7)_)z}jl} z3Lxkm$l=T^EC;#@J{G$Z7HM0vhDjPFf(H-6f$QS$j8Hpb1YE@f5w-EC;M!$mjADMIwQo0dG8K z78NzHk3o2z90O|3{(y+6Si3x@p+OIxQ&{D51)JCe$CHRvE1jqd&KOB9RAt<9t5#24 z{RkY(r@;{y%G%m%n?35!B$Daa?AGEmpiaVXMo=yrDigtsSWSldR<_GFqpkN;NT5WZ zfiMQ}qL>a!UV%6LsS_m52(O^l_V`E=^*-z+y& z!;ZU+Tc02b1)=6w>gwJ&lELd^qfJQS8Aq$;GZQJQsy7p0>%_prUY5t8c12yb#Q+`% z2$IlF!$Aw4SZqcdmNKBE?(Xh@cM5A_I7I90j@~DE5)`Bzob;=&^cb`CyuA(IC@5$` zHmFs_60an=a#JPFMLeU3w~}tk@c3cDxr92xvf%fVo%&37{aes!+dWAr7w2507=23| zd0{T&+zy((*#IcCM^S287{G~z+_oD4uN#?qP^@2HrIaQr@m16D8gO_-jwbK?PQ}AS zRn8RyD3=UHgl>I5$Ddx%&ea~%1+&;ReIJ*Svegh{M7}YLxaj6+Dr7f&LxZlVrN!ur zjW3zV^s7>}roOMoj-%A234r66EKGnK$tevnf|;E&2|z2BcW-Y`Mn-0RN#hQX^;H}l z9YJ-+%2Q6c=gre1Kn$&;8or+sXBA?nu~Eg`W~Qz(yc8Q7d)#T-xt3T0^BlF!1CL;lf|C9wguFphYLiU;*;rjjD(-=})E z#*}`<3wpSGesDb?p35`V#|z{Y4uJo_xBw?%W{x(FCw(|t4@#QUixGLk-h)eB7@bu&1M841kIH}?x_1-RfUwWFGL@?ve0xs{?TDpj?#Hc9($6Po>FGmr zdJZB3cqMQ_K||^7JxV@MwHm%FkNj6PaqviDLSiG|ollpTcgp{?JQ=`)dj4r4H;WG# z&$AeD17w)^6po4Zip}Z3>Gd634tD4t?CDqg5LZHyEbjS>7vM3Fv0i7|h!Nk!3 zNCKJN_b(&phpbs@>Hw0Zrn38IyME0N*bH^Yg{;X?Zuk;_4e>D+AO1zzy1MIK3z53e| z%_GlQ0+LxU#dDv&#tLW(uliv=>q8O{!W|wATn^@$ppzwzK%rci>4qF(X#e*}8?tOg z^9Xy~@IZ+l7%v2MN$PkoluvN~pr3cwHXKIWvEy^2ks(_9U_8dptmY9@ME>JM>0iYB z55D0DT@!JE#7~)7J{8;%v;m{jQ^Kks8oQ?bZSu-+OvV?$Jy0mv_Gk+-x&<(~5z%34 zzHLTCu`n}>=s3D%1@0lD>?XeXKdy&$$etbgjEOq21bfnqXOS`1oB=|>zri8Az})I$ z#<=3Z(3X$!s0ZzPI5-&J8?c^BHn&eb9HHHIA=TNNgOn4jX>M^nG$)ZS^ZE%|-n{Ko zSdckN-nfI6mVZOFWmT4=8rwMcL1ND74mxM^cl)F{!%os1vUB73LE3KVa4F5g^j%kA z%Rsf3?|hK*#CS3O$U7sfSTbH0qonq}kk)wxM21^8 zNhv(!%5Yse_Q_G635b*g1dQeL4rYG^xFn(enq8*58aGd3rxFHD!Z*F<%-c`)ZIE#P z1sXlqyyauvErgFaQ#Ad@vUaUz@hE0QVJL35Y@=txZD>FX1QbMy2Ot0PKC6IA8V3e1 ztkEc0wcRkOA23zoeRPJ!L-k^x^qSY}?ucun|B8KlcY$t$8uvp6kDpr2J2jGsN>kJ< z0>SYgcsNqHsLrWh%z8c_Gyb!ysBWPwXg&c-=b&xkn*7pg!7PA~;&ORG z>5!1tB!WxCHVw!Q_U8i|k?P(a%Ubw5;a8dKb3SnWis|M;s%X%5M^>{CwcJL7B2ZqU z?}}~epd=L)7V3fa!a{49@AcIQ5D__8S>-?LrnlUhX~QM$iSj~fZ1WZrFgB9q5!^Jj z6tot~+IvD0Z+^23&dIV$+s)fncGH0vE}2c4Zn{nNB9C1;OMLFNZvX}mcs0T{P;2kYBrv` zuok4`Y@vOsNRd~d_V$Okja5k+V8Fl+0*&rYVA;BpFwvxm35|$&1Pud1T&t|1QRQ{E zUp)&eP%qE`J*8`aiYPsn@U>G@Qm!`3`+*9+2iif~qXpy10#0K^x(wZE9?M_+{rwqI z-4~j1IAY1V94mR(etsZ8(Xr>xe;)LUQ4$lMfG?r?a98I?F}>SU)k<~`DJVqEdp-ks zx>NI$Zjk|^{Lt#-e`oO8bk_@a;*B$dr>dh;Db+bldSFLuv?k{>U zekY_>QBh}6%x;vbT{oc=#Cw2nEpN)Jl!co7KY4A&3JseY0j&Vrw+^5#K*>eLIsj(5&3PvzBrpW9 zojI@crGO?{-B#6iMBAw|1RpB&Sqlq~Ytf>U9y?z@pQA=M(fT4Wg^P0KuZQ!yZTH?xEw(A=i$whpQ20 zPsRwlCB;C*(x4QGl9-Iq-T9VLe#Pd$w20Mw1$kB)@AoxFH_{#b~*jjv%>BMf2B#$YL)$8c#*&NCa)BE^% z9HnkapP%M3CB%<*XpNMPc zl%v8TC&c^pLvZ1=&xaHjVO3^0YcUNbp+b^GyB3j2`Rjjj-yM+q{Pf`l2)kM&k&AYY z%I({?9p?cP`=~uss9kRQ(HV#wc>=MZb@LS*fVbnt`ejQgZnI$JBlV^Lsp%KH_5u^) z!ih=hNfuDOQ(}7e=m#d{w8u&c9C18}YG{&?O#v?ZC9<=k`)nQb5#yrD{%pXT8bSDj zN7SAA1f+K=O>35`4n&7po{__+Gg(~TMw!GR2wQE;e2YA2H9~jCBL_`GS(K^7dBeRc zf5bZtWGM0)!i%5USPGifF6q5DVQtbc?EuFt;)WB*!J9z#P;^ipX35LUloX{9bhiBA zj{#VGTsIlt8!eEpz@Z^#*JCuHcTqg~`ST~BzXlGigdB*&=)&oYXP6#Vv^YUqLo9-V zf*?=W`(EgRUdQ3uu-QClsU)SPrNzb3C79_|I71C za50Nr>EuWwqaB=AZjEHpF`oU+TagX_I0-;THWpwDA1#`gYdIuV3bs{~sA zLTdbIwdtG#w1}RORNE5i5M=ROHdRKI`{IF<=m; z`Q>H;+FmZ@`#P3S5!lRiL!Cv38n?G>&VbFv{`X@0yfxv#B2D`~7L2tm-veG;l%rj> z3g2(6byCidt8MLHPE@Z`N!V+CZ}-E$b>07d&aa8oJiJiqM?qEEoo{>#c$;I_@tG5u zt19EyW!m$T)J$Y`F;<#ByDB|!kvqB1(G5X!D9X+m2HX<1zeuU1*{fAl!pyaY%O!-T)3U=k&^^xnKG{h_I)x}^4sZAy-`Y2MTPn!0 zy{>Ylc!OY8TYJ_MrdhVxlZnOl!L4Aa6_AozP=Xj|7)RsjYfviJ*4Er+ToR_31Inu( z%7BwaXWPyq#Z6GQW6L&5Ft@s-;UmeCa3@6~?!x_9Mz`B%jPLxmA&Ww5t{a4O`8SxN zJ_UsCzES1qaT(|SSghDY5(Jqkbu77ApP*9^CnH)ss8C^X%oF}dX}tqH4* z%VCld>KO63V-2T2tU_023BQc@xTVg{W|=SGsd+n!vzxo~)i50>k2<+n2_H(Q&0B#D z6*y^85bhQ|?OL2}J4+LrjWo`!SltP@;t@fC_+ozIH(dZA9$8b=oZ5-o|4Ki(5tQYx z>&wzlD>s$McXppb?9(`Y(5+wsuK0V*v4B)<1}}c8eF+=sIo-}dQhA!Gy#|aa&T56 zu{Cv|c!C$Byo5s|Bp%;C1(SR1ehQ|lR*!5&~G{rV;}Lg0brND*0qOeB*~Qwp{aZW$5_1;!$X6HxQ-`2e`^^Pl`GH8C?Y zYkqWCJT1)lBU{serQpLaP?x~hV^DBt>bL-wB$*+mkC5WVLpbTUA-dLSb;s?=jU8Hp$vQYVfEWTSzxn26j%MS<1FW>QI0Ku$KYGAQdw#rS zbD_)MQZ{%0NiMD&oEP!!D7&IU&8H|DcnEWXY zLz3zHU0|t=3ae2_+bTp=0NmmU7=}y2v7+y*D9#<#0)&)lI^lb`^8)?7T)xD0_Jx!j+|u^zaC!%(el z)Bryca3ARCkiaQ7LgPe}Vrbca63@B&F7S_q6GJ};Sm_!F5)aN~te$r49##_b25%bz zs2Lbk0mK@I8>S^B@Ed*i)3rxb0@%s{j6wi-0*N1hSB4Ekr(WK2i~c_Vccawt-vM}S zZEYIr>b(HKBD>;@jg0~5t2^iuT3lFo9#MNIelN+O_KmvtI|~GrT8+^SBlNMBPnlX# z=AoJFQ<+SjwMC%|xr?l&aCL6hR-#pw$k;AzOt3nkz`+3-PFC4^0_)i|Q1u;H49iSA zIhro1%Zsl6oHT)xkmj}jfi<>t1OR7BAUOhNqD0gP{9H<+XI~3H#>E{0@vZ|K!1^^{ zF*Dr+(~Jeuvn8omO{cLoLKubqQOq9~Z#XD#ru9 zB`9h&rU4rks#y-zjcmj-spbH86cTt@#!%+9O(6460eKFbD)1VJ&ABE+wjHp3z!M9) zoeAvvOOCM-dZK`e=v@H*0^XvOUFM(3Sf+i(^HGkb^+a}V1`bYKvr6A6sUJ+!`>7*2 z_bP7-7uQE+N|AC9KsTO&qw3Rfb3N5_+hab6c-HsY+ad$^>nu=uk2b~&I!8z>lIIHx z*+J*1r}hzenjADli6bdQ&SN9WQvj3%UaHA0!hk=2=ClV79QzPi@1$YK+Q+1&`^=>r zb9DocWuEW4P{`-Orqf`#j^uK=dA~(p4%8OjrN!N>Y!JbKzT={%{s&MlD-q9Qu*Esy z*(1>?FAqRkK%kB3M(u&_HLD1~5jDUB^d66LTf>f%Z-^W;h2gK9`A2#@HetH3_ExGj z^+ioH{2jY{a;~8_g2v34)Y(z zmm|T=?*MCI{@pDSf}YtSj>bR;Z~-0NJMqgGk0BX`&LBWhy$^c%85EN_hdMxx1PDl9 zPmeVLY=xLf8*aa#AXD@oH4cikxY^$l^K^*&q1~6;>&)+7FjYU7PvMxIqKeLHjk{_k z$SU$(l6)2Ge!<|-u1O7l4$;q#rHwqH)NuK5Jg{OyWqhP6wG+f@5aWyP4PF3+>hT%9 z1TPf>K>Yxy+j&ZGKxR z?8NKk>@jzoT(?M-fMYWSein;Rf;eFNqyNrvu(R;bTp#5y%tCPB2DDgP2_g{jt|%a0s;bVZtfISzqQWJ zPCyUbmgqq{Jx#>3st421eLhInu2yQ~JFprN+`s=KiozQ-$HDnAa2}MR^f?7GzvapL zj_>sau$m%3K?8>>g!HN7QpXKE;S%5o+}zv*tw%lZJ79q$BKb%k03mNr@>M(MYq8=>&ScM z<1ZzFA}wXTsdNpvd=&{bm|Cd{qX|@suW*Q1HHc$5rSg0XAK3>2Bp7+}ytGNqSoR2u zP=RntCyblL*`uInFSE%1U{1E4VO=aj`a^;lkrw~ngn+x|e09jK@Xh?F!)ib$6zA}!q^-AH$+APv$X zCEeXE(%l_OcXz(I@BQ8J#(3{L?tgIQoW0jtbIwn#Mj!0c>`R!AksfZDT(FF-j+ z>2R5|k&$WlCh((k?ftm@!#Ya`ybtZX&RkZ&HQU)}RREFeHWgvix?krS`nk8H1s>X-HN zUt8z=J~P=LFF&lVpF#%0%6))-;{Uy5we2}xvbIa*rAA<$$1mqkd1co`{ zVy|C3W?oLkO2)`&4P=M&J8hxV^E;XWTn9kRp4Xgi1e1ZT4kq;y#lrY_b%N738PUi)bUd+1N9ynU0J;7PiEa1tFWlD^7TO&Jn2>;n@Bhe zkM*;9TL={bBiRkX8Jv5;x??pTI|0xEBWrNx_xrd9Fte9e)0$t}Y);X-XdRm~K0Ae? z2Jnj)lvRLiRB{#ck9X#GQkNh$4^)WR*W8SEUgJ2oIuS7G{WvIzsOdZ&WM}xuVcwIu z>)?&`a;7l*YMM=#W}xo(dE*6cpYb>1;~^G1G{&=F^!tufeRat$I%%wH{iSG|;aX9~ z#U#v@Inx7=qKM@gi-OGh5Dh34Jx~-g@bF9v%)_S9hCg1Mk@@R+J!KD3s#Mt%fy_y|wr*nN^weyY**!7w4?~ zfS;x)TmkH?b~)we-U_^GE;cp=x@fZ|9O!-x z{zbgh)S@KOjDk_pWYS>$Hq_HB(757!fKS>wAjs@+wDrlVE2KA3p#1x8eCFv|mD54eyQBY;*_goP%&a_5ua`LTJ;0(LIR4E7v8-K1 z=5n{2l5vZ=Q>VRX{Eyd{+4L8ifs#g`5^wp6-RyzFjN0wjI?VF8~pUm{BmFMMR4oV0$w zF$KWiTsw~G0X%B+U!X-9(~03}Aqn6lK2-Rw!T86#2AA7yoO|sZlLfunNhPeO5Br&1 zC-Lz$Y!p=m7s*G@J}gy)p7$)yZY?RKy^)2HnjS%4Rz&@pGFVMkla_LiozKyE z&f;k=cjGQ;2$7x`Gz&sEWkAnk2@NHPL*0#K(?MsH?oh1XPkyYZdbk4Oo^?>JWR(CG z2ZEIiINu3aKgU}K(GbEpeWyIzF(kd8=VChm&b|{6_@JYq;WDVZsulvm?CtL#%VCq_ zJEFBSKN0*=v#F&+_`NAm#2{>Ogj3c@oGIn5)VtCrfo9TAiLPbEre95S@sCZaAJ>xS z_SYx)RaMfnt-O8lwE~on3|Ej9@($`9=%L=l^yALW{3WaFdyw{s{gAMzY)Vs=b@5Mu zyfXdDlVX>!lGD(!fBz;d1bleYf<;1jtT`w%qGp z)toq~1$Fu5j?)Jrp(q}g3uw`vTT};&vFk&fClT;xDnQ3@@Nvn0tc30rtSE=gEHzR2 z%ZQ!QxeH6&{}FFBNZ2Z^ys1|coGPddhber|-WJ!$_r(|f-aDeqBY;QwUw`RK5j&`AvpfC0g6W@n;3iBt#uwh^Z|qK7l`P@S z`~V||elA~G_baO)t$d!m%E$UA$?P44JZYPWC(d{GJtWsQEg~ej`1qyxJMRnxr7w(3d;>Gs7iU&{!bi_dZl@r2yvoI zCdK!Yl}2V~Y;vZRKd|H``C|~G*ytTVkKI4yNl}Ao;evVI|86rZp^w;_0JB!h$JnFM zEq2EL)jMU2D-mBBU#e7Ss99%y#4Ane=~$qPCUVNl)yTsGNDOf;rV(QKY~IN`yCul2 zQ)|BdEuPZz{Bs3|&N_pq=#kfID4D;<6 zTAfHn^G6PkY_kvlY!~X3ODVX+?n!wAUH)_f)T-|Nkmwt zAd0_RUNni}XjNMF?kaK zF@pnZt@dY9cx)k!hR>6y0lVAxKq%WS+a_78PLY>6?#>lz>ECW;W>7fEogJL<&Hjj` zMD?4yi5@gFoE9*Q+-ZOFXzD~H9XyGs`)I*s*H9ge>dr|^Mtb5YO!ycys+DT`QD!Df z%x4c#D?ZjDMOz(9nE$M^%-O7|=y0l)+HVF^`?~@93R(>m6X|aiDUj;R_|e@#iSU~# zSb8nL<}+6Z0oxohp_3;)RWLTIRjL9p`O_)w5MO~qYKPax;~D-zKu%LMIb7bS2`V_dy8h$4=U7}s5a>z1(5=!o>zE|4M5l>V0Mc8@)0bVRO%inv# z4$u3#7U+t>_4VvpMj?P4xX&T9{()A-kj|n$=8Wyu`Tv9_ROqa=Nm6!P^tODzjtf zLiF@#M|am2yUP&WIzrB<9S@G8Pd@k{4)d%nV^-tG4IGRJ`D{$?(IPD+s zgU;zqJc7oC17>y(=kxklu_4G@N|aTvWy-1>bBj7>a_ zN1vg~baXuPd_)4ImDVw!Q3spTYKn?u6;`XC{(Nt2Y9ioqb_Busjzo^AkP!R+01-3* z@DH9sV+cB=KY#uJjWe1qMe%D{t{6Bq7)_v*g@lJAjQouoH$ZET&tc68X1>GoeDRtF zpuKY_Kbc<}?E+ySNXjk&WyE7MZy=@tvjxoa!a_pU*0qXNwt^me51=cNrN0e?EX%*M zeBy`GYXt{jl@-^ud4x_|Os??7eA$i6lKqq2cmtbv4;yDs;ckeM8m(z{rlp4rhpPJ$ zn50t?_G60-Bwq^xjHKq&mtegEEwv?(`$&lgFaXqveo8m&j4fd9Mc%ZIAk<*S^~WWj zTyH(Vd&*&*H32!KAf*j)2b*Rf)!_A?fLFs~ZT&3t{Ua2V5HhIdC*aW3($;MU@o#WP zEg%TAY8s;j+UDaRYHW*Q+zL{Vfj(F}2AZ*qP_T9OgOVFi1D=bc*@^r`p`GT@@?D$t z9P5p~o46tixW8IqHHP9AN&^cOSM(8w9BzwSDdF%+;Ip&n_;+(;x$I% z>5pF8ZmCDgw4y#JQEH|@Kh9z}Btmxxis$%v-tR&txJWak6f_7kFMQmVc~I1tizH3I!z-$$;>+r~do*2ww0sbi?orz*FF~ zlAiiv@Y?4Pu^9H&Vm7v+B~YtoCK{K5WrMB4vu5&c{{OH5gInC0?Op%6RRtbZOePH$ zkL-etTaN)z#BPyra-M zzZJ=wE)M&Qno#>HSShIclL0v{*6Y<6$@>RvGuSJUrC#rjHlGO^X@#M{?XY`QLpdtr z8QQ|HS%XiX6M{?DVzF33B+Dvx4infnY_D#@wPBtFid3PLBv0au{N-3r7iP7qiv7v0PwVT-ip#b=36@vUz*&=&gQ7e&7H3ut#xlCRm=k(2ZGTyoASw&=Fb*3aw8-TPLc#NJov0z@lGFUtUnO zYhw+S7%Snp!Ib&&(@$Y)I&QJ|5sD6rjatXUxd0dFh}&fh7Cp&? z!;D}R0hG=xU^E zCFR$l1WkA77oVz>AS&NQazNVxCV))S)*b{M0l12&-$r9n%eEK?5c1HKB8U6uzpi6h6UsFe69+-UbU47Zw}no( zPyR3y2QU26%fvzbY%O0hrLX|Tu%H`^CLFS#*TC#SPe(^bMa6Z#*YDoU5Dj~~JvtLm zP@?E+Td>pabV>*au{(j$1J+*^;P7)@j_?4@bKmGjtnii4lqS34pN`2!^cAv=mv8?`q z)CubVjrr6zGT6v%;f2_HML3`(!%xRr++jEQ5pa#M#pM?Y$iM)Yn2-Q|hMXbvfZIHP ziVhyQwuD1_lS!K!ss2<%kZY5l0g_>9ewE6ezK^%GvED zE|S2}V>zUjr~U>e&H;n^m%HJwy3G|~4MAy|?qV(-29f>?6q6poWZFbM38)rKj6$tS z2SenVk4&t<2#wNt3;n9zy`sq!fK-@`C!XTtPlCqf*PRkKO+}n_+UkLN&E~t#AfzBN zXu+%yW{y;~!)l}KEUyCd^SH_rci*A`uF-0TQrP$!$+ADhUokRnz?xX@Pt7EQ|9~ok z4e1QkKv{VL3^#*En{{8V)dGJ7cY$c#hdbkZS=b;vP?5go zBO<&vOTw_qU?wMel>(gy8(JK=?15b_r+}FgvqWJFu&qR}CUmc;V8@)s#l~`*Oq2}_ zNaIwrzMta@AmGw63J(sp0L(8mJiNUgz$bAyB~W@1YaW#0I1|Q9UZFky)RrROl5+-s zP7ASgXp~Sxr-|^a2JRMjQ-Aabhpr`FeJu{}8F0pD>(AfZKNMyv?n;+^>vkw=;4YFf z1yF`;wjwAP`5j&!*Ih$j8!glYY7xOnz|oADltGgOCyPO;F_3Yzr4tEsh_49I*Fk$Q z@GopJ+QNTqQTV$JkQW%JXWaX!U(=m!aywd|rJp1D+V`&_2=^xjJUTn&F8#a8*n>Kg z#IzFQpj@7YA`H(Zz4VE+F<9)O|6q(>0lorQ9;66={Fn!@ADH%`yro-#J;r8zj7%y6 zV0i%gGTnCyUUiTIvKT|qu0V%k(#koE{EQ<(S>$48H$$`I^3K8m3ap3^0s-Gub>Jz- ze9^=_Ky4=GnjAx`YCKY*EXS3{4KD!3Xu+SVcgCNr!HUOLu?+0^1^W>2Tk!pD0qo8} zT?Ohtz{LZVfCfbvO+@$!!ZGN%NP}|`ct(~LnnAfN`CeE885u7k?bs;tZuNZD-{fDS zk-+nyFV)YRVBV9RnW|B0m@lTrItP-C9FgZTpkEGus9+19k$mG2{AOL{Mkb4ma%uXf zuychEx?cj{^ueSbPp7M+(-p^a3EDP%9_KS_+(oCG(6-qDCMFikd5oCHfU?EG#2kce z`O~CAaMSxGg6-`9hSknxI+!sCxeNA(8?^1szBs$kU~t1;FQ@|e6EP&AdV2IX_~r3Q z_0iS5g>@y1zgm0S6g%G!q9SjGsSa8D@o#pyE`7E-Ka}RiL-WK1z+CLSDjF1R@N9_v z+RSx1&$O^}^HXw%BnM%m>#pDF%UW8eZF$)tcI;Um1GoqD2UsO`%+w3DBX3vpZt+hV@`2(=PuT~(;0 zH^0uh;678T4UX_9$gfT255)h3t3F;6icd0I)D#8WDZCzTjS~5@+cOtwP>v^!$>Vq1 z)T1n30CQ;0_5q6bAu_=E34BgekHEa?+Iq|-^71CATb7VL^NgAB9L-m)YA}!DO{!Kt zhd9cW!tV1A56^fzv2XK*1shj>Fu+jS^B*0<3)gm9ew(+|?{PT-~N|kwbf-!$@*I$C5%wjYLA1fD6 z8%9~Vw6yTAd?e%x#G{Jsrah6# zyl1JjHI}7DtJ%=M=uPi}iB5~@(2|E5u@ovfs1CYy%g*z5bo-YU>%%+r9^?uHDU{!% zwj{7_AA5WA4FNWA3(BzTOXiTU@ZuVo?-zvEuWYlL%tK-m#3N{>C5^PC9+=1u2?)!}M;P};sgF{R zw5>8)D($z`{ig!P+DM`5}&I zkl%ZTS*BXvVeloczN&r`tTBwwf)qku)dd-OQ1~b=an%**$`9Fhq1wAN=*`gb26T$A zgoVxMjVAWA7pTFc1OFiGRA|)0aoZTEVJm=({2l@dJiEUMBseW;(N%Tx_x_6P9*h|G zAB6NOA*C=e5pJmh9R)P-aDqq0#nHp(afQ$G@sbad2**_5awi~C9TnmB_`^ps1Ia>@ zF*6KI%+Tm)#}-Jv`iqX#2-2&<{*G!Nmvb#^^TYW~oo!)*H?F?vKKtQ$nv5Vt>)&;*_?Odsc)OR)aFK#Fi%J{coQ)cvYxde(J!aY7d=I7^! zBnYSfyfM0?t}E{sJFdD`V9&5+cT5g4adSI^vgm)m@V?s`m)11=t1tfb9#>l~t=4Zp z6DkXS--gE0%L|EY0XYg{PlylP8n^Ra&OtG~@XnuAz4izbQ^o+^!4^5JvmVc=>(u2nzxpRZ9RXIfUmVHh}QE)IYZ`r;H;f zCqGyjU7hivHyJPSOZyTX9StEf&d?%(RTkqH`C&QOPskPE1EYh{4HY53?ja~*f1}O~ zY^_?-Nwjx1)PX~DJ2c*?IW;-ngItgOK{2PrL&hgyzAHna>TnTYSS0(wRq}nz4+W@* z=J+??K$c?j`vyjqlrmM@LZ?!VV%~EJAt7^utOo>~th?uj+Dq`ZFynqaB-qFlmIhHU zXjA(@U3>_acfhw&$k%9ung%-5L733*U@P~)lY+q?zNF4ZTgW;EDJepQgBBDS{vaL3 z+b6)o-95&#PY}@{lym}uYz>-N;A7>X!vOKd+=KQQ{ox!a-YOKsKq&Yjp!+hE(p#(B{?4>EO#Q91`@ zX$2^lPWM+AtCXgFab9T^J3yb)Mx8qW&d2R0bVBYkK)03uE51cwAMTT_w{|1rFmeC^ zy8#`~EdpEhj)<4-;q>9JwTrS`L7^xLv8qSce42AYTJHmH+VOT#sM&^zvE;nKpp;b34#` z#YRPqK|)eFr+OU<=jnfhB;f59b)Or9QRv8ID}d)B;+3wLy}doi)0B!05Vt89qE(ti z5aO>jBM^pknuRYNM1N=ktIgQkyXujefo zr*_h_UYSg=j(*Lu26QA>u~>*8A4z3W$hj#AvhPrFh~s!&2VOZ|FxHT^+j@YXj2@Xg zImPf{ZNri!z5P6jnz& z#7x;ii<{Y50|VEXTz7Z3%V5)S1$^1iUU^&FBY5znn)JugCFEy;9B_0}!FEMB?w+lq zG7t5-b;=d|3RK-YgzT4-mEqcMm@O|aXRHPfdgv|caUx+VpawBDG&Scn|AUcLDT7ts z+jDE@w{yMQop2TF^opz&Kg`uYi|v`l$jC_eb49*6bM?@SrOPhSS*@yqbrrT_^-dc# zj6FG73{xvr;DSJ82EyTVGN+EM>V}}k1#jvJkp2{LAap~5zu=^^cGw3kMGm|G6Arj` z4@c*Yi3Hh{hqPs=IwBexFg08NF}ysIKg*4)PzLN2ph50{ zQo(ux6^kq#A)EPJ>9q6ic>Hd}F)Yy*W^Gm(1%+eiN@ZcxfgdS3O#&(2<|@)8h=_K_ zgsH)3NJiEeN!OY>tZdN!h=Br{3@hJ<=%mLceW2Ms{nfGD2|VCaP74(-~U zN!o8b6e<=0-t@NkI7xpzf$dpn^#Gqiy$sM8-N-B z+Tr2h0lKDri-it0vxGGe3Ls+iFVZF^Ib)t3wHsx#{+FkZR3|I>g4;~&6l%Tm6X;_FBmM%?UT+=b3Zu1Ggi!4W z;tvB|r(qC(t0IdJ5CXU-^T`F0NY;`CD=;?m|7CchH{_$uk z+4g&Qh;zM1EB)F%ekU~GJDK!);M_4yGKaAeN(t=ilm9G@q$e6QC{Z7$2CKJH09Hpw zBW`}Eric*>1+1^M41yekS@#$Cb)f#mA@2l!kOM&%(Arl4#sCE$e&B+1T7WuLz^Qxp zXIq=lF%#H!p<{=iY5wC(FmB#KUQzOQF|p!RZsi-I6fq2-X=FgZX-ke z|1KyUBBEFcsqpi5&!Q(z|{ECShXOv4!m(%1XbrNyE^z*JwRddkOi(jNd z;AtUU`_!b&s_n zdQvi0v&pw4F{8~kr{#tZVITTaD|@~k^~(fVP$laN*eP&!m|{oVw3hTz+{i~T@S11eAh+E0+4 zG~p_g3vgD5gvc=EX%$uVo#>i+)kBD?M!zT23ZrITm;F=Dtp&gz1Yi2BDUrXpt7;ZL%t>0^cJv%DYavE1iH*LyDD@ zq$I3c@(x;X9DrO($JpHTzICI$Wx4!8rIoVkH8Pv3B)*ACdEaGn0uH74E(N!be3*~R z_bVWY>C-0&`QiOD%a%lTWZjarG6+-+fP~PL7+mZLVGAe zw;URL_NZox%H&cgp4)w`D+3EQH}6}7Sk+TEuwCXSyuLdEdj&*WynnVu_%ZyTa<8-{23OT%UY zg&WXWj;7PH_&B$-d1K5-6%tC!?p;Sk;Msr-Ga{l`(=|<|r&`n@E^ zqEK&ELf-n_1e5sMJ(-I^k-c>ylVyq%a-R=G{tI3)9*rIQGvUOmZ412`lHz8TK4oU# z^j>}N^?V}k{3&g4^U!m4o|u_0o^Fu8t|1>#XANX{df)i$15Sipi)`oVi=4j(U7;neg)LG}Cx?=jrD!l% z*v;zag2s-1LMPF-{dZOnvS^q+w_G0deAR&A^sbj^d-+*@sfeW!b=M}9swvrq8l^0j z0?*AR7K~dDd?ti?x!!wftr&lB#QN|C6Ztdvh~JQY?!%E^j-#7w znlR9fj~;!u-wZit-fXmCwyH5}qPDr8-z<~Z9LM5V=M%Rv6=R{(9>zF*ve!7wtx{}q z8tA6_hd#=Fc%WB>n7^x_GS`XLe1~6Su)gD5;O?x4h~3_o)*(72Z8qQ_4n}RljOjlt z@mw@jUT`-Kr5VkRx`?TjuvO9&4{v2&Ui=MtHO}vi0*<3St_4TksZ<~YS2*5zRP&Tt z(f29M%uZu~gIl&*$kLR!$SOtbWgM-h0Xca9cCb&QUweZfNu$&Dj4qCFqmQnfr|^64 zYL0h4CB(F<@36ch4I8Td(5tV^oU?5nr9Ay}{uZjN$&->uQR>{}ie3KAg2_kgsNW*2%g(ap!e+znsK2A2 zphJAK-1y{z2)#?bVsAFJ8zYYUtS7ZL6cjIU{0{W>*&grKy$p$F3L8Z&8pmMf$bMwp zNHOh6&GUHkiI0*P2EIz_698U{(w|tAvi4E`oVYgAZ`7OeZAqFk#hJ*N4of)PvryNI z!^X4ztwtNi`$MhWH(x3i_kU>P-lAoV1=NZQnTDU0{atL`F|XGBe4W@Jp{QtJRIRnN z{#@YQ$`;)=J6FQv?;=jCvjuL}wDTun!MOz7o79Z`oPY zik)2ED9mN>|0L;lK52SYN$N=_73Z~C%*9w>beZ8kE+tTFKC5Slo^36)5mm54kA`g= z$5~v?AaYZlG01|OOV8+u>JKqb@eJtzE$RZ6%2XWEAK>PK@(fFy% zhbn_O$Fd$CxF^^9CN5mKv;~-5PGAo9+NL++@sZaqCJC+%3_?3cLSdtM5*xdVhxpK~y-yaWBp#cd-pe z{zg7EAv^I^?CHxdIGf_j>pNGqYjLKOL24i;Bf}OqH0dI8p)4c@T58JBgL=DoO;ofZ};h#YzUW{}q@3 zmmg-A-s@Og>&KK+M&^0exVAZFPKTkXJ@Tj7Z%=$GTW5E`;0BU8=Xf zD%+kcr#kF!WXtu-%9DL;Pe@GEUeZj$kX>=@t!}$uGh9gdhyK7!?3;FHw(11aNw4mA zEfe-2`vYmKBehY}pCy|-f$`I_S_a|LpYVk#A*8f(vF7t}psMa+kb66`Hn!)K!RZY) zHsW8}UvImfDa?t_IYz}&J{UH#o%Fi9npnN4@M^WLZlRI*w!@)U^?5_ml*W9;ihiEt zm}e%3*ENm?F&yyR0|OG>4^e>E8inbrnM@|^ZeFFC{PG3J4JROYA=h{qth(1| zO845s>hQYUWHoY$3vvlANf5RD{*GZFEn&AB8vIX@I0hd0K7og%K8i#)*Yn`kiwYb5MW|s zL*Jx&QxJSkY@)e)kq37_LC=Pz(X9vBnoJTHAF;+#IVaa;N&JVQ!>4h)Yj1f zBiZF-nVF`eW0lbv!ix?g<_c&L+S=Pe*1ZL013Ur(=ho!#78$TQwN?}xB8h;2AA%zH ztqG-?Wu5C6m*aM|px|Kbe_-%m1Z{v!h};_7j10o)!rHv`K`rJ(z^7E4N44RtQp@E; zn|Ho8S5mkDG!k?vW2IA$ui(aB#9RT=4RWz)BpD5|&DJ+6O)%14gM7l{0S<(n)w}*w zJ!ai+)Kxn*8v7G>Cvfa|;2$>E$u@O~nB2?&m6ZMD!5Z1yILz(TsiRy0Lj0|5{3(XI_=Y`}w007nIa{#6VnfH8*Y(aKEL=v^gkwE2z&T}=r_V^Vqida^N z2LS^W6)plufD;YcwWV(EQxUp2NE!fDOdO=}fx7zgWHomhz)nVe2>0$F!&ak7ECQ5J zsb&ez93nN#p)7Q0Eot8}z5ap7h6SS8G{7ukLQnBF6QnJLUF^aO%HRV!0)FbIgXMy` zYQz4cL3J7XsxdLNHK3a3_+@xO%M^=K!4CPSkTb97_LyLPY1`*lWW8)xNQQ%i@~vaa z%wrrHzfm`o`_E$tG>S%fE!vno!YHvdtdcw{;8&it3N<2C6R~0{knJy13;dU0*ut(? z&tY7YBpS&{LNQ*oewQm77$pJ({;vcN6^&|@Jo!7u(v|*HanPaM`w7XAzQAlwTK0=F z(w=~m6Bb>XKg&d|%Ozk<+MU1$fIeMtk*q90Rvr z;PoKL+H|}`wCO!;mmo4gw;cHR5Q-S!ZOYj~@N3;MbR0^6%O5}<{)3(!JSfHO^414m zV6-$DEl9Qq@2bZlK3g|+!62|9I0I&IKC=iS-C$in9B^ihTJrt8iJW7t8`aKQw=r>8 zQ<9SKc$_)G+RgNtQ7n|l=YY=bPN0S@MZTUuq92BqBx6RQ=RXpyn4Tt?jhIZDD~EA? zwZDD=0(^F~cLlX&0{J*Q+wd`i^9KNrhAm9fp8!yn3|3oefij&X0K3^)Vi8tgwSjO! z5UGEpYp-g>QAZ*rC9t)MNTo>2w?O-11C#;eWXkM*94ODPtf0ha6CPs7f z@njN#ES5+q5?U&@QTBj60P6-(nLw_`+7O0NMHtJ4dSR<1sV0JAumsS87cbfrnl<@s z*ck^ZVEzMx2oWP=UyJlc3WVEAfI_g;RIPO95|^rGoYp_?p`WCP#r9u?tHs^tVd<*q zA_nCeNp}dfzo-uoq2g-Gj(z+HSLqyA0MDE6 zztFE-l!12l%;P-ydENNYc*; z!9@eBbw@(>^G%r=h$OqMG_?nDcvYaWC@6+Y0pO@7L|hYjR>Pp2i*Y=I*GmyhVc0f`BL( z(-er6F07AByNpRlNNyOYL3Ex-?!dSj^E*cU(9>L&yXNY+X3pJB%#4`|5oPh=OXG_1 z4=9-rD6nG814OInDvSRfLXtWv1)1x6$O<#yFhax!_@61TwGNUV(E56L*?}qz`6%GS z>-x!p_`(oRv|q>HoDvIQM%v~+m@t~nGynqJN@)PlIT*i=p_Wy_^9P1WE=3X2BNFcA zvD<7QCJ5x9Wb6x~KY_@hy|q!WW}KyVbRq6IkN}p=i;iXH!s^E7c9h4>-cts3 z;3omdN3!iF*5UR31B^S2o+x)KBO?o6hcneyfEIA_#IwalK( zG%J_d`3dQ!`Hv15SJ}a|Y7wdun%pKkT_Ys*|FNt_1dS`rZ9BhErIsHc9Gofe+BGhs zr#!%Vw~wpkWT-s-vN=d&x-Cq8R8@M{MZb?9FnWqrX`?-Vr_R&su+Vb;OkN=3|sxrsL<*0N8pU>C>1$wjD3+q0*&m zj-jNjJ51|Sf*zh&auR=gGe10;XhGxu%%qFBR2eSPogCzAHs(b(k@uuBYX1&P=T9#K z3LIYAoABPxf4#^d!FGbAPfm>u<;uMPwQ>M|Ke$4mPuFv?<4;Lso@Jb@>B33hn88Yc z^njnu9l!9Usymur-$K6PZ z3iM1Tldq4n-TT!G)dI9WppUMCveZHjG)!5U{6H}1tO1B#T3UkU89C!p&0XH*U6_Wj zs?u4{dk;D^=6dkU)>w({T_gWlUn5$Tmh~9_arUcX^i9f&l7}xHrL*J?w!hJl(R~VI z|EyShCGk8n`d5Sl5p28PsgFVJu|WM<^2tArAARlZ*H^qmJZFsGX+FUvIw>u=xN^2* z|9r>6a;KDgY5OZ+;yfFCN0)1pXh)=SDimi@uOd6<7=s^vV5B4^x~}oz<7YdJch@Jd zwd9;Wz8S^5+GM3hPi=e6?XR(o*{4Sjt%p15e8dwT@+w@9!@NR7xAx+81B&nAbvM%M zeB`B(ts;!!S}R(gvE>OObSxfpPzQ+Hd* zL@SlrRZO%^$j`f@zA;pPeD0FUr;C*Zaa15_Bm6R(6(M? z#ZHRmP$VDReHv2#E1gMDZJu@Ll0iQ3KySA0b{#L^d?QcoqB6jD*UDUlW6q@V(`0VTI3z#$QQK^+L{D`kV4VpRno$^-skkhr=|A#>q zYUH8F%!9fhXwVRu6>z(3ps1GDKCW689X`wK%HXc4WWqn~>r5JwQD#-q?xXox#f5)a zUUUW)KLwwWx7H5&jXxG*L@JfWnhtNd?(ZhkJDG9X46;^4X~_ozA(tY0d|g3!xLKsR zSX%HxPBMm&VUsj`fh*PB>W^h;$plZ9;94>898zLz^F{q)lA{zg#LT~(*5t^m{+ zf+Oaj?K1=g>!;A-8}f;jg#e^|({K&tns}<7JUTyfOrR?M(LzmPt#gGozqS zS74%fjs$4EtR5@3(0%HfY>~nDPNr&H`aXkZXuihWU`>wUqpNQN=hr_eg2a|HlrYbN?~5`cacWhNXR_Eic->=`1N@vdO-*djGdvWmk zf5|`putFz7**Vz9>CHeG&6;C*dxu6H^+HNn6bt(nyST-9l9OtNU1lk}EWA82c|Cms(7md}rsu`UByIverhC`^r#45teJ*&Ao9U@2%WZzGS_znAa!L?uFjAcpq}0N zY^F(3w7wfuu`NZ@jMjs~!!`&V9 zopkov{P=D@;rk>gxs?8~H!`40c*iFS>ojtr(=fel_4{^$fr6gmQWERyK`lFxT(IKQ zLR*yg@ssq=B&V~#LZe0cQWNWl%buXXdSe0y6(0l3+iK{k;_F*V$5dKlBZ9xQxl4s< z+w`gGy<=F=^IPxY-Mus1CnTId+}FBLma4^0eRkAq6k{Yd#KT=_lXLJo=d5T@@FJBWosDzPL7&mMH43k+0D*7^3B#BAt^zl3~kD z@K-)CocwhSRokoZVZw}gSp}UZZC@l_Rzz`!;JXyKpZ8_vUaNXXK(e7HoF(o{hI*i1|$rEJ_-9KN)Kj!b;!DXrWs**pMYjBY-kL- zi{1xQRY6oXYEYv{S3WU#kn?utgY$N*fVRdUnx?z%){UgpmfYL+NiteAM9u!or?2%D zrizk2J47kD*JqnqhGJR#)7Tz3+T6YZSASrVZ=Um|N#k+KPbKecQ&BZ2jlN|8%MoA& zWgs15sI3Yn8iCj9Nc_^*Vd*X5GgQ16ywgw1I1+=@w}a}XWsMVW8}iHdU(Mk=lkO;K znt!|;Xr?El$3XI@?Le2QSu9GVOWlV`o~!AuAgy8MhGWZa)<=A?5j;(@Ot!6CH*+{D zqUPSZKW=Z$K597xL6a*ewpo9{M*u=g?7yVD;S^C_Ah9@ zNAoJH?5SOf%FuruMC&S^zHLU77wh9TFss^l#lU5SueWaHZR6wyBe0 zM04#~_~lL`z4P`g!C_fjJ$CgHf2g}3_qLb#+0^Fw)X(X&g_(L9&KLAJ0mNq>E=Mz& zs{0vWTi=|oJ=oD=^xneXv~50xamUuc64FDe{@$SH1nq|Ro5Ak)JiD)^FV?4HnK~C# z{Y&}Ee@kxCJGTlu?-aBXSp7?Ufg{U3@|iP_!q`8b&+OyBxly;)p8A!(F+6z$|C-*< zvAG<}Jy*P7cT+5UPR%-ZrqmII&MC9~y;nx0M5bN@3)mT3vj>S693v?N@im(!CIYxiE9 z(Z1Y5zua09;5kv~wwJBK$Efq*-usmzuk)d=oFJn~<`esSmR6r^zIn;GeoQo%6uVtB z%hz;q(F8aPLb*XnO#3ns-}Qoz3xdA;pf}w(J=_d1b4&|W!#v-0x#+G1fez2q(Uu3# z*>7~`t)qf)UuApp1Ty+OC{!LB`OjF5L)76vDuu-Y>l z8gkifIq$f$);M1tw+4n)$R9b>wMMqtQBna_H7XtwQ|l(Ux4xi*py@TbN8>sAjn28N(9I6w@R>>5Fe{jz;oI!)U^LPcRV*8-Iqd$Nc7%KP z5J4r+z7&tu365@q0P+$TxWjE%tbX4Ju%FtEJ*?{&^I5+;U$tnX)s8I%{By+5W+u`n*|v>ten3ymVV@ z=R2-x**xxa8MvC9_XJ#+VrR9lVHT>KH&AeIPaW2<#MY`nF!6S81Xkm?3;cp*GX$%+ zTn0jqN?nsg5#!hgf-%TESV1ygm_5gSG(!7iL|$zcN`B2^B}kmc2&Mt||g`1D3Wx+1 ztXT*!U3OVqcKPY;doPc9E*F74^0`%Hi}Dhf%?e^6xaqQ5>P&6v?a^R+h7R|&0^GpX zXb~jtyc7IV#BOp`6T;Z@vG@TsBdZWK~w*LmQWs+uFC z_p0q^?$2%u*vhqSq(+YGCYG9Qp7Yepy%z8NJ;$pg9MNE*G&QvvIYHT!+&()ARI|XSyy6x~4}f_diLac8Vo# z9%ArpW7O_Y*Y1d4wDNf6s;6%cwP)AuuvhBh zU}klBY>I!`&iHG_rCd}y)H;zxIpW?jZKFWrAlkY6n(7o~cjEi@w?tt=Ld%0%pYjWT z46C|p*bjc5|7~QbKi?zg*)j8lC1r3w#NbnYX#`;-=c+e#jwg>qLnS1TzvaBBg44(U z_aU5!|AbCqV@QeHwMlskYjo|$OdrRn9o1vUUhJhdUz6}Hoq0rcVe*L3`@Cb~%Tsmj zS>h{jj~*@i?u)f40xrMW-$DMjOR@>t%c|EIn0{A+S+ z+Kswx0qmhF%9ff?r72R}umwX^AR%-D2qZ|6qJp$71QY}m2+}dqNr2FiZV-?rHS{7N zy(qoIS=rBfe&_rF=X`tk!WT&Hd#zbBv*xV4X^l3W16IG%UNfXVcj@)cKeQiOWPanzcp01%klWx z5eVR&E58{1<-4EMfcxmH-m8dgy7H^3xX))oB77%ot=-qUa%<66>8>bX^P4kw7GxKJ zKE$uizvJw4-z0ub+&TPpbXc~MV;o-dGa_V?)<096y#4h#@bc%mQKbqiAS6`z9*)L1 zI1H69g$@1Mjt@W7l8^M-{(L_Ch)}Tj31_~&&lWc3vJXI^uc;EP zUY*p)4*gEgkhS<7!Lh{@9(DSF==4F9M~++QY8*LNSy04;QzoW|mB0H@jYkL3Df68f z>~6yM5i{n-;5|clje30YBf+=$R%4mq)1@GH`lT=)aJisDBD7gmNO2K+KBRHSvSr(< z76czFOVr#rC>(@%ctZS${I$nFY7AY)NStbRw1(*buD&lji-_0E-LW0u^(9n|R+a(5 zxIktnfPbX{+}YN&3%W^9wz1Qo9Wu;!6aZ5@?#1)AYLAA$w(qyLdx5+(JN(b9?N%r% zh$nzUFL2i*B*S=R({6a|+fQpTe)WN$Xkc2Q;Ev?D8C_~}FtDJ+<@8n23UbA`QFzg~ zddj!}rG4kI+L_7EKN(>GcwNwLuMlezCw?Wp{wzVVsWUJ-Tdc%MX(#M1lwg7Ef4Mbs zw_V<~KuP1}3xK7up$(h2L65`o_NzuE9dN&Rf;}{uV0sDQ*7|$mz7rsf57~9?0^=E5 zOH#TE`YM5*N6(=wmZUuPfM z8NIN(be_-9&$!udzG{kKFon!(U{PNPoB1AR_RN{nDUBreg4(aL^R5(>XMi52jr_+_{taX=U=FO+MwAd;5#8b>Lv+ zHAvtPlME|nunQOmsy)4ImvNBxN$h7I%K|Zlwc68);^s*shaolFA&Y+tilqj>*HUCr zSByvThh$c77}so|@;p&7eD+swW^g%{@tl&Kv%8IbJF3mqW`EFy?6@`B| zA02u>ZMWKsv95P4JXhE!lVsc=qw{jGJ87rY7Ht;bs%9DUq74XA0zEj1_msZJU)|g9 z%p`oMUQgR#6AzgGoeAMmd#)=P5k2%)h5WEJ2a0vGdy}h7`kn2A{DPd+T|y~Tsq_1v z&PYQ|3c`oF=Mb}i5dR-ufG*Cq5>}YxA9U{8n7)tnn)I8XEHC6%k)DHhLr&x5Ji~oX zJl352>yQ!5vXXbRsPEVlJNwFqeFY+s`=!*vWtjNJ377 z#>FcizA{hUt)9HQin5GaW28VJ8+CoQ#ki@MAJEO~xj-l6qbITHYL=R7pe_Hyh66}| znm`6H1{>7i36}NQ1{WRBXW%;=TlTZ+m$#G8o2@JOh zaBjvl?Awj0DXzYMvLPTpe)c*@MJ&^w@oJ5Mc-lBx;PPhs>*S$42>JU5V@C)5PiDR| zdOh8NVCD7?Tg9~j8qSup!YH_DbNQgvR`GKC?y6qj_EFNjw zzT9<`eqS%tWY!OG1?U8_R&tIvk|;^fM?F?rsq%&G+X;7)2#f&;4Awx&&+x<9aE3Ih zr5(&b30&hQYAlX(eR10)=g+wr3@{rJ_`0*w`C)qXr$a&oytTr_2}y;W#UA6RuAXZ5 zpFh^$p<;INEy?EE;(+=^IgJB?US*`!TR3k%VOT3!Fg~HZ% z(-tHhO5PK11R6dHj2?`P9gG-&ynkhX{$=&dOP}Q#A5m5D^B&(+eD)iC<<0q$#13}z z05%13f5xHO@aJCt$X;f>_MuS*oY|TeU{D0m|3bFEXpqcywRoH^VA*}Sf^ij22En>; z2n4^N>zmY|s+CYDuYJDoaISLD;{a4a)>M?%>y)-nD82jOIdvbi;(t+_L{uEezess@y{-}O43*K*A`vBANnTm z_LMD*ZLMe2EW79Jg^q29mO1Hoc12n$vcJ5&H5>$vO1~ct0o(n*t^L5i66G)_DdX#pWbhIOy-;)fd=r0zn zuJ6Uy?8LA1$7pp|1CTKTR0LMkUM&v34v`4B;3tEWcQ`{KG7|9Ov$dyC`#$D-x#mZk z$y$Ufs^j%V-ZEk@*u{)JpX@(~Q<8edt8l5nFuH4HK9axwO_Hjtq*_CW&+pSmhB0dU z1N)ufK5m%v*Tm+2I@W>OCo)1|W#x@g8hP=)Hj~II-vGjFo2}`PFP&-4w?NA2&YcRx z{t4cJ5nRiDazVkeT?&xsHg4@Z{EHpz-;RxyQav^wDM57OyY1MT67vM28m0P*pk$|M*yf z{w7H=aw0f37HWmIUn=Apm~OCrjeDSCG+#Je@IqGe)29iQ6zOiS6lC|*MGe~^WC^KB zc=@tN`;WsDPA!{gDI=W?n1TE?`3MSV$~;&~4AvsZ?L_k5=B45TeCmT+5X z1x{*NQRa-&co98yln&2Vrg%M{%#9+wqWVtks9l;4tKRt>Wxi6V;c%juQ)n*jwv5<) z^ZJkQg~9@H5hvNRZB0gUBO$*#g5=j3wKGtH6F6pQd(e|GWne!IR7Jk$Dlxnp-+tSFABPlOwm3#j3jXJj-GlUOq}~Qe zN=jIGguCx1Z}C3?hBPTe9iG!YhQFR~U^=+8P_MN!Vv)3RSR`Me8$rx5>mPczW$sh2 zEUR+nPIQ~5J0kqUU`BU>-8#nJ5ypuqfl8VYneuj14W=XlwZO%y(^^N>fzdQZmFk-} zWt3$qJzUq;prpe6Y?8Nb)~L}*wXmyiI*m2+zRRz*LLiBZ1$z?t7H1Mbzj6u28WxgP z&R++&#o+SKAtF!OZghX17)GTj2pL$xhGsJIaM*_3xxY;Y#?oxLg3muo)hAes)JlBP zY3PcX<0bLrLs?ENq?1rx7={puwNbA09M(e3ALPUT76cPqwTEYbv>Lx@SiB7^nYsBviF(1_q;bd_8;V2kTZmw zex84+@N=I=TEVxh2rM57tG)G5q$K;CK*F-J##`x}a~ko@S2-`0Ps<}(T~CcluKK5R zS?*{UTf%}#D7)622=(D%R#U#vx>EuKgL?1GQn7S{CaGC?TM;o2!|8N;(&7i^SZxEq zlr3((yMgwVO{R(!>u^V1zTgVv%p zT?Z@IowJfVR;*7v?Ed{drn)Zs4a)9JjSi@s;QO#7&X}9wv zhuh#B_?{q)8cHA)QB3UtSRVCDQNH*_;HRtG`#aC_hx~Qt$8Yni|pXlz2$v4}B zpU8nUa?=v+Fsh6E5Fh{wA`azL$Etn&h)U*EKOng5zk@=qt^s2U=+?fw1d{tofHY1b zX>z?-zC>43_bXU}GACWK4xXl5B}t)pS6v~CG(bIiP=epG7oQ38>Wz4yarMzQs^^p+ z@g+2)MuXB`PVq-RH|)ZE=+SqfUo@4zVumU)*T_?TzC@?Zp}KT^=sizHKv2GsZUB1p z?gGu7vwZ#?_u}D zme1;E^&ekGVm&GHLHJQeK2vo>yyJtsRS_-Q&Nzja-h%~|8_S%avEF{)kT2a}MFkFV zZe@fB-X!kDt7jMRHh#p@nx-7RQbwG~wbCG)p{~LZ@tJCmuqL=iV`!nD&AlvbWSv_D zsU6!7Ps5dk-w(7*a07MNAaQdhJ1s>;k(+%!pQV-B}gfJo(}EN9^e&U3U|#ZrKGa zE@X!rCWn*@(q&D)zmgk)unV!@OBFZ$qGcpjlHF@Ol@syQt>>XTUBX5@Dv*UW-R75s zi@0!vnY0odC~YppY7n6Dhpj*#Lv zWT`M2u3FL7+$tG8HJf$wHSUGo{$|ekW%9J5)@Ri0byI@NIEwo&tefm&B5m$T-|@0N z&;5rV33Pmj3rjA9^Rsj{;6vu-*K9Fqukms-p7ug$ySMqYuFB4-_x^NVgfXd1slJ(F zqVmRYMMyUB>=w_-$yw@)AG%(rrC=^LzOo_->Gw%Gjxd|Q4M-+0U`9bKfhW7jn|%T- z3T6r(AVCAXD+^~E8_bAEWZ*wR;ERgK1Bkkmhavf@h`@(}-{4_auS#n&WdwAKBL?-C zso@0I%&!#8)oZLiOi`a=&FW@XMTj7&cxO!Lc;o`ZccQGuQ5eB0{BUzCAvDzh!u$?w z4N1eumWCIl#mPL#Sa=3p;gULse*P82`s;ktQXf}mTY3^t+b6E*x$Y3ZLjA@W0qbw% z*uZdiiA&BQ2Rgyf(CUF`3$6=kJL;$BufY|GAW z3o+#*K7EB8Rhz}6-7JS5K=WwIb96h8ymfUjzDoQoAS7i*d`DagG~_^Mvg#10YqO&* zwSucSttjFtHxN#!2L%8Pwjtop&|~nZyMNf&e+%T&qj2cO>mYLY+nDa7NLC{_2Lpw2 zXdA(xn2)YFqmx(8390?f_-OWs+__vJy+#qYzl{Y8b4B$^U^+XZh|QX?>S6O#R>K30 zi91k`qU#o$1m&-4p6IZA=51+js(T0Z9g(BF7oVH|6F(RJeHOkhS_x%JH^3ECzR8DJ zc!LjT&)mfNyL(Gg#F2@s;@MWWv4VXl_pcZpB_#l!4@FQOD$eEnG$gU&9*-2e)4#k{ z4tso{66=|uUupc7Oryk<@svWlBgh>^InlHD2I?0to#2Z@t?SuqowD3GiZ zNW8-DmK=+nO4)3f@m|uiBp}bAu0HC&Xhp#Ng|^cuShbmQpg)(E!hb8_Ov29z)#i&P zj#%v4WL0z6QU+rVqg=@y54V8dCqzs5*W1u(WN|f5x&YZ_81g@7?j;>nrB%a>kYW}P zuGb}?R)&ozBt~-5yl#ttK35U zLbdax=kq|=`GMc$((IDvK!-CU0>Z`vNRkauEf@-$#7lmWA<+&kJf1$4Y$c~fP%QT+ zHD7tYHiIU;thEqSGa%vmEikH{Q@0_tfsC^=yUWbW435pt$b>U3?7K?wCOk`+c?pRY zBp8ywCoR24C&J;Fu)`HCzWdFr1RRL3cY5^oYI~H*p$fprg3}FF!2$w8RP8Bh0SEfq ziI1!FvnsQkkJ#g^DC$)xktS#x7McJewoFwBrfmEE*yso3r$nEbB<+Vhl5dhdi_T8H#i55L;119CrqQJ5ZZNc5RfO#^>OUao zy$#)Hrz&G_V`E@eggXFz7H2`*FQGnn2OhFnl?aDjE0%4!HF_79e7L|A;hyHSr!-&qM|B3uCX&Jr_u3(inPhYzV>Blhc2;zx#r?}X(wa-_~yvx>H z=5B}-V9Vp~&$vPEH%_6!-^{}?RtjToiEZ%rZetzrqMvBwRA*72o59p6oZ3sqA~W=Z zxY|uP(Cnub3N`YTHKfsk=CV|?cl3<*O2hC7^4HINln9Se6I_cCvSqcJ%R%%r)Rmc? z890|$VE$9eYv3ALvU~$!)57)814~Lrgf(Fli_Z712?pa-k0tKbkRraT+1jz+k~cG( zfBp|x5JV-k3jqle1&2;1+XI|woqt0DbP}op48=9jkjO%Bt4traWS0LqOPw`N@qOIA zZ(16FCfzJG5-*2hd%eH_N=Vo(#s^S81Bc@)gwFhmA?d$^;gM*}vl!Ia`Ym@8nTuh! z7FLgz4oRr2r*bKg-yPB<`y$MaIj@7@M9puD+l3b?^^{&g^jsqEUTGm{uxR(qpDBbo zpi8-%g%FgeTvBzn3=5xGOKT3TfGO{l$}tERgR;ls)Z1gwMh?Ke=#g!JUPGc|l1iQh z8Afi>&n>*&@GtHYm_?Gwt|nD|fd0P|=&Icdwoa>_{TO12Jq4pLV?0aKQ=5oebu1UnzwPRX@8T|Z6Fx8lPg+n6_IO0eD3br$5eR?!DmoR z7$L74or7?JEaQjZrFK-z{`R7Z_-<3G4nxl-9#metZR8e!#wKlTTdtK!vWeZS zeFtJtVAFOjv1dj(0JvBLMJ=X&5aiMA4xqXPhWjw?qOzA<)7Zrc^eZO{Ko6!E|6@QP zsR2C`rB4Q2K=);`OS8~Fp|~_JISZC#ax#vE6&wb){i2!ypt)tl>U-TXE&N zusEb}ltwT0vQxa(noo*#IBs7b!Xo|$oC@jf>^l3P{8E}hBrd6Ed!3%qpMBIpYbbU} zJd#4rnyQil?Ki-sWv|!RNdZhhrM-Is=nmjPYo?-&CzPtj1IV`DdF@os&ftFq4&$|P zkG-~B-tMmKo}hq<_O)^`Keeh)=M^ShL={vv<#4tq@*k`(I)o*Jcr1acC1}~K1Ke>X z&*6ObTp;R&1065_po%C7o)QOpE1TCNdX!GrmILx0rP$h|k9AQ`?rY^Po3V&wD}FIU zi{yJWOy;Zp23W(BnVa}BvDM8bfyP82)5Mf0llKqZ=l#qzZFbDbmh1j=$)_xqz%KND zyXA`LZ7``jWdLuT?^Yr+COq_mG6UY++BtUO=(0B8I5Ws8*z+$j7ANAisurti{@HFk zj*$e4DH!*ZH@VS;o@059)v9?PJu=K3WBX$N8Uj7L0R9BhU7XS5Q)0e5rvbf(+6>4K ztk2!slNA2>V!UnlV>W-nvt77^a=xQS_!q=p`6O%YygJ&~Libi|8k*3D$CR~ZFS&hF z>UJImjeZJwcU~|UH(=->gB1Pt-FLYK3ImE^pjlw9@}=H~!*tMZ1{CJ&^8Z9emf?#Z z=l$laY(C^UhK`3YS)^!azALg}Jy$p=hvOYL;sQrO9!RYxZ~p)+tks&m=SRyt08C)u zZJ>Kcp~39>UVkIdsvR^x+AwESt}uwF1`OQk`O8>7tMww!&WH|t;){V}GBRLasq4b2 zMx4=tQy7B{y$&KYDPcG_6tvFBHt2YpEdl=6DpOEZudQ0bf`7l?KH zM@yY$M!k()y_a3bAPDbIi81&~&iPN7Knb=Gp#L0X*f>+ zOfo<47;8a`C387Q{iXj_JI`Q zrO1yAUb0Og!jjT zw)F=*%C+w=0U5yqG&FjR13`HpAGXTiw(LZJwDU#bc0Po=QFHn`nqeq6<&1Qvs#}gsEzDT3SRa7J)B% z0Z4?qfZVp#4NyA9MGnKaem2QIq3uA^? zxc8zJgL4Ja&Y`bg;ru3GPTKTMU=wA_L<2KG8qbYER!xaGy*Xx>tnh~MbREWnhlT?o zyFz|{0Wis;gY)#MUe@Lu3rB&Z7nbda-9+ERoufkl5SLahIRL^aogqX3&MJdP%fKvk zg#tYpT$tX{YH1r|Kw*2)lfu1023$`@qvUF2vKJuaRf40m-RQgDxLxAC+nwwKc%h9A zE1m2Xrl5J2v3-Eum$X$mvoR1E^GxB#vGZqYht(hT-0~NJ^xj|H?W<9wq8ZdSu>O!I znEL)q94$*dH)`6qz8;CqX-XbwTd~r3-rafdRG`Uj!1^!F2H()>+TgIOv1YPD@i2sa z95>RJfpJ0P>SN{*<~z*|&HiA|av5;E>P4C+12BrzA~ekvoFDAG9$zpuIp)(K`{9sX z?CKGJ6mNG>bgCguI_z_>>u9K_5WjWWo8R*_;wYPDCdfJ%3R8QktP*Y@K(ly&zH zKLm@xjskYipqJnOt_74l`KWOn064tM)ODz*P~hKfAsHH}4HJsFpWo+rsUX(kAW_*Ia@Oj8q%dE)e(rYf+# z>5Z~vGH;IeafKzIMEEI0H2Bv?5TA2jT6_Kc_P&hksqKU)GS5OH?fh5D1@eAYyvW?) zFhw5YRQdZ0?VYcmM;2JF>kjTHTut$W!E#Yrj@VZ!6(nPTI;H^Jv7FcTZy*&Jes{TS zznvP#(C1`0&LdY)+ypNv4-`O;Q{RP!)4X!Y%i%3WkAITR8~SYk(O3*JG+)19zr|5Z zXfxiXW+b{4R-?y-Khxsg{0$IQ0SR^#EXw1_ZS+e|3Q#5lyi#`n7BC#J&h7Dl7k42o z=wyifC8f~GtWI;p?^RYzZ!gnL5>7W7~ zjGsFg9PI~_+A|pL&FjJWRCEa==OfLCy)2M>rOW3#I@KI-cH`@vZsWIwDVir`AUDB> zqENff;y3Ls$5mg}sn(qF4RZ`g?G2J*g1FHC3WyB(*T7T8p92lU$S+kFB)+}{H!#(oXJIkICg{E zyR}*k1_D{|l%rOgV&x0=>V$Zs60ocq*a!V0e42cH1;M9Vy#|p#4Pvltu`=1Ay zZvJX*3UV)HG1ArZZfw|fVQoJcyi_g?(e=l_$j?Gni~`*?lNK?dQvWCbH|o;M%MEcB z2B;BCQL~$7jw_yfd){(G53-;MK3jFLdNVpAL(2U2WQa@mR3uP=M&|7Z7OE|H7#w&C z)h8XpFm_CuzIw6@%Sl;&6fn2lvcCRGz5Y)M!!|(^8*Wa8`p}1HUM{;=q*=GP;y-fP zf-o%up@@RjZjQgx{P9RUpyW}&h&^cSRs#Q!)fi-vx8m=B{;kLDcL>(kRU=~AqkCaa zcW&Mfrx(=SxEK1T@FGq3B08eaB9zr#sa^P`7f?+O(_5Ig$MFVO2!0*e?FC148>I!Pub!l8g4dhEw^H#;8DlYI^Q4R=CWLmiZ=(`C(ROTf z?FPPpRCJSFonNmnY&q2P`ivQ}*SoiAHe*f-`Qh-Y@0;t=&71-9u|`I?@*KBaFfviY z_t2zkLQyuzaOYev+s>!+;)2CGYF5JPvRuwcw|(v^@)4n>&P5OmEuzrD)YUN}lef)Z zsM%O70U1afyZSsa(K{76YRhWH7x{^-9sz@6(J`#Hgn@c=_4)})Xl}|=p%rA@&3JY7 zNZxKKxX`drF0uVIQD0HW24IVE&|;!_nuj|2XeHC-;7ck`ta`=}f(`Gk`!_Rn6O-gJXN|5}= zG6t4CDVzNs{XRbcVxLmHE{%hu@!rmryhj(<)zqeXGID;yTQ&2bHSaYCKOCMReICZ? zzDek9$zw%?uP#7qIYD8D62AMZH1f6WI;sW$U!dtLMw`#$BAV|L=axD9gWJnBck4Jq ziv$7ixbLM9MxK}GElTM$3sTlX3nu^6e3r7luX2XPUn21)gc6y`Q$BBJvaNp>tCwWe z7M!c>(Qk3abPhH85e$Fkc0!3NVz>vn;WdKNq1O|ZRo-7mL3+Vx1B+`?sTx{(hQ*pw zisi-tW{{5ypq;(veFG~2VBdlMwR0^Ty@EfpnFS~S3=q$B9z7tU`9lu-d;&LwSzRO-x|8vju7_|I1p`f`6}iYdDViC@;_>PhJUf z#I+l@QE_888*g4l~TgV#fMc>kOXQx-f2 zXC|Rt=*=0k{3A?b$Rq_~abqyNk+_I+slZmb`03}ALnCSA z3pS|+r2glwo~U{8#3x>>c1xG3Wy!cTNhraYCePoLuZGXYl`wA8d-`eMT#@?m@ zQ+^O9)@4w(Xz}<4R**SeFi-9fos5>GJdC?!-(zSN41-Fb^Et^$pa)W%OqEQl!iko+ zL_oxOM<#!80)lLNj6uQzo1ob**B0YvBhWbHpz+iF`*~U3OqK!VP&?&6FU^7-tx8fX*CuSq@pYdMsKAZ zA8HgRi?l+u^p2)0633ReIFD0V2QS4=On8P2zOGX*)>mtJVDP_m`Rbq~0Do&C(V2-70r(ulGg=qTNPih!LMe%Ep7#Nl>vgKm5 zY}~XEN>DRUIyYXO9A09L>857i{=;hc(VYf${yxD3DLfJw{avrYOR9W{N@mX!1a9Af zV5$iVuey5AX$W<1+vqvMl)L@Dykj$i9Z$#k(!Yna0m>XtPIjsRve@R%9&g-FZQ@_A zNt+ZlpZZ`f7KpGq$RV;hYgYI{Q-s!i4E^FOtZp#MQOO}%gwhr(-9}xl0;HqKFcqvi zW#Y160|vWFe#V)8(vTEwO?W7L!alP^{pAM?pF%W*>UH4DovDLHmG_CO^h%ZDF$$U~ z%9YtRAkI*uaj^WC8$Xy;boIO2Sv^G^5a^L>34oKX;*4 zMxhIX(f{r@Sm6Lz*=fJwQUF^=Zs_ZIN*fAgWf;)E_%E?tXWlu*y1Vjs6RZ|`2{`?d zqMQBdMj+6+XJ|o-O>i0*H2Z`81Z-%du4cvADcnfo0)a-sjDS)z07mUHH}E@`lA{KD zDjfK`k7KIuq!1MiH5H1W*BaM2I#G7QmCeRFgC*$(1Td5+QB&i(etlayu`dnmKS}4h zHiWDN-c&SVRr4zSPfC+TEw7kO_E%kp6K8D!b$vr%saKcMamk8c-DRv70UNBN0A(y4 znO2a#Ku&Z>={vimkOS@IHJ!TDY~5hWd*ECHw4K5Fzx_)fAds}aA;1a2xPm<6u~4gl zboG#lP7k$myl=2s6BCIXh@2at-J>lk|5JF2vO>Yo!J@|XDo7D6L(AhtGaE1@=_cru zIO6SHp=71jU6SWc;p7QwOl%E8pIc}554qxDcz|KI1|5KXfND1vQ>G9b?WDEGTnyZ) zO$~HoB)(I4eHQ9>hvCT#xrH9z#}MBMiqYWbcs1n&>atCuz;nKhOrbP3bEuK7Ae?TQ zJb*m%bXv14@3-R^F7I2>W`?M&MkLSEl4p8EJvBN?kj+f% znZ<6MI{Sx%K;%=DfhQ_?H@mD#y+L234+^13g2T-Dz_@yspZHGzQ9i>YK>FGP%)D%D zEH?B+-V^)CzlE;^UA9rMKIpTgtR$w2lRZ~VJ{iI3H(TNIM+3Hh_!OP$f3QnHlce+D z8&OxL(RplUkC$GCU8QJyOMBZ5I&>)t7GRIm_xq1i=RxE|2;;c#_uIp_C{y%jkQ z`lQ~8FU4a;i1wjsC(%Kd!>W5T=obkjo`&qV2WhGMVSw<@K$LvEvYq*+H&fUuu zn~?wM5g^gm$Cu+HJAFt|NJ1FapQPi760L!s<8BRXVtaPuEWtck&IgH{uTg|~LM%=) zT;@Hs+tOsii11!SppF(I%vjn6Q+%kwfj+b2OxumZkZvQ;e?5OIE%FU2f<>PZS;G)d z^Psuy?ZkeA6>f{wFmmkr&4Dr*mE+N`c}bkbuUq8*&c0IxFQud#@C0nGXG=fUh~LcZ zO1D+#Ydwy+2uzFEC)tiS%J=d3GJlWO&_pdbJECG72vdHnPVqWLQ3fcS=|^!XfW*aF z6I^0ocn`E)cv0L!$-Ed8vXBnAV6SZ=?y$M38t_P+=b~tO!Z^Fh;zGIV!3SDfAAv3Z z9*42s%s-A{0pBIvjWLlX7PU{CaX+{n%S?_2e)0{y1%OW6+U)7~wc8}nw+!?7nxMoP zdbBOU(=3@3%J*Ul`=%rY~!c93!?{3Y2)p7y^ zkn%QleEuH=TbjD3&p{$(COsz+~Q0VPh=+ZHE>FE=&YEHj=My_@aBs{Yn0U!K4qu+F}w8=4s-)Rt%;d_ zNRlka3s*wdG7KhJU_l7hJ_mTsCFNrjC1C5pNKY+q`m;N1^1t)T;KHMxit)!O!De$YHbDfK4r&v56mF&(7J;&fM{3(vvk>LUJ2qS5>R*^H!@2Z6mj^v8 zi^D#x-c)`I;}5qPYUUPM@)b<*(ex>BOgL>_pse>!_U5oF7)SXa^rDuU2~7PBDqY_R z1_cWei0V!@!%rERf|M@qqn5GOGk%;#UiNb?r)bW-*pn7Hj8maolQn#oJ5J%9c))hmxk96XSX|5`NE}3$`(C|cv!wr;18+m$;i%Rh0qOzSdFD;(1uO) z)ZOe33pDVu-PA7~_CGDoFfnsK>AeJ3p92lZg{c`G+*X89K==qW>`Z?el$If}AdL|ZG9pEa`dJOr zluq%Al&Wo!&pyIwOm)E5TXM=jksD}a>O)VI2RBQIr>$-m!jH~K>D_^m3?80v(oJAS zxY=O3m!W_~Bkvw}vjIwcMG?Ov{h;~7wbnz?wsvD9Z;AvRzUTn4I1pwpC`ywgQnJg6 zqAiKLSqH-J1E!N$!*U}^?o4aYjp^6ydiFc{fL$@lTBy#KGFwFlr0V}ejw0Vc{FF9K zdrUVRS8+c#hIitNZ_b%Agw*L*O{?01h9kO(5-e^ZM_H{26$3LR_<&U>t>cT-|D{Gz z)xn}HIOQ`VqCzs70XORZW<^N}7)~`)O5BHhvhzgVTO4E7t;@UY@VI^hHFSxrY+Kmd zww4|^3zuRuo6;EUO55p1Kzd`rCWgA5=QX)e2dxUc%)g`40l3f|7pw^1Ur{b_<_bug znKczEck?cY^kIdSbZk#UPDt;a4h)26^fD2zR3s8TwA6D~rH8l z`&ydAM{~U)@alplAHSW?6f;7Z((E|8KBe%Nk)z9n_`+6=RI3&r#M ztMj$N@m3|P?^<6}@-AKV!?ffybIo2eZDW^-c}{d&!sBud=ZNx578@XXqdS8E6DS9F z7S)2L@dm)d1Q-89aO;VnAPbk}5dpnxu|-EG|42Lmd2%7Aaeuihr(xYz)e&X{9`=?E zwJax^M!r#Xy#E5@amfmi+3{i^d$v~xe3?(;QsDAx5J4+_c{B5FDKtdY0GrlL-rR)zTQoLWcw00LoM*Q)t%Jjg(DjjKX-OAof&vW( zdwNYlJ9P1@4n&1ot~Ob6FCz*==-NkP!-nB*=h>O3z|NfKVrI%Pn1K5gGm3ZaYk=)^ zAW7+vN}%SfO?;$1jGIvS336J6CMXLaDFGIlLKpr;M0(lre(H1#FFE1QP}V8mpALAk z^iMR&*A3K|=vc^MCkG5AX7wp}WV^C>?Ii5`#0%p7q6D z%}EeyCu+e<<2=mKpbv_|87Cw2>nl4^RnFvbv$}b3e7-P3Qv73U4389x0J8!h&l&wo zcfxrTY6)<6H|K2Ttq@<354|AlAH#wNQq3RfEvTs&;bg#i9eg5-`~J&WJZ{m(NWmTq z{eeP6U3D4R436^XY zo(cA?4D6dquRtC20MG)TgDOwPW$%n9V&}LFNBdqzK0ZbXC2Cr6DsjZgzUwiF2Yv>) zf8}t%1#)LBuU=>*mT;i7&yfr$jl71W=P{@!V|kWj2PGi2wd;G2H!=KshxpXQ3lF<< zb>$zVpC@hF%K%-xe@(=ZwVA5?3*`5#R)lC?L@S4lR>e|+ynTJm0@sT(RId>3ggpb9 zwlQ}NJDBGbV4nQvd#{4;^)ylqgzXY737&6ZgOL>KqByyTNZkH3_H^KH2)d*Es_7nh zWyHrgd%=lUO07V$6=M#3qQ0fC3Ttue zFkHCeH+)3yu#VY91ISgv?jH&`cj9Mdvf=rX<@Sl2 z6KMtz72xmL14{nL9=q{H_bn{?1Mobte|g7@ual(QC>82p+zy5Xq4XGA$FJ%uBfXrz zRPRFW5GaQRvK(hl0=s)J8hEcLXvVZ4NKxqqZRh(+u9QEs>)qT-V7=`DVGkRaq%$=x zhSfd)b#rg{`Y~6c2^~FGeAUmLftW`>W)^4_5zlolPc%}Ja<86T1qV>XpVBYwL19kN8mYR-o|dAnPApaU7iMMLHCUy zlvn9za3>X?{%rtJ;{aQBsvFQgTS61MjPi2K*B={0+~SF>IsG#ak}-_WxCov=CRMQ> zjfk=i-Lo@H3K+FL?jq`!(w>0rz2IGQ{eHdU z0>OvZ5sH40u;btpl9p{76bgSO99R! z`0)q$C-`O20lWkFVUA;rtbzFb1?s`zx40en2JmwV+}gpf+K>PJ=l?F)|34!UfP1}w zK>na2|Ncw=zt+Y$c>lGw|9{r@Ukm%Mh5bJWdV@#jA&{e^4X-m2U_DPUeAfv`S^mq| UUpFp|GgcURU;FP|Ws6t;3y>BA{Qv*} diff --git a/tests/e2e/passkeys/admin/security.screenshot.ts b/tests/e2e/passkeys/admin/security.screenshot.ts index aec19d629..1ebb9ec9d 100644 --- a/tests/e2e/passkeys/admin/security.screenshot.ts +++ b/tests/e2e/passkeys/admin/security.screenshot.ts @@ -204,9 +204,9 @@ test.describe('screenshots', () => { const security = await openSecurity(page) await security.getByRole('button', { name: 'Turn Off' }).click() const dialog = page.getByRole('dialog', { - name: 'Turn Off Two-Factor Authentication' + name: 'Do you really want to turn off two-factor authentication?' }) - await expect(dialog.getByLabel('Authentication Code')).toBeFocused() + await expect(dialog.getByRole('button', { name: 'Turn Off' })).toBeVisible() await expectScreenshot(page, 'totp-turn-off.png') }) }) diff --git a/tests/e2e/passkeys/admin/totp.spec.ts b/tests/e2e/passkeys/admin/totp.spec.ts index 220084645..4892f444b 100644 --- a/tests/e2e/passkeys/admin/totp.spec.ts +++ b/tests/e2e/passkeys/admin/totp.spec.ts @@ -71,13 +71,12 @@ test.describe('TOTP', () => { const [credential] = await Credential.query().where('type', 'totp') expect(credential.lastUsedAt).toBeInstanceOf(Date) - // Turn off TOTP with a backup code: + // Turn off TOTP, with the access confirmed by the login: const turnOff = await openSecurity(page) await turnOff.getByRole('button', { name: 'Turn Off' }).click() const confirm = page.getByRole('dialog', { - name: 'Turn Off Two-Factor Authentication' + name: 'Do you really want to turn off two-factor authentication?' }) - await confirm.getByLabel('Authentication Code').fill(backupCodes[0]) await confirm.getByRole('button', { name: 'Turn Off' }).click() await expect(turnOff).toContainText('Not set up') expect(await Credential.query()).toEqual([]) diff --git a/tests/e2e/screenshots/admin/__screenshots__/account-menu.png b/tests/e2e/screenshots/admin/__screenshots__/account-menu.png index 87c7277806a7a542f928bdd73bea68ff84ed947b..22903d9339118187293aa7aaa0db24ba9b22180e 100644 GIT binary patch literal 56992 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

Kl+TyuQ4IoxT_&(Bp#7 zC}i{B>NeH?xbtgruF)xL+`1Ky!`%6=ZBv55n$*&PSU_5fC9qDb_`$mkb6R; z@fWN(cY(#VB&sU~P7XO3GE1s&pPFs6rxXki4ed)8c0;!8Rjk1LM1_Uv#k{KymYm>N z&w*H$jg5^}!mC9DZgY3{YR)VsJWZ-{7LW9Tv%h*J5^&wrByo;H;IH;73kwSoOXB~% z$gdMyQoaSMnW-tj49CQ`qlU<;_K}y%gQql3vO{KW%YR|eH+WPOc|*M7S9SF@WI8$T zP?YEpUA~-WS3p{-fFcKZ%+;^!*omTAYPiNZ`kISh+%o7LjTeqh%+}$O|6bd0pQokO z9K;aa^RTwb=h34_N-yH#3%}C|A{*h@a=vj-?WNm_#1+we5dl~tAyS&7(qs$t3Y^3C909ix zJ0DpPY_qBOJd4l7gcei!ahsJp+^hftoS4BBP5M-eA-;~z};{49{s|%hc(ODbp z(CGA5BEr%i@w)79^Tcgg?hBAi7a_*LuF0^BKBq~~{e}9=F+Bv4(WoAJ5c?{dhTm)X ze42a3jCKEN8M7G7E@&V3?W>`vfK@)MjLs5rt~bW9f>7x{%Q8rAFiq2_G3(l$ax2yU za5)WLAznjz2(wsDhyC#*TFlT*JKv}yJ)TI@=1yNS9@o1z-xau&&zyoa@!6&Ex$U5k z2PhsKL@l3aQ6R6)+gMUmk-Bqw!qd_FQo_#^g2J22+v#5BXjv84nVk#<(>o~@9s^#1 z#~|=VP0rbokwbIUW|;E>b_?_B>S`i#2LGM>q#%~9!J#2h&;4X~UrwE>x5TYM`6Pc? zv;03Bb}`}(LvwPT6?bYIgYzeM^kz9YjnvfC3=OdzM2t->EfSaOaT7t6m6dfkW886) zs@PFC9CyZTgPb*naOS@|KTiTb$6t;?R=g14B&Z1(j4iaPZ4gy|l>h-=D*u3ulQSwZ z(#~PdVtLY&jvI&*IhKa*KVHl@`!GTs^woDl$Hbpt#Elk* zQXnfX<-O!WwY5#U>5SmTDlXT#s;X(C^GaXWU+?~ky;?os#Kg!uHAPc7`G|gw4>VBZ zIUElzZJ=aKBt~yR&OP@738r&#tv#=;PJhXxD4$+(_-~%3XD9M1tJoQ&TUU5+^UHt(Lc@=3b_^La5?ec(D*aBY;SKjkl;22;Xb&_t$Y)^_5PoX;L~vo3Xa;?U`ZHD z7d~m;%EV;hFSG7Zy!bGlTvt+WH_N40_aUOi7mehUHFRn{>b1%Od!m=U4O|gqVfp08_f1G1i{7?t8sIs`S#cpJ4;m3I;Q=v-t{ky?U7q5(TkZcEbK~|qRNbfcK^f{f5oLfu`BK4^7lK+9qshl zPY*T?^n}y$`d8tnWTBxFjTiaG4F3GbqZ5^bELIj4NtgN`D5KJhT_JJbM6od6-B7YjIs zK`?rdvRnl094qH7bngVoJ!4UcE#ScnrQ>Vu#9Z^R7klSjy$DI2iZhQ(!E@uMcS-2Z zOEDqk%NXTcL%d(0Tv3qE)@o4^`8hH9a+Wr3N4umUa>l`jb9dV53eTm?Ffnx9WBkkr zv#`@x%x?Dnl#yp{rJ^+u%Q2Qre}sn3C^P!?kf`URF_~y^hxp?KdKk1fcMH9^^!6qz*oD|rL$$a%*xzGt=_BM;cA4Z zgY1<`X#x(*EgpNbl}Us72{CknqYLgsCEArnWMpIrFxs%ZCz;2ro9Yq>&oCG)zg0f9 zsaux8`_HW%MOXvN!-o$`waO=K>n8}e*4ICvDZXB@!3P^mf@UQ#k4eieg=@^iFupBRcG%oO|~Y}AUw zo>jP^&^vxBuRJS^Bof2Zo`laeTW_|V4JNluXg%fTyk5<%^D@@wh^1E9Wv(cn$*5<8 zEi(p)DD16lvefLoIrQh=4VS*sWaO8PKCaEDK`O8jMuA;}=FSI8ZSW%Ij}fi7lA)q> zVby55CjBO%S2EIP9n$>#p))v_>jlZd1R`Qkd-;yuN^sTAZN%5Pd2%G`xz$@^fAHG? zV{?fbOc;-Q6&TW7*F{v{WvW7`vFfcfoNmd(CFoH|4+|^tASjjdu{`8xWvTV z%Lae&zZ~v5hm~S80Vh$;wENFtXJY>AA2P1}`=a9&hZ;z$hULx~Y$!L)A5o(`t7Z&x|qVf z(rQ$`ETCbCGIn zu9xnJ0GVG&hI-g0Kd0kgzSd% z5IAY*eV(1QJIyOhBlhCNd>(T-C=zf}p{#_Uv1g(rL(%A?r{x(8!qO{6Ym0bFOxyDF z^10^xoZX9X3FfS@@XYn;Eg4gV{TkbY=Shaf`^6<9Tp^MVsjxxD2mw+3CDS`Lh*Hm1 zsC`P?VbI>TysG$;6?k_PL;XVhuZN+RaYShd=CU3V4eX1(uokzAp#e2>HrR7U7%dbR z9kv70f@f+nb*B#)i=`)D%go!UMPV@eQ%PjsRg8)bCdZi`u_~NR4B2p=L5)QZisc0; z0@w^gTAG@^`&o@0dFc%)i4uxTS>>7U`-_UOqp}^+1j-S0Hj4!g7@8(}kfWjYa`4BT zZPbA&H~C&b-Vv?9nuAEqMtu>2+X&r*j3rRU$|*-*P9oH(3A2_RDzlc%J}!Wq*ftjG z?fiU;u1yZX@Z)V0=jTvQL|$re^Fd&u6&3YD-pqVPYx6OCWh?LWDv+c+6|kS1Dc9Gx z$`J9`^;fKi?in{aF6W6AdC|cyN1VD@bb!PtDQOmg$DzM`0l|#$!*9mIhFE(X0s;a~ zw#MMyrwZZD>ARQ_xc!Vm@IIM?Zeoj3#c>RItYuRY z46^`T6h-Rmob~vjko)308&r0)ZD+^Np`67w`i?3w^$n`NIfpO{R6?_=>XqE4s9T?) zgKq0SvmL7_l)}Q;zL-{0@Uol=)qS7ak6Y7;ezm0dbKjL(R)1dOif*;D|5<1;BUP4l z<2l(YK1~OC!!k)1{F0Z}b7l0aYi}?n*zuQygsN;bwf;n&!;eQjbuna7={#ngm6@uk ze(*@ln&J$0EhcxP|43%1V)ySSC)k;8t;YYw0{FdJ?fODL=m}_Y8D7MA*dZcX@ZDDy zD8pKwo{D>lPMrcMc1)Biu2-k_VERE86HeV2p&`k49F@M5E*S%RLhp48)bMwgfCJ;_ z4u_~ApEmhL*1Kr>5${NjPkH}pRT*u0?=3X&Bp!@^h}Cqpdr>^qh8IV#c%j4()LBLS zSbM6uagQh@_{}?msw|)*oi=l@z(uWH!Dkn3x8C_~LMn&qbkL|rxs+B>Mz}ZfHwY)fsqk$V~{&g=tGe$rDxfVjb5@nx=#J%^dirx8YEh` zGW~qdb}cy3r>Av~$({-wjzMs>YCfBh+lx-{W#7K87>O(E&EXUq8=DyM^W-Lfy>hLE z3+Ao50qAXhe*TJziWwW5p-PjFKA^W-`5(P)JTenT?rBL^OU!a?XghM&(toq8G)Q_} zc;@PPJ`p5qJC$&mU+?fZ3DU(jC1skO$PoNW6{7k{SIe(!G3?aE<$yVwQDnt^JCeJE zP~xGm&1rjEg~VF^pIoP%#0;xK6(Em-r>Bc)@2%Mu^A0G+9~AGt%(Sr;!DZB$JI%I% zubOoaz$8XstZJpTbgl9u6|-@ zr4K08r0pRjwO@$;rvuMm`(MUWjRHKDA+U7{6&L z8UewN2qLbRQ2~v1xL{6Cv()HHPQDOW7iP}4KyNEj^? zA`$g`j++(ZYxzlT7tV}~vv%Tja~r%VBMLhit8cB`rW@)-MjLBlU@Qs_vu9kdpKAOUogQMHJa|nJua)1(CB)N1Uc_Dt@2b+ zst^#0>ay;6R$>-c;bqu@@ne1b_g+}a6Jc~K%{~}%Sb1()u^fBZH~$$K35RieAKx_< zYZbLRqH{7=An{mQ$uAbe)N2#9UQ5DB!4@t>1|}Q>K5J3?FssgB9GVcpy`^nzFRbou z?=_kf${MQos0?n7->5g`hSP|x;{52`a438Y6*nIKFei@G?GkV?>-vS3mX^O=p=ji? z-~L>+!K{W;2_Alru`-jRg=jwFeFM^EKJhp$cCOV;ICA zQBdSdF9X0)!}NmVb%Ao)KcHWBpPKIM_^VIWMtVriH@}P9&V9^zOG`Sy*WSM16^Dzm z!mmnbe^k~}yEtR>^rDxdTvsiFM^Vf4xtnd%ZN(asDKaWg5wXV$P%7rE0O8Gmtgqmb znZBNK$uYeHgFYG7n3PYN22E~5z38>Xf))I+{%rGf^fEnjRz!+~0h!kRRSVfuZ zoQvF^$TRSZp6BrWt&La2NkDB0jVxC;a0sT$U*oANTmOA)xccyvkjbGiKp;&11F zn%$Wp+PJNbz}7D3Js+yl6O6k2(=bfGnt>k~$~YaqDBc-UqJrp&ZNa_?=LDdT%52=D zhS0H>d2oZg&Q(19`pXSNzc*W>KNOS;>KAFX#s=7EFcB}FU(ylw+y?1S(^}VY#=C-c zq#HH7?|kjGAktArT!VMF;aOgemqb5vC9T1AUI&bYdXCf+`+MX#8k`iyf)HFij;7l2wAUd@}G zSO<<{W1TBi-y8UdSnOjFum^$S%fqg_1WaBY1gvyAxt@wAZ{bG-SR@>=RX+#RFXDf_)G5lqj7oH_n{geZr&fK1l8bGF z3z8va4)_54@4~UG(*P|10XDdSLq94AyiiFO0)hnlUA~G9!@HHSuu#vL$ZY1kfA&5s zSNE#;c){%|T3fFHIDopr{P!=Z1H!JBpCM%QySuxo85x`a$Ksie^|;mBf69w*(#dM@ zdY~cnGXFtoU5!-k>n+Q?tyE1*xAO7lQ=I}IY&8B-?`gvtdsLv#A!3P*%h)xd3j84! z{r)_K$M;Wor7wyZkhC)ml;js?2XsCZ9Vb~=2VAdPqyQde56sy^vr zb5iM2GUIho^p$l5jL*dwW*yhEUw>ej1^;TYiB>$CWkqYQ-lII8kDIIauJ|TW^g0%`6 zIou$A$5c8ZB7*Qf&4Wjeo;ij9DIgXgeT*6%=>|JtJ*M#FF9evh$Z5KLe6#ZEc)yVK* zk^{8~wX8HT#3*?ErGv)s^XB^Ky$|JMevxYs=-v^_fT|K5e53v{n=aUSb{H&d89!9h zQ`y6XRWGxHTJ5X}njycVS5*Cw@ z?X&U0iEmX?;y$5mZjK^#Ve1uCQ*wpp_^1$xzA9S1^5U<%znbZ(HGaTshRb1BS=_kN zh_%@Egcm-O>*1=E(dp`op*%6hEHjLJTQ6MTa&417)f!$Zl<QlS@=!H`9?}|Ki)WW?XA+!7a`tGnvgjgA(I$j^Fa2zuwb2tL>>TAp~tN*cXv7Ih%%( z(LVyZ%o-v9UIuF9cQ8LN)P2C!g<@o+u&~1(w0s*H$;-Rq>$&|!|H-Y#KvU<(qHLWC zqtTC>jJVQ`V5bv`0B^RFq<2>$Pa-eXx7KnuZw%>4gE%7rKy&~i1g$}X2 z1+zwe%7v2vpx3fordw-yQ~urz*qu;=6ap5_-SdCr%#PD)>()PR0DITEH{DXJt262N zW{kg@-73)58k4}3#hb#g=Zg-rZp~jQ1FdeowUYbaTpk`wu!mD(Kd?-wra6T2LvYtO zb_8_^h=%je{c6~CTx&ayFNq@f}K&+QvB6!fLymqTO6WT7g1!XOTdK|E`oZZ0?(?KRvzk_%xw5Hd+b&xKY>H9Q5l=&Yc_M z|8B0Qt6x@V&pG>2C$4ZC^n3one;l--j2$z_SAjf&cSS^?4Ep~Z<@Zj#gOLorqajb4 ziT**VF|c&A0ZM)Nsng*V)JK=q^SHwPh6;m92FYghyLpWdnv-kIe0zoKS7#xN6rGG=uR@5ZFp zQ8X0@i+ERZBy@Kb2_rL|VzX-vM5f^Tr22 zIyB__BER`ueGN{{T>V~UXVQpH_)Tp-3g!>vMIwIG{L5mu_4Pfb!|Ywo+Gkd>IXv<^ zYa;UTXAD6aW3u=LG=FDi#~91+u(~tLxMQV(V#HMoiOWM-8R2TxV3^F4%q9{*<=M>;Toa$3<klze5r7@*mKj zM7jTF33?oHYenU=w$?(3cyD4XlNqbZQ}MSEKWqiLymRJh<#Z;MFn@|Rm9Jteo8iS%Dk z5xh`>blYIkSs5TN{w}Kupcb6+E~!B6tH*uqyKF`QO4kcGrH*P;_g3(;a;2w|V0m^S zJtm`KY9h#kq(Uf-pax%J(7lq9e3CEg0k+wG@Ck0ATdywP?{ih{f$DiHhwd5tc!($U?WCdrVj76l?x6>pG@h zdK_F7%1Kr3llMM3zcb(xmE)NgaX-_u`v ztOprKWui4!Lze3)DtJ3wJv|d83>9xBp4LL|+=NL2N-*_`#7EC=@5@fbt-{7TdvsgHSu_o6V-F$83v98b2mxTwf;1Q6w{U-s4(V!rbC%kEXvjN4%{yFl{k<>P`uvWjDy5+fz9@)?rfUIjw(o zkR0ayQOsNK+ND;=S1qrC>J5(h8a6anaiQ!Kc(np~v(>h1eu|35?&TCq z?+QGZ#P1Q^X7si-(hHwFWnpX+28X>XVjYH9jVJe_v+(tJZbq<8t@`?2wb`^QyffS^ zy@A_%bzIWlQ^&=!Ww$RP~30PzJs?PK*de}8`l zZ!O=%xx=5YZv9gK_$q-_#UKE}{%67JFKe(5BkL2T`luxnjFByCa|LZ*~V~(w8AhVTOF!Yd$4R6l;hS8bq7B`*mLr zXR!3Bk}7uAe6QOKb-hL-Erqc@g zeR=b}N#eY#PidRfw5M*Dh91%|35c_M9mV1DmRZ{pY6@M4Kstr8Yvijz7JTS{^PDl8 zql1CX;Ann@>^K%bTT>_QtMf-Zssfc;aYQ{CuFiFlZMHL3dmi_tLe<-gieK4NZ)1e3@`fO#fl$zS2nO5d; zlj6KBETjoh1qY;=+5h zUS7)fdY_YfXP3p_(m`L${&@64dqO5}U7&H%_N5**DG%p6_`0#Q335V<&cwCb%3h8rW;d(S8Wm%35dV4)Ysdt)U((190c+&zKV zpp1;W<6-%0-T#UnIW!9`D9T)gL!b6^8%HGj1z~N*pXXaHZ>$6(Ri5pdR-Z~Jj&&ec zk+{xLj_4HGq=IA8NM9Yz^q=YTOEJGYHR2yKX0(jcJg7HsQ%OjorBC~09*g=X_d(9^ z55pZgTH2xt&e3%0G=qJ*$5&rU?9J3ZTLuKa)+|e-#u>b?))R!tr@!B(mSG)ddU)gZ zpt%ye*I}4TzKjQvXU{ClbT0Bfra!_A_AtUJ5t)wz7c_JVsFl&-&8iWSDLs&wFi8kk zo5Q2kB5#MZu*-aJNRW^gtCrKltfv&p;<84$$put6XwQ1`M$~;irEu_ytRB2yf&{&Mus_F4sn)8xz}?2c_QNH+0@b`;N|L@ zk|uVbWjVZ@s;6sUCosKfNoyMnG3dPhU~CcmOdFrh1l6(L&b)DJ0Ie9`AkvWxo1ICw z(u(t5V?KJH_Kl*i+fH|t-^EBRxM#0t)ol>YNilA5kkxoFXsple$n%QP(B8T?{(Jt; z*M-EI)TO$-_50FBx(;siZgz@%^-!n+8zpi5yH`M5DhTE)e_$CWf7h&h{H2VP+M%I5 zr(I?Ce@T2xSwZ09ijSz7y3{yNFnU|A(^5_nx%Lt2vr>zS_(Y%}!Orl@WBw$|G*YWO zM(f@X=;Y1jLHgBZS`4YFjt{7cYPOo>@8zinrDvgx*(`$_R^+1yMs8%Lc!*bxG$HX@ z@AYZWos>JfMd?oLZ6o0)@KgTcDcc>vTC2I(1iRGo@3a4X+KRR%SF?zfxf1VZm$~J_ z#qfGWW@-(#>PP4zKf|1+ATKhvOwdZ@iOo`*Y0`$DsoTKjNYn~bF3}I8TVF_M!vzR=3gLTicPgSHh&41GYwOYJrECyt6MxcAZR8T?$<=Yx)%;;>-g*%+ zB_$G-Ry53A#6#j1EWs(vQhWO7F#%u^k?a?uv*Ge~Z;T;R`sJ>)VzyJDPhNA@K62&G z$C(%X*7fi1)9}00&85`qD^z{Qc+b7h~#Ho?Z?v3BLrLbY@HlO%VJ=4qjPI>Qcc+|?1<8=Fv@Sfo zRO!@706EWJ3`g#5z4-q1rsx6*Doq}hRY{sRZzYFlpcTSnlky#Qr}&14}wwmkVMZ;j*Gx9$%g;Y|O7=CB7k(tAd2JFe z2;{PJIkkEwXDwK(uXYjAH>z%T!d@)hiZc6?r@S3$i{!VzkczOk?;=X&|4&&6Y8Y)| zzdUM}+d4Rp{+=NebYs$SpfqIlqfJ_OgU{K?i{#zdr1L>^rn$`z@sMOiCghxx$dv?rEltJ;Bux~AYa9iY%%6Z-gF19D$=FkDheTw-S}~9 zdwYAnw2_-1tk}=hrs{zUL-CK#}aVroFP0eMOK6)TkMopI{ z-8^Waicn|fbQgdd;@ghvEOk~Fcu_uHy|s(172Z}Nf$0Pw$J@6#^sV@4^G^qtmkn?F z%%-{EH_PhKwGF6O#T4g(FuCYdnkV8Ob$Zz}ye_yjvvfiOu&>3^iTv?I!lKuKFe{>ou0F5RDkGq*) z6Vg_Y!`pJDf@v}rF9I1haWS!)!3@ZcF+N~l0jLBXlZ*aatG3gQD#@4Esa=5(etr@E zQ&6xAKDun$1~S)Lip&r!j1hAR(S+K04DQ~lRHZ3y%el7i|G@+R>CkuC+ontwI{)aj zd(=1~B>p%4sJ+0X+f5`C{C3n{L7>cB*e5G#xx-rY|JJ{|`&>brSh`1BELI78KB{*i zzMq$&;h}|X=oy(c=$IJXZGCbDBe*Z5rp-#IlrApmyFbs_VC$9l+=@Rs1^?H$kgTyl zlj21*juriXu>gt3ygWyjTk2{ObSIk{Jt13`W$t=)&rIvY&{d}|du+z_&1x;hvNTz{ zDR5b`0Zq?5^AtcJNAdrO`hUD6^cD>_)U}~Ir_omk0esQrRF@~-XKeQl39dZ#YEx2D zkJqsNa(PT4Rc~|30a4}f_B5>&(6nVZ|4fLl$vrQdrX#ms(G~oo{WIpiQ<_w%vQ zgg`9OTGYR0Txq`iGRgc?y^ri8In5JZk8kQ}8$>ZFj`D?h&+RoJCw~c|tSrte*=}Or;05> zkXvUXAO2}PPYo)* zIsvq%h@@Y>lW)5G8pk0`mc6mcM9N8r9J5^=--~e_ik`+<2|y%pw;(<{tX8L3xzs+I zY3#~S01DeuW#e{sC)Rvt+F!hkD;`bYT&}gBu_Ax@=Gw4#59<1FgX*5O@5VFGnlDt_ zWbvo)&@WWi>e(znZ7TVzc{Nc>?!Qe>rKRb6G(VCN>o>A)HJd7Y!ut3sY$8Jx4ITeD zX&>NMcAYzRg}L^OlaG8ub!h?Dwf`ERS1#$HS=Q4%HJG1Qy@#S8jCZ<4IQ5AW*a+-t;Y0CaNPikI z$u9fehGyD%!JWp2u~-JBb8?kMJKlo;_BqJ$QuCW{{9kchWBVz9Em_oo-SA)$0XZfq z_hT2|S^D#LqKzyuEAC%X0O-3-siN{3z3b!lxD^h#zMk0uj-D=-BCauwD2Bnu@)tYc zs%BS=qYApPwW{$Zh_s&8K)ztST2yp8{bxA1LtS;m$1CuZ6656`dois3W|GWXZY;nY zC?tUolpl%k>3&IGUk~=sl6U*UvZ0rAv`9XQ6fm*E7{~mi>dNpR8akPtc!9(+Z{fmall^R1VKe+{{!`4oAt0PJM zj%#zM(u{m84vZ!{RE>>m{PtHl0-0F%Akuq+;Feq#!x}~k*z5hw?6}7j0jTDOgD@Lz zv9yY?(Lv7sk;}ND0_x2QnDdsT&DxApsV9xVlq{Q)s9Gl2bn#b@@WiIMY{V(Gu`c38 zC+~qu7G`iO&}eYpO$dF!s%&R$Saa~sRQ+spnIKpD$+4^3X7j$m@yw_#8)DFVv*(P< z&6W7vURTT>y~oWlZ>qdG1Cg*+nZcR)tvsm~WZ9UEL}vi8t0}nneIL$ae2U3~E;b8) zM(~wfjGw!_+vX~k9FZ=Or;L*5=`TJplbbG2zx-(O=S^A$!;?4dDlfUto=llhXqAPf znZVt47&Cphzsgy))w$w-9KsdGOqzMJ>vn;7A^LdO|9UoN>{5oua@a&s@?FoDPfX0T zvtpRv>TJ;OH%hl%rr-C3$-vfiT?=Q>(k%BSSh4T#xlht^FGHrS`~zP5nsjYpy2G}| zxz0q55#|ig*YwXLOIXBZaLbje?zx%6qDv?4{hd%<=XT{Ma^49Gh`4Uo4LcBJJrz)zZpzFjmS)yKL}D__NCBSK$$oD7vlK zdAuLtE<4p83tD$*P}S*b*x^KNTC!@f3<1WN3sk) z*41oNwdw3!BDTpAy1aH}Hef4pW{6;-_)M+5ZQXlVs13%|dFb)`@5aUya#btMI%d{N z0qG@ozLM3e{^>$Ex^jnJBG~uzl%=$Qd&fERV^q9Vrn=aAb)y7C2?$rFb?v+9PeaA| z-ZeW-A_Jj~^cv9&;vbOZTWTq?VhR(6bvIt-mEoav_gOJmE40^kovKl~&*~5^|cX){{RS(hiRW=$GouLoB_G zeMY1zk!5X%$gKH0d!3z2&Q&Yxe~Rit)D@DXxMm)<$43V~O(S3ru73fnPers3e%FfO z`mB`{GxClG?cd;Ac*r2W9-9Ub9L}%>7Tiz3NSSWwWWBW_(B~cW^mbdd{ zL48_@Ap`A34kcMM7Xd2#V*e0b9$iLKYcBu74Se!re>23lc0zXaTYO-zESHhPTrHx? z`Lv^PlWWI%&Lh9;-=u8vwx5vw;a4P&V?+@jj|QdUHb3bG+iocL7w;Fe&L)Oco_s;M4r!isq1FUvxBV zI!F_k+-%_I5B=A3pIA;9&d^6b+m|CMKO;Z0-oIhZ42+W)O`dqMNFpZ_5lwwr@;b26 za}ly4>m|5PS9I6+xw*ulFuBmw#(bv|Mja+&4;NBX_kBxwu@&D9FwYji6V2K{QrBtVu@8WBVg=Le@u1QZ#OW z!wW{o=Q;6P`pM2B$M{EVbX~LC8*V_a>N*$4ExuE=b4KYi5-rWD>^h(E)0Ez@)ORwL zx{RP+Kc&=5JB=`k{_p3VqU6Np$_{(p0n%bZ;VIM#Yo9{_ILk@I8>i2tuz0@y+tvoY zyTPfL!gCgU3i}V;pAG%o-W?S4A2hNDlbw{KFunz^9~a8(BRLy|@tB8x-OscOL)o>J0mGx9O#26Tr>Q9u1S=yK}Dt^6pZ zvW;a!Av!k~VI=0*N78|t=uB-+i_ev7s^i*!{fyVOJDQsBE3I<>k}`892_4`}k(Exo zF~lSu*X|349bY@fEF(hm{P$Y3<6U*;@LZX7C|Sm4$g2J`*Q4~422vmoYA-K7Khfr2 zF*E)>n=V?~`=ByXN4-P)7s#acrreAi`n;4Nc5+D{(~4c0?9zwZ6fcWyh2M#)mGnQU zzD0cUcZ4mRO?OdomlDuIheb#F**6VNe8Q!u5~|YsJZHDV+;YQzVyZq_V;wZy7zu5J zWi`3yYZwao!~{I&<(yQ3?qq+TwSjxS})vTJU$PtrOM(=m}#?v z&ky)b1@&b{r;Dg@ibUng#gNg9oYp*^2hMB8zr8`ke=$qyr#D#FyiOY_RLOW`?Xuc^ zPgH(n3xBfTSxHNUgBWB;c$fgH>IIFTx6AqyaBOVqOU-NK&ir}gRY{l>>L!_4%ljNV zNqG-EiL)Q{(hrPr&sC&e&Bio3`m*{S;_@LM77XmlrjFjydhExWXQZ5Rz2OtlaFBU7LPXaBG114dy*9?o4(Ua2L_RP zcV+*g_qo%-y_FpG{=_!@s*y7aENY)*a@yh_>w-+)R7ibcY0OqwV~>r zS}4obv~xcLPDqgGTmvlpvGFD|9=G5BMUHV=;yTaiz*jousq(bF@<`F|g7xiM_09~t zD9g*W5Z$x4*Mm=C0m|>*1}MCj3Iwpf5QbHSsRN^{ApFFen2h&I;gK0aZ%tPq>mQbX z9&n4VP1DKSnAZ=`r_*Zcq8e{$nJb=#IqzgKSx^Ra?sPJ9U+4&U3% zhT1>NLmg{KyId@{)#p=n3Jxo?1x>KNerw1K{`V8KIdcE>j_(eEyiNJ|{P62mkO_MQ zM|%z9(?@jFygCulyA6fUE!cAO3F~4v*{(vb&$?vI#u$pRI>2K%d@fnIY z@fi{_1qd|V+-7!t21p2HD!os3{_=GKMRj|5g?=PJmPOq!O`tSc_FO~<&kKtxo+IyH z)TF{Xq0y}r`-OvVGl*x0%S zUe|=qVh9A&i)HcPL}(=2k^x1kek|oNfdt;U9}(lqNzFgNj}HZWt@FStKGc z@mx0-9rTkxBal^$_rh6gaMuB2XNR^|k;i#upu2gb7X$}t)L-T~msV6XG?;gNd-8_b zzUq6d#R}Z6&s3D{_TB>cuI8)hYO}r@`04l01~6gyc*1FwTnxkgK$c4!29=-dvJV%A zYEnQ>6she@1^d3RCDhio|6eR1j{dloM8-rTxmLnc!P?rtK(|~ixyRWd*~#W}lL~c4 zIoWivTb%kGP`iMEE_G+E&a<+SH*s+*jM&XdX1Tt$OKsWJi6jPNTMd9tw%pBW>LYx) z-WCklj)Ha031BDgcz9yvg1dWy`=zG)cNB52IHvc2y;dnK!0LSJFY!1MN_N&Cp4)Q* z@phkOcB%-=GEPcAzm`5Bo8rHRd8oe0o%;H%yA5zcYI=Hl0=3)*#+>g?E8duH%X;y4 zDWEcu#Gb~*n*vJy<(lPt`OMD1=4qF>b^Ir%%wz}U$Z47dfa2;-#R4omW`nvzJ}j0> zjwzYEvOwu7+jxBJ>Vi!{R|eoxrdJfTx}#h3`$|QC<5$14rF$%Z11aqz^=&-^NtG1=CE@=bE zjFQ6D{BjO|U2H(ZNO@QVAg}o!4k9AR7XR}zn;oE9Vbk18MoQLKHB8$Y9ufxW&JccF zy|PYwUr*Q7hMCuUQ|7673nUUrM`^+qq6FSD2JCqAUl6)dhjdVq*7S$P{3F$#uj6&> zR{G!TczSv&s#00qQ*aV;Eh&N8cj$ zISh~tMgNDgZa>p|!(nO@X`Yc7;E7s+IehV8j^o%X2laT&G|+3=X-y~o3$#}GKdnW8 z)_XzL;iZm)oOfAf{vYPvGA@p<+ZrSuAS93=!3hMn;K73i8X9Tn#vwp};O-jSgEj8Z zxJz&i-e}|QZoxtzkh}%?KQs3}_uiS`e3*HsK2=-wsZ&Sx-fQi(HSPoxXPPUFvs6+3 zhf6|=bit-dMPCOhEChDn?5uQ|I68{nLEF0eOE+Tak@O}uJ?#7{hFnDRg|&i@M&*U) zmpu1s5&!Un=D<(va4|Z2qiOf|_aAtb8~{nWHIS5f4OAY44;n*6)9v z#l?{L0}!472?(!UpTC4O`>}4|M^<<*b$|fD&A}aphMa&Fp`X|be`9=?6%wcuy=aw> zOuEh9b^kV@9Edb1=+&yU(Hfx^#_ekV<3G0_zr9{{diJBTtY&^S8&OYWh$PHR*36Q{U#`}SmzZeplXR)hJOV>?0nG* zZLKha14F)ik(ZLyNqD(IBW`Z&r^dNP^J#A0M-PiDQ%iPCR63j#a zr{#=$pZnVXvi_HUNfoE7@eN>DaAfcJFUXL~f9j}B`hnr<1n>E^C>ntw6Eb>`qb9v4 z?|bh|uTmxfbuN?I^HOLW!f$~fLCu&&Ce5aXTM?v196ZqxV9dOk&dn!b5eF@&S!t%< zHrSZi>(RO2X)za@6Fs>4*fgXq(H;jSoi9Vr-fQ%;-uJOqL}^@HDGeuIWX|WCPP1Zs z)w-iB%XM+NH>aL)jD8GBX@MC#xS5%xjtDcc$mRA;5+rH#ajfc(o`eeNT36aLxErR% zm;s0v{e(IX>s4K-UbV&VsJCImtbuaukN@QPJ|;JAmhaFozFYeSZj}B7q-@wFDM?#0 zwi$;ZzaK*nkz3CS33F9t_Pt!bV2u9eChlRfmmDg9gIn{Or6)NBHzh(V1JyD33@)k$ z{h+K4iqTmNPkYCg|6&6{kVh3Y3R&R{z6wS3+VS3O5MCOO9AE8`1S`=l!e@?K_m?lq zGnzrx&G>ZH7ngUlz6S+=33+1m>3czcTC%n=GmmQD3#RpEcwO|UDf4<=g1`DQdE6Ue zl!{tL&$zHr4@+uOPq$JrHryl)&GU|CJW4CB7V?&ssi#MdxhGa0TJ1Tt=Ue+7ja~tyQ-2tH|7$GKILA{YeNF4vfHn;WcZ2LwS-7l z;}l|l5TfMwidvoE^js6`Sr`yUw|6h@9~=}0N%a=rg1lZD z_nFK19>J3=KPY%X5-~7NIX8(m%*ToQD6261=H7Hh;l!IFO^V6S;S6wG#RP;Z$fbyr zm{q$3R_Ln`z$`snr@_H2Q$PNkEaKy`o-ATFHpg(%MY2oc5ikEQ6H0>o0Yr3kABZuu z{aUspauTE526yqf9}4_~x2w*4B}_ zV3VD$G2CL7Pr(g0V;5}>pj+|<^v#`Si#H{+Brw`m))4urzgWtN(xZXX4G#2=@z<{B zZX>hPWeRj5k3L;PHEIlB)71GrAgUaqmzS~$`J}{h155%@R1cA`iTOIo7WT?9UMAh< zQc*H;q-qY&8u$qHcm4s zSQ1UuIP%W0tx3zd^hIYoRq=Mta`#mN4aak*H%w!bnlTN|tCv^NS!uyeOzDoDgYpv| z5n5~Mn8dK=>e}=8vf*-}X0n2U`Dwy@lyrT@(rI4anlSjpp5pVP+xaebW8nb$O#dLQ zJYVv2GEqgxHjda>7wAefxsy968%Wh4V5|d2tKo>(G7OTH#=*-zd-ZPYs>iDjKHVu= z>^k)xFa~Z8GeYJvi@cbQam&29)2&7)Ps`m< z$*dU6+J*+qrHyTakzx&=ZQng0>8_zJ8BvBqAf$M6Gj`gcpAB{aqVun_U0O6^3hKKk zbY}}*;P3k`;9ns&O=tYTa8sW^of8-!w45>x0VrNq=5=RoHi9v7n*B?yIIKU5Bd}HvY{2 zwuQrDj`6G{?N`6XS%xJ6$wJz%E-_aE!OAUL`loq|r#qO0lzUCy*PUv%n(R8x2JY3> z2Xp2uwiUtW1dRlSpfuC`8QoV_P}z0f!oGVi4C^Bk5iO>OB3 z&S$sd!3z&CHL^)?f3y@Xcv!@Q92d1qvod>Z_l~fHhxN@mQu^1bl$G)_tMvD*8s_8TyAv+QUYFD*$W_E7r@6*gx@;;2L7?Q;5dob z+%V8|0YeNbW9_~itoE6#&jR$N)mWN${mc((G@yP2m20mumiA|m!7MtjDSdqOJ%p{Q zimE>(8bc)}NgS>_UG>f~K}Sk6{PvNeZ5;zkB`pG3CKBG~;KxEm1193>ftVC5)D_Pd zbUY?dY&wjXHc+q~M%WQ@_PAM{w>{kUTxK86Ffh1B+W+eZ%xzXgm+J_vMACh^fJteI zQ&{D2$#{XfF!3a(AlWxTdi@m=^Gj@LqOrOVBTKinIz)Df0^D8zwI+IxjB>C@H2zHE zQ>~KGNG8^_SoLA@c)ik)MWt&EHx`#DO4HV`vxs4tWWT^cIaorYQ(o9nz;T?1GE3p+ zIoC>Hf3O$H@ks&@D`~N*72YgT@Y(noMrT5vOolS&7!N16auWP$F#<64$H)tcchk#K ztjiF&#O`+*h5uUR%QcNIblVvXh{fJ{- z?KzWiP~&B*F;TY-{fJEpA=RWxy@{TobVmGVNmZ@!d6tzAO;USw-=a{3T#JF|sWw1fQ)#AGHM6l$2=yH%R2>wu(lap~IypH` zwi!Tm-{}l^3#vGwLfvv^F zXGfSqWu>IN+Y${EW@->N@Ypx&ubDaMe8ko~0zuEffsPl!>knlL3ojej*+M^$uS*k7 z{1IX>QbjhK$(1f7zZQ^U1{&?9@#GFHSGj3Bp$#BSld;?GH(N$^Kiisb_%jyj^GJB; zr`_h@+WYK)0soe=eC=9+Dyy^h4Ab1Fhr(_q{z#cZ0{JMhe31;x76N97_oMT3PlwU1 z&O#lcO{Y}RrM|Udb;ScV=Gy%&Sop#yC+J%+Ni>bIMDptKhi(hOM??LXvQmi~%Ue4H zTk$-uwdYlMj|Pllt+0}FaCGn(xMHtBpt43IW*wno29edx&CPutbBU(3v`5AF8wB8@ zgJoaIV>p;lj6mlvO^g*cHfk7x-_s&A*>j*kkyu09t8x%kH4^ z=~5y#V{K#GbCM6mZx-=kXPghyd~lHc-W1!NDK8*18{jjiX$-1iB-u3o^M{>UkIhp~ z*@}RaVL+P>XypD47w2{_1ZA`W%;(LO6`KRiLC91fW?J4&qxeeyb9N2OXueWzs3edA zoHcER-#U=4K$jf@EEZ>5Bi1_uY8V<;BY*#kwo~Iu?%n0ZqbOmkrtw%${N$ILdwgY2gn@d2|M6GO zr)up!nfzzBKevK^`aI5C`#_?D`t|EvBg^S1{2jws(dtXUm7!#E*6$3xxJubD)85t6PdmUM~Ai7AQR0qr`T zW1%E+*C%@9XCji)tsN&ja@A62kSeq60A2zdOArA5X9t}>qHMqNt9z@VGoes!q5adA zu+wDzirSheLkBzMV_qbC*4WPn(_LP*RdAX1Mw7GrpzQ}(nXiNe*#)F6pC&eaRP@zQ zGqGE{D;5TM?1$aeDC_Ry;NTW7pT;ep9pQ;*A5N;zD%Z0+^x1BAnwZZ;N$3hyPh+-Iy;u(fXGFl1V^b1JpN3(asNfp02Fee$ z$O%&1+7j<3#R=Nw-sjsW6=qNMsye>q8C77sT7ACp{*r+ax9)QD$#f@|Q2QtRjhRgE z#NlZi`sgfXOs=)vO3eP90HbaRc?W_S3a~X|8a&b{SxqCvroPK0E@9lT2{EOl;?y3@ zDO30p+E`pyS6xZvCWxTr;eYh=krS`-8qXw0+%`we1jF4;+QBcUkYEa^@q#UJB?o5a zit@3UpS-G@W^LSK_Dy*?21aJCU@y~!>%UxJJ2e1_wv+AZQ?JB^gc=lZ5&6+cVKgJ> z=dYVLxhF96!C`8MAeqo(f(L}QG=ElDWG36W5Hz;t2E|pOm6g;AHdg0Y7#LQT=#g`7 zbIPQ`aiHQTB^E?`n}|gLlu<&Tl%3cVVGiLfgvO4PDGwf)nW12jE#}8Bt3tUN5 z&ys%2h}bH~1ZzIL{a-8S%>+^2$f2dC>3APt+!bIHgANzKj`iV_%C&w0zj(l9s9Q3e zOP%{MmS_NBn#px%#`#jcvVD4fRP9x+3`2D;Y_l|PJ~6eqD_vd#TbPx;sWrQHYjlDL z1(|&LBq4v>+0d_ry9ay|XXz|pM(x_ln5t_)=mn}Xp5+ZVZ_99;*7hU_&;9#O^Vi|~@AskIS07H~Ququw0l$ZMz_9Y>ze;HRM0G7K zOCk9#r%T`NqP4=l^Y>0jZ$qKbaZ<;*krvNCKqT}AjaqWyb3R@J5Q*%Xbb$Ks5lECJ zZexA@Z!}LG))0&NdTC4X8?8)$b9(1L`dL6@iKT)|vUzU2wOJs`P{W8jX`2_gS1)2& z7tB7iBH(S2ziD}YMRISN_-|)LgZ;|!+1B=_@m~uxr!rl{LetLslM6y%F+Zq=`8z5I z&ir`QoC$PRP*4N#$&U~uhjif}vX7ytMqr*j%<+4$O>`MJb{|ts(Sui}ima8mfWb7U zMkh2wdrsL%dnKvdslk4_{m#v@yrB|kI$~?As zWa7PA!Pi3srdG3l^_eVuKBfdEx=Av7Qu!ZIGDZql#=Yz#ZGuz#9S3|b5EX+5NCrD4syHs2Y$V4Uqo0jdV90=u zV=vY_2Pz(NLCo~u1R679d^u3V(No8Zrf_d&1y-ygj~NUx?bKJa(nz^UU(B+EOV*_jC45=2v4jW@v zEEf65alEPkDs2;^Xc?Gp5K-Wpfd0^W+1%D#D8E#zIZsr^zQhpB-V{(M6DSj) zmy|2ybHhOtN2k8;SBFBhYbk3@)MQ0XpQ+G$YjJawy#9Dm@KX3a{nOngx8}@yw<~Hb z{tXFBNqVrr=Fphv*|vVQqf}s)?l>>i`(CUMoC_eNZ%tNyL7%ct6dZaW$Vf8`mCF{@ z6D2u;5w5+Kzy-aMad+0PTdh+292?OY1u>rKxF1eXki(>Epy1yj8=W?wNF^u&g@seM zLFz$Yavr$Ux&-tm64HS%3U}6DQdoA{7`uW&hsMa*?)!WWWuBzKEJly5N?JH`c8(3p zKaS@eEoG1gi5K;%CxvdAg?n(U8X60bAoO2sF_0!*el7ROGE~#8dqqF>ZDfS%&D-JX z=Bs37(QTZU&*{iX-#-(!L}-c}3>%|5`ePj5Z}TwIsJ?{ZF|bpPiG)CfsGQ4YqbsD!=?IJ*MhJEKw*mD5B&wy?@G;cbBv4!G3p z;&O;8JPx|`l7#BI!TM~tqbTadyUC4vH4KKZSVvUBOWwtzKK{cCx9LYWhzXh%GZ0Xb zgEUuCZWf-CHB=^LZ+>4a{l#Efm(i$2ji}&lj)2_R&Ye5XE!au_?dap$Cu840wKb#z zu#Z%vUxlQ_NWx<_*Ap7=akY^_dLS&F_bK0$nR5_ypQuE?NH5T`3~l*Vv0&korruPe z8D6lK>tGh9kWH`T-t;}Sn!!lT>N;4>qr|AbFePghykBb_Qkd3N&*n@G0yJZ{7{F2* zu!92h)3-W_>2`MJ#Gk7yC10O=pV&hCYST%1r+HVrY}`?f<#b(n~vw#l}~!H>4$9TP+=aUT0+Ci)lE$n6aN4d-mn`Twe^q zW^Rlq{$v4(Sa`;Q7?~gaaQW-*4|KR5L@kwi-X|)y(E56pEB5lG01n0ybv!-e8A|tN zOF{{mM4Ku%l;i2sAhlE_RcLDTl43k|W#h12Z_><>h4X!r?6BTm_V~v2^1!PYKa?Tp zvts%=9s)h5(V$Wf<6c}`e0J`&1u}$+l+}R#{d<)!kwdA@V2|H*LPj|OtJLO6$t_X| zS|=G%-T06cR=;Wgt>QHjgN-w$&`>dr3@`qv&-Ye&kbqd@<@ibw3RJd zrGS(_WnA46%*5*Ua=Q%9B$exwG`W2)>L!o0`ef3#oaEORGI9jO0BMF55N5Fb2v5=1eWQj9Z8!4LEGZcRNg@-wcmi?Z%v;#LYtM2T2tERqjR9en^ue)eUJUzV zCAmBeSf-<0Iu-NqF=j*f}TW1y#pTWNb|fI3wo61w9sHMS;Lw!R)bbLk&Vk z&f&d>b60(K(%Yi$&boD3j_GrK-O5qb0=d}>cIcRV59LPbu#hl3^#HS>;3NY=$}7}Y z@Ij9%3uy)eGE!ZaaIg2cz$LC!)grIr=~c6UB-y>f9nnV$+Vap?gLhSzjZma53w>LZ zcXE_V_dDUbc@=lv35<*Hsg|Ckf}9?iR`86DWA%?Iqt;N2fj)4_F}mJ%V2$X3q05o} ze)Oxv{=V?*O(%OP6~*M<3U;iYcL zm@54ksfL7QTHzQ?Ois36lR`SZj+txPCXU0c< zDiY*v;8d7NgxXwi3L7_hyh0WTTot7>X7_-U;1oPl7}_+VQ7fD!C5iF1OUDKrV%}UH z9&Jcc{ivMkUK>qBVByt}2xQ-;C7c`DJiT18uD1W{xq9MGm02jRMlEcg-8oHAE=phu zuD`Iiyf6wQ;nev~fj+sUR!LHs?T-HBNjM{Vc*4e~z$AE#WBHqQ#po)^HY!uYbUW-T zPHR6Dn^jYldGwdj{#xvEx5j|NfJG*sn(ClP>1{#Ni(_NdQ6+O*y4D#w57>n4f#K{1rgcz!%Sxhbk)3T zUdfTY`z^3}LQRgX1lLWNV~o6i3w4!*{ii^ii&_YPHnF~WqmZr!mD3cxs4}R$dTF|) zw{x=MZ3OEig!$G2iGf~7prD9j-60_7QY-iAv!7h&$LF6XG@3K0plX$O?-{9GE{?~& zJl+{1JtVo=uDk|w1JL+z5bv!516nU?A9i=FQ@{56Nv-NHAj4PabHgIPfz0mU55wsY zV!yKjhOMn*>ex{9F7 zsg<=g8&Md*ljHiE$yu8UV5z611*iDRE>u7kXc^X(+|K>%ny2o}w-ygT+YDeU`#Tu^ zC)D!){ZCyUPjRQjxqhy5&oJspv_}_tdTU?1vB0AnlXsZ{u!Tngt*wD?)C3S?B$>OUVVyK@3BSV3%A2E%7g2Eib z={2UeCX(>zLVa}4m;T#@cF%#}V9Z=@_NIKfYytx=L>aE4tZZt7&Odu*370?aI6BwV+t@xkGEWo1h;0}!TBg24fd|Wxyx8%yQvqp!-<(&aNG*1~~ zXTUu{w4Q{Y7-mq-%)_ee0+yaWUSzeM$Yu_5QPuj&#?`6t&-%fKez#~4$eO_7RKeZi zh(etJ&q*Ufhsmw_H-lnwWJHzCe0?acdqTB|m_;TF1syxpBAxOLqoW65Wwk`+f7>tV zqZF+>zA@VMQ*X!>lZ9yc*d}R(sN3HwCZxn97ow<{ZbHK!`;#iGreszv!TBUtR4YcN zxR=>I^h)h%GNq?qK;f>fYX9ovd7%8t0}CMW;D&jxtjUv;7!0cHL(YqEuF=XSBx@JS zAf-p5VE;Vc&<1RWS6r98(NvWK&%_75Jjv5c&|(^^4{%rhr^}VH9!y}_H)(tTV)q@A zwC2TlqWgq-VSy>6Wa1|`h!KF+^x}M&SD57gC;GPK^0I^UZ*%@z#>0Qdhxjin2;ld( zllLDYgwHczW6@|ke62x?PyqsQj&s)U0Y;(g+0G4F11NR{AW7ZfqyTpqpmsI`0&xI} z#O(zT3JJp`>6-vv6anzH*0V`uCV&;u$|XlE1Ohvb85)q!ZE9w=x7gwtQ2CV{SAt8= zGG$7GG5imOnP!W&UVQXbceh$7p5W~qL+JOWp5iHE1)>X1eKkvg!(Iq{@^n8%{r!f~*QsGVB{er~07x;H)gksXL2 zn%x69YHk(hf#3TFQ}(2YS@+v7qfTI3cYit2v$)*ua%f)Zz?eU=F8_(-9(izO1!9_E z@VJr|I2iSS#Z7n1Sbx5`CU6u1q2~l;qRyncOvXru_cQrl>lcKygKfY3g+e|_U>3=( z=B9PFdt#2}hi}fD;b5lWKBG${=T60yK%AC-p5FFEI`duNR$I8@)K5Cl6)CQHDL3SBBU4wtxQa)oIDLDjT@bUf&< z;rJuF=Mv;*qx_`){grcGk0q`nZ?Ff((Z2T4re()H{Z2qlg;RUVlNtZ;g(DUL+i%pM zbV$&+PM^&5JUyf<#OHkng^_M4cG$SyG`He+pDov1CxfEe!T>j!wk*hX^yYjOBmckd zy{mPH^S5hV&Dt-KHB#Vm_>K+)zad2Sxl%*;UrO=RL_6kaAKDY#IS+g!7b0vJL`WV~ zrN1)iBas#>f$*Z~dXMW)Po~2R`%TfsTiw6)>R^W&GKpMId(3uCHgbM7WI15b(FsZV z+7Rn0u9-pBKB<^pN-(n6!%sXe&-6=tq36^EhYj}gJf`r}8W;SlW7Jf9ElzLv&Cxs7 zqu!^Ac91LE5GV7rt=(7J%_>*twru<4EAn`hZbgsx=y^<{QRV#d!PDeH#=D)?1Z?A4 zQ)jr5=RwVQdY2|_zZge`6sNSTMoX670#91=1wX_3Z0FUfZMUt8I~6+yFzHk70yw;) z-Nm-#sxhy5FwTGHrUb&$EIv|)qGj*_C9?RJ; z(E@qfZoWAhGLo=BrhVaxo!gHAdfY~ zZ?#!@l5EV0`RxfDT`_){lwam)X5^`^ont>!e>7|I`~q*~r}2m#WRIWz^6a_$o}VRa zqzVlISt-t+MqikIoa@cPP!iOj)zRh>Ii=`NjZiUd#(5!dd;40C`LRyc!<81aPIu`O zJ+Orx3EUCbn{j%(tV8`IV@e30U{+vL`bGmF4e5J!s9>x4;(9c+~d z{S=Ali+<1^%UARo`xnC3uPqu>WnI)7CfZ)A3a*cB`y$BGQmQHi*Vy)o?|DiSvx28I zc&ZG(h`*Po1=7GTL*yxkVy|TwH*fF26hd5Y&IdZ0xsVMcWZXk=!A%Y zZF-{4s}0^1Rqo>MMgEyO!@*gV=y0d}qcgA$*`r?9nV5RtXL?EBf%j=v|6s1Xf0k(G zu)o7#f3+?+1BqDsCQo+heptFt>&?(5@_ z%Tl$$`1WQ_O$Kv)3+{{MD2n5>35wBfs#MC}k}3s88amL`VGYSF)j7CQVGY2<&a5p| z%!cBbhekTl%!Iw>6VYlA9zmMiLOl|_e0fJi?i%A%j2B#TISMw z7jx?{7YvMf7L<11DiADG1B=MXrHEN^#Q8`~ZZgY*r!TIF*2`yE-;HQlNS!UR>pI}H zG%K-ekIMI_P;{Mb*ZvN2IodbI*`mJ$ZNn>_dM-p6TPkk18mzx`m1aSjSodi_9+p?y z=`FDF@!Zq2$8*MYv#j8&``9D|VGJBqb@UGrx|AusItr4F{Cnztd*komA z`RV1Q{XBU+GO0b4B#|gWVz%AxkSTd(UK&WqC?tT!$(rwIDh?g6(6H(31`L*^83_H_life=OpjEzzeEFMA-=T# z!O=|oxg6tk`9je_bMBL1rzZUx18(Vo>EgX3H;)mWt4)FP7mbcyKQF(lQTrTX|2eN! zP|qAo`ub(E8;|^EZ0=@Mh{Sl(b~$O9TsJm0BHa%$&|2+90X#m-=vny(?=5RFZ8rY* zk(?4tBrJ|^_GaaO{=k0F&BE0bmCRZlStVnhJ@34q!I3`@HrGwObh`nzo-J)y5pDx` zCbu6DcRJc*%)Hl$HLT1E!^dzXKl*JcKI5LbWqLHEzv2?%VAM1Ht#Gl-y^4O5?1tV7 zSXsd|E1G6deisRvk@rQps&4*>VizmgJoV9cPrdeJ|D*@ylfa2bbzt##!#O5*9VlD&R8^A5w zJUCzh8sbHa00bD#xEHM-t=ip1GrN1kA#G}M2jq2uB+pBr&{GNNjVo$3kOEY50t&pL zCi4adZ&LV(6fSl}edff?BSl*5^)Hk4 zvcA=SEIv_#CSM!n7NINEI`e6+@WE5-eYu(q;koKA3ejEN;2Ag(ErK}j)q5GhDO$;PHm zoDS}pg{QnR06BSQRM0V>Huv;{Y}GDf8pwnHAUSm$Sp`CgO!X2L99`T&C;ONZeeB8GSiKRn){DO9o$Ea{bGXmGG z#%`P$u3_ga{o;7MBBL6|OspZKQaawmMk$k7<)aQ*RnJyQMQQtASuRz-w>s=4;wGOz z0s6JJ6Tm#!K|r2hA78!#oiE*}5kcMi4vDn!z;sbzOVCG?)Zu>ZijNKvQ;E1_&8-Uh zYT|)2VF`7?!xu;OcWQW4RGfD7R>@$P86Hl*`wSGn2;${&$8BR7pp2N53@j`XtVXx) zquD8P0kUE!YNd=I4R)Q}K`rct4_bbCJ#vBIJ{DWdq!v15tziTLMFqc7^ku)5$HI>;m+&B(Jq1vjmW+tW^#fa1LtE*LnNFPT*Qq%-b+ZVSVF@ z<`|*5o5A8d;#a0*OgEmJOK5_~rXL>FUIS5Mq?P$zEH{HLs8nsKZ6yvs?uhR}pk#8= zC5oy+_{B2eW&hmtXYJUF)R~_bks?n4sldfXU(|H>q}pXG!s`6&+}=?^9^Ga(k7o@g z>6B=TWnfsNkkSHh0y5s(A8X|^Kg0bTViT3Zac0oYK`GMA&i$5P>)Y9M9bWXU(?7-r zr8`YX%2`0@Im7bEIM(JE>`x=zi+66Ey% zYlaMgsMk1e@>vjae_7&)&rzaWpxEXdQbQitscOoJ;nxQ?FeXb^Xvjz4jg1iv#G=Wp zZ+J$&(n-D+^O>tSyPT__(52&c-Ld-(tTOfYdFC@a-jr?>riOEoVMKl^swV(O}$X*@Fz(*_3v+k+$c@-iG z3sAcMW75np4&XSDFs0Qp-ECxX*aec86ly2HxJEQaMI0|Dn81T2xI*>An4XoT5v#?0 zZcVRhltEo2Zg%SOCqT91Vo7=C3p}rqezoSMpuWsTNYHZx+QeO zMz0Z@(zYg;$04Tu6Gz2Q{EIK}XyBC%bIn}*9JA>|vI9N4wSKWac6ethWB8kKAjPv3i^tBm1T#8!wpNn4tc{IvEF@&8a(#v4 z6nDsoGEUAXJN+*LS5A3Hg6f2h=y8OC`}VRw`T%p%X=*e%4mT-}Rq4J2_0S~1^y^2L zA1)L_nC5NeC1r@c69=g$dm6adIXQiVH2ZKf2~&D}mCTS2PNozzc0qV5+sxgw zqqu4FV4;Sya~)800MKj$O3DNert4{StZjaE8Pus^N2$;QUjYsaqOMj^*PKMuT^}K4 zd;MZ{9vvA#weR*L_Pb0~DoIH%d5y9ZmtVv_&oy|bd;5u1<%r*R{~d8atM2x-*?phd zzY&1{yRzi}*G0?!pMQFWz4aO>{c$v|q63A&e(X)30Jg%*w^mC)f({U(WwszAp35#& zanGEsD7^#%4c|mAY<==)-4AlLuh!A+57Qoa$ysOxQ5w6Jy4%kWs_5p84q5BV$^GEQ zBnFD`)$Hw=MfX+#ns&CEPYKz@^Obk(>eD>G))m4g-~I;fy;kjpj28ok19hEUzxiV& z0V&Qv<$>%pZEfwFg2B&#VlT}SL^dy~M08CVI_zyOl0GtkMxE<^NhVi=bFOex8eHEM zWo$f8RT|sU=bG`Id#KczSe8Cq1&OZxYSS0f;&K@NC}6%>&!ncfr3HMMU$^_i`j2CG z%Tv0cnx49DwFN}_FdWs@d&0RnU+sS-o@P`M)L&dTF_{+3g_AfOlNnMB1C;_s(yzVBq2F~>kUCD4g3-1pAUl-e{0Aor z52`7XgW)4olbO|28@$R|{8c&aIdRVDPZ#M@FG&KNG{ds1hqWlo{Cqlz+#prQc@GJ| zm{>~8dH|!Pa35#}3S$HHz7W;fEuBCCY@pDW`}Q)R1<)C0k4VZ^$j9xR_J?TI#&O}8 z(Dh8Hfc#dD>E?4kuF2s{#(H=TRlB;7xl53+1v48^8kJf(;;srxoVguMxuT2;ES^36 zy<7pKBk_KI`tVLGcQUJB-8Falk#YGHh0yG*H7O82Zud5WDj)aZlJoeLGqF;=65@ju zNt@P1%}dcl8>6zN3Gyfg+<)!Y@TcZlVS+W{072l%uzcCHSQsJc%QcU|jJlOo*8E{w znrz~4QA$zQf_NK!-m({XZvZ~?K7C0jiidBO0vHIxLBn^E6YfBL@SB2TfOrIdG9%pc zaywV}%8IZJB=);+N+C?PFhq_`xxteM_ODn#{2B~gALDjWXm-J9T;2+@&Hzom+toZnC6>X!;vf(1*2#d-?;T*Px!e(A6E&UY(*o?4Gyb#0xtPc13K1{@ zqS)-xHyXQKHLdJ`pt8AnFty<0(H?M`tb}n!V%g81kL%ieOKF&_2NLf)^HE3eceGZb2`OFNhgO9(H(KI=bP$u{(MclU8b{P3epy+(Yn-CX2&b@4Bz3yze6> zK8?wwF;5SgXMR>C+xKR~o)*xk{O&fd%a(jil=w%N2e(-Iape{ADN5q?&N?I-@fqqS z8c*{sDPoaA_81Oc-Cd^!Dl_URjkzJXREY3>ANGcYuE~4|0B7;-@j5-QAIytn`H{G_ zf$3@CE%&0lqDsUf9T(tR;uMU6;!1{N!7)4y2A*R2#VW?zae**`BE|2Yr z#{Qo@Q(SRwA`M$daj6rkYK#KaGw*aR@T`8I#B+<0(jkwnKBc-J_*MDDr4KlC?J%mV zf8c6iDc9w@AewiX8gd;0n7OS3qJS%Le0)qJpX&A*12tFLd$#O ze!?%AcQ;(|b6Mw260~0+*t3mr9Zxf?)U*%(q%Z+-<4PfX;bcA~kK^}8Zn2*@L;hgl z$eff|IhFcfo&Fp{d3D&MJUR=WBsd?-^^m&{z@7co&GiO7TeJvXo#vBT5_#>?F-Y|{ zswDrG-ulh*$MrD6=L>8V*2u}N8z#ilk|%oUxU;l(P*jz@aa2wi{CGPmv65zCnnFtv z@v}oYCNd&sm#kp}`Df$xZvS*pzB5*==TX;+TNVqok(Q`eCMO5C_902NAu?|Fd1G*F zSMCOnD#MoTe5T6p%~{rHcbU=AQyw5EtJ2WHO(EYdr6{jg9?YFbwIbHEW{JlT!sw=_ zVz_HN(eJ?hn%WSlL)ep491dqe`P&C21}h_>>U6&4Of3__uV`uQ2g&uSQ-$g~s@CY^ zCiwOt;VpJecB8&7u$*odsnN|1b?rNdGrS0Ut5^HqsXtYw^s3t@RTGDV;AZqrPy5@W z4+LB{5|{%Ez?{I104m^y_V#Yy#Tzp@1NPhwME<~{YBtK(c&(Wf#5xc|A)zLc!0;}s zWPp()G1S1h%j-=_J!MT%o3e)T$5YhLw>rCE$fIrV{<_%vg_#r$QpKZ}1-^&}FtG$} zoozz=r2%AE_ z(A|A}W9BJUSz{lWBX5vpc%K@Szj`s`k8-fCNu6Ly^Z3KN2 z8%s3L$+!K1A;l*%(>}G>oo4*Eu3|l!5dyNmj^G>0xl`LkqLCTMMz%75?X361CDQTp zZl0Ke+f3iv#o+$6pWl~$9sC#E^setD7MQl+5v3`4bXbZu2QkoYuyss7$7?Al%fMtv6wD zKB(!@MX;iVY2!qgM?X9vvgH&1{n!citymJ>2Qf5~@4I)QHf{!jLMGb`JM@1ltm0T^ z3;MkK)Qu~VWA(%DE(m*8ajV&*O-D)gI2sAr?smq{6~Tqc;q}-`X8q20mS(1aPiMw zJfKF_aHWyFwY2QU3{ho!%X&(!042Qct4QsG`}yp)A&hu+c!Qr6Mc|ifnH3=W)RFx= zLY#DYaLqjFixicUZGmA0s+&K}JjAFgrV-TZYw%gXgbZ(=C@!N4={1-()x zmib1~fMnKbUB0um-wc@AMEz@j+jND&F?U}G2HNEGA7X0!Kk8Q|b;)2)0kOFW@WI=w z4FUp<{x(w^(R##oe!g-G=PO;2s%c(9;2Mi=YdG4t0RtUq`AmpQg*>8o{ zO%gnX+0PLgUZt5m5vFvIpRRSz51kThZEb5{d0cl))`17&7xTdwdXeDL)I27WYps-= zkO6buS-6y!C`9dT;mDyjbNnCf;wOc2VQsplo1W;3FTAog*Z~9KtnWfY!(C*Kfmh#F zKq@SqMhq7(BF`cP62or*xms|eRm;|-jM;V56dCncQ)dYkCHo1b%sB4$WJ+CmDoTc* z|5baprt(MSo_&4-^NSz-P-FylHr+rd;05SU!a-kJ0A+QRv`SIlyyuV~qXM|vU_}q( zfq{Wi)2}&(W0wNyJ>Xt5!Y|0gCXL)|)57-Yw(X(;{9}aLxPZP7%|FvS4zBcX+Ks5=eRSA8Qj)6uI6P4^7 zR6|ihTtNBthyZM!N&Lygd7m{QW5_`09=pCNE=qJ8Kl`L;z@nKh`~CYaqnP=QryV)L z_R_rw5p;U`UR_G6S5%8cn1mq+=Av>=r@lcR*GpHv8ndr%k+Cd>37tFX=?*QlxG2vJ zTZh_CIH{*J!o%3i3{}7J1gRWdIK+gn{oCRx=|r<{+){h(C%}E0q5#yp&yhO60Yu-P zATI(A$HT=PTP||Y=P-V5GjI@1Kpl`UO6kLO$MF&ExXDV^T>u)nUtk(Cpe=Hod#Adz zrJzU_2Ybxg!ycOS5fRgkubAg8dOc)vw1$lxnT#DnE-aBXZbC!*68DafiL++H;~{U} zRZpDkgebkSPjiFXa39Y+ImDMn)_25wv>Kn>##E*@X=uLx+N@l2xZ~-nd1^LV2}_i7 zmYrf0Kc%Jo(R|p6r|9C}6_AfrzIxA_h8F+PC%(M|^dp%_zi}quVK{l7-s5xl3dlFEu`|5S;p)Nvp~`iz7^^85M^nq^m1* zaTHz6r{(0`MoT$X`IgHge_3^wi=j#M#fvJ$5eo0}pm&>+=@mnd$Q;^c=;!qVghuL# zA_|2{pP~z^^s!WRiwB+&m&#K!WuZHi9O}7PsTEZ;pB#!Nv=;W(#LW#>aKmVp2#y`P zYZe}t`H5oGhbAnq+6P^b)`Zu4ud-@wbDfMn_X$lHX**_I;t~=-$VS)NjI5owDWQ&J zzOPdY!XMnOImG`to3+y*N;~VO-YcNKFYGwsdf3FH2go@3JPaa6KIGZnF=siv_+she z!7g^|)nsOB*YXD+rDnE(d>d)o4|#bPR@G0B06D)WUy8%DM@QapqLpNMNV{v~YVCKH zHc4C#J}n>(;b$>ZH8k9)91?O}<^yUnI(+(+SBsI88ACsEfx|dTt7CF+F=V%>JMDH# zn*;bh?b7n{^6qW}Q0opjfHI?)ND3uCzyHdL8Z!vPAb3)x6U>|@yb5o);jM@ONXp17IM^u z#_iCF18IwWm|;p_$a2m$g;hEm-6yu?R!oOg|&_@F-4QGob`?^ zpFr0i)wMH=^6sdI|+-5}zc-JlG7#Tzry}Mpbj`KzVMwQZDrJ7yjQ8DeH@A2yTihRv22BN?05)LD?v%(4cG?cWZnm-5SK&+>?JT#7JBFk`p7 z{Bt%qhO)Dz(0%>**{{TKL%CN6U+-^@RF|L1VfnKU85pHMAAAdxv1X`+-Fyqbz6KS- zLV9#So%nm+&7#kFeV+|p?)5gy+YXjhCh$?{M#xCiPbf2`-C|?Be*F$ASU_iqu4&G_ zo97XI%Ftf2AiYg1E{maX@l^+fv2J=4b#ZdZ(1Ji*oWc6&FT#bDQK7gp+suBq!X9q= z&1)6yA5hM1FZ`9pVt*!2;nS1 ziX;i2GVtW)wFX6>(|;-EMCW)%P+YCLZ3clXduPyLdT*cRAC_*N(X~;xXhRUn9-lB4 zMrG(b-?Kltf=Hu3G?q{J+ij<%AdQ~2x^Iat_R2K-yGE{=!cwn@Dytj*p(~{_jrzsC zTM2nNVv;CLFFWW-I3fGXwBpFZ_Yvi|r`~bd3avJA*UYDtzO`Q-Id82NA@bmP?w4x? ztK$nYzLX(PlyU@+JZCem{Y9=VT&XzyCGY$bI@GB#Q1xvA32F49V6{fLO^68hjJeLp z(7-{qwUO43i^H;Ce4G@_6>4SZa8T1}*z^9Ga>mORLf>3x#)V~Vm2dH;-y@%^Du3v|wl zOzDMX^70OiKH8TyMw=ZzeZInNnh$vZhi-0XmUVL?N>(G5HFa;|=9RGW*z2x|5?Nx9W&1H#;ktb+1sU$t6pV3k+3L?$Qm#0 z%e$5JL;c2lR~rvsp|E#(5!OJpSU_;@TnDyL9Np)|NHtgHEq}K&>ayEw7ydtCE7`f! zSAeJ)L5-bXpgz3;E)eMElTFs$r=G8-COAO3+w>-N?=QXIm4W(=1eawT=5x~L1RGsx zDanWC;nfe$zt5QRHWEbck7vd;s)u-Rw<_kA3{~>V3LsTPMoTL4?s3TU{P4NW@9J4m zeMx|^g#I^X^-|3B7YsZf!r9tAAdnvVNljkY=y+iXoH+pe|{+O&DQmnPVQ+6Yl(&FsP?ik%W zc1ZTXFB6(Q$i932ciHP3=WXxBjhQ7p!_8G|C4KzV_W|w>L&>Q7dgsj}+bxUozI2O= zipFNohX?CD`b3e8=Gz@8gO-IGB6rl}9KC5m+WU`;gPys9pE6X{oZj2W@Jgx9&`;H^G*Z(v7ip)L zFGE)E&e%Eruqu#t>*O_I(cvz8Qk1O;&+Z!)TyZooHXY^tWVAQ|!_+Jo;6@FR)gpHu z76?eN?^YJ}A$i8nHeHLJEj^W9qXe!)Tp`XcqS3Ft#JC?ml=%HOtMhdj;4vm}U)H?* zo${06b@&(Afp0t|bc}#6aftTr~qYI_*R&kH}p=^q%1}u^rG9ckyNnM>mQqwUbcoctC|=V3`+=qP%^SCL-lRWvQp)Zv5s7uu zc=b)sT`Aj*si-~nFCP0(x4&9j81R`~at$?FS{60G4y1CYYunN7l0I;A z^Ye7|sG1}ue955ozukxVpG&~Gh_rx%Qf%djcNsV7kGkXM5PACi&Ffa7oo^ZUzu#vR z)ksc>P8TgOVw81$Tvhkt-q*M&jTV90JBS|(1wXaFrfyv^j2&5K0`H$*R+J-6oNKY& z$L;w5-jQ`Rmx$Pv8pJ&z4z|?LtS2n5(&g`7k3A~=1ij$z zq7MQwlWQAy@ z>o#{e3yo2#;S}u!9fGA-EV)Iok5+6MUn(NH=ch8MsI_mQa&n4V&aaFTq}!KHcuhHx)nbUx6=bE3tLYmx5PTlZy);_-@bhkzks0( zLB@~w8l`LA`&9X%D(L&~Pkx!|;>Q)rhu<0--dqR;N3NGvQE{Q&fJ9BA0~77 zi&1WUb?3X^(8sUj%hOrtU~75czHB9P_5OV%IrF<}PN~&1sL=?jS25H!D5)>9dWS}% zEJBNVeHY7u-hV9>Bem}=ZEXC`^1JBx^z{|$VwVKFIbRD9xrwk@X=Ar~F2jv9o~+xA zaaT7sH2kV&b>!Z6U++7ONwkQW`v>V9W?t9W8qWxT6|3Tq@`gt5jC%Wi-=FTW`dSqx zzvRdA)ij;rV%KMTFOiOfB?2K*2}xa;hhA?km)dIPLh`9Bm(`0ug_ z|AW7V^#t_o`PKiF2T3|BL#cWb!)E8qBT-SPft=( z5(~U50orDI6f}4^kX*xuFP;!Bljdfu#-!A&g=tOvD(gqK_NdkdxS?dF8xIE{$D-o< zM~bHekpFIw#s4Gz8DI#34B{OVaR5O9of|HgCkivt8Q(?{+qE!!y3sSmloqh%p7^k} zKF{66CEG*^!bOGVp0i{Ul`SnTKyj9etUh}ggTPMK`ko#mM1Uw){}0KLwOGf!uur1v z95Js-R-I!*2BlAg%6s$Z8E$qfABBoYN{(3yto_^RM1wo_@6(C+M;F0_E^8l}v-R04 zBgovH4q)T;lIc`xGMIWIdj~g6idU)q6 z(%Nk+{a|KQs_*QYK7*j5MKOjm1<`o4yOJ$I{{tA4zkcw+MB!H;d~Uhgr1|$rFFIzP|!&d!kOtN`7?cL76{lnUu z(tn0$tIoY%+Q~xLK$uk(-NBGJp84K?v{`yEd*P=;((AF$nn!~VZIbnO-dEKAEt^@y zbM}Mk)NF10j0NW81Y%13b*vWt0FCT}>uQ=;q#Yd3x(O?4Osw@;U;56@vd3KWR)Wl- zKb3K_L$tXM+0XM~X9JP;@jL42$iveqX{>h|be|k|R8drOWszFtxFBwt)ontYv(xRf ze$K=WgubG4`d=A_-V<}1q$G*+rh1tXe%*rAN7#%ei9lVj^?IHIMr^vyK2{}xq)Fg) zM3cO!>keKClk-6OX@Wl1Q#QG3?`N+x{|t&9l9!|HXpwhMniKn7ee|1|ZzAA3@v+SZ zlGf2tLac~2Pn_=?B+JnVGGpvB6=npgO;~sKP4$4>DE0=5U7%`UZ)0PrKR4>pqV;If zCNA31dcJ<4^&+MxrMk;XRd-X4|Dxbxofxe5eL?dq|DF{Gc(E^l@_`OX2d<)Eh0yRZ ze!iPA`O@p`H=Sd0w@**49>Q?+NkCg70{+e@<<@5K6gyFze=WY*MtD*=t!i6jba}J} zC^rrb4LJc1`Oa?-E%j3pZDB2NhW4e9C~Hf7Hn#C{PyC!e`ZN5OBWqS>vO3y9>U8cnZL62ZZyX1;OqucS9N_p ziQY(ggfOkWK+Wi}m;#H7kEil5&6%)8*= z^$As!llG%k_@tr^%t$bQ?qhHJ`IXX#WG-|mKM;jFl# z=~fCyn6_Q%>GE~^_M}(S+UbV~4~rqbB2%0D88O}GEH7wygKw9ScE2sKZ5OsY(jcoi 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 diff --git a/tests/server/confirmed-access.test.ts b/tests/server/confirmed-access.test.ts new file mode 100644 index 000000000..2d7260f07 --- /dev/null +++ b/tests/server/confirmed-access.test.ts @@ -0,0 +1,466 @@ +import { vi } from 'vitest' +import { + Controller, + CredentialModel, + SessionModel, + UserModel, + UsersController +} from '@ditojs/server' +import { TOTP } from 'otpauth' +import { createTestApp, getAppUrl } from '../utils/app.js' +import { createAuthenticator } from '../utils/authenticator.js' +import { createClient } from '../utils/client.js' +import { createTestDatabase } from '../utils/database.js' + +class Session extends SessionModel {} + +class Credential extends CredentialModel { + declare type: string + declare data: Record +} + +class Member extends UserModel { + declare id: number +} + +class Members extends UsersController { + override modelClass = Member + override member: any = { + allow: ['patch', 'put'] + } +} + +// Log in another user without 'post login', as apps do to impersonate users: +class Impersonations extends Controller { + override actions: any = { + async 'post .'(ctx: any) { + const { username } = ctx.request.body + await ctx.login(await Member.query().findOne('username', username)) + return { success: true } + } + } +} + +type Client = ReturnType + +describe('Confirmed access', () => { + const app = createTestApp({ + config: { + app: { + normalizePaths: true, + helmet: false, + keys: ['test-secret'], + session: { modelClass: 'Session' }, + auth: { + credentials: { modelClass: 'Credential', secret: 'secret' }, + passkeys: { + name: 'Example', + id: 'example.com', + origins: ['https://example.com'] + }, + totp: { issuer: 'Example' }, + reconfirmAccessAfter: '5m' + } + } + }, + models: { Session, Credential, Member }, + controllers: { Members, Impersonations } + }) + + let url: string + + const post = (request: Client, path: string, body?: any) => + request(`/members/${path}`, { method: 'POST', body }) + + const loginWithPassword = async (username = 'ada') => { + const request = createClient(url) + const login = await post(request, 'login', { + username, + password: 'secret' + }) + return { request, login } + } + + const createPasskeyAuthenticator = () => + createAuthenticator({ origin: 'https://example.com', rpId: 'example.com' }) + + const registerPasskey = async (request: Client) => { + const authenticator = createPasskeyAuthenticator() + const options = await post(request, 'passkey-options') + const passkey = await post( + request, + 'passkeys', + authenticator.register(options.data) + ) + expect(passkey.status).toBe(200) + return { authenticator, passkey: passkey.data } + } + + // Set up TOTP for ada, and return the TOTP of her authenticator app: + const setupTotp = async (request: Client) => { + const setup = await post(request, 'totp') + const totp = new TOTP({ secret: setup.data.secret }) + const confirm = await post(request, 'totp-confirm', { + code: totp.generate() + }) + expect(confirm.status).toBe(200) + return { totp, backupCodes: confirm.data.backupCodes } + } + + // The setup used the current code, so we use the next one, which is accepted + // for clocks that drift: + const getNextCode = (totp: TOTP) => + totp.generate({ timestamp: Date.now() + totp.period * 1000 }) + + // Move the clock past the confirmed access, which only fakes `Date`, also + // for the app: + const expireAccess = () => { + vi.setSystemTime(Date.now() + 5 * 60 * 1000 + 1) + } + + // Request each security change, which all answer 200 within the confirmed + // access, except for the empty registration and the wrong code: + const requestChanges = async (request: Client, passkeyId = 0) => [ + await post(request, 'passkey-options'), + await post(request, 'passkeys', {}), + await request(`/members/passkeys/${passkeyId}`, { method: 'DELETE' }), + await post(request, 'totp'), + await post(request, 'totp-confirm', { code: 'wrong' }), + await request('/members/totp', { method: 'DELETE' }), + await post(request, 'password', { + currentPassword: 'secret', + password: 'secret' + }) + ] + + const expectConfirmedAccess = async (request: Client) => { + expect((await post(request, 'passkey-options')).status).toBe(200) + } + + beforeAll(async () => { + await createTestDatabase(app, Session, Member, Credential) + await app.knex.schema.alterTable('Member', table => { + table.dropColumn('password') + }) + await app.start() + url = getAppUrl(app) + await Member.query().insert({ + username: 'ada', + password: 'secret' + } as any) + }) + + afterAll(async () => { + await app.stop() + await app.knex.destroy() + }) + + beforeEach(() => { + vi.useFakeTimers({ toFake: ['Date'] }) + }) + + afterEach(async () => { + vi.useRealTimers() + await app.knex('Session').del() + await app.knex('Credential').del() + }) + + it('allows security changes right after logging in', async () => { + const { request } = await loginWithPassword() + const { passkey } = await registerPasskey(request) + const responses = await requestChanges(request, passkey.id) + expect(responses.map(({ status }) => status)).toEqual([ + 200, 400, 200, 200, 401, 200, 200 + ]) + }) + + it('asks to confirm the access for security changes later', async () => { + const { request } = await loginWithPassword() + const { passkey } = await registerPasskey(request) + expireAccess() + for (const response of await requestChanges(request, passkey.id)) { + expect(response.status).toBe(403) + expect(response.data).toEqual({ + message: 'Confirmation required', + confirmationRequired: true + }) + } + // Reading the passkeys and the TOTP needs no confirmation: + expect((await request('/members/passkeys')).status).toBe(200) + expect((await request('/members/totp')).status).toBe(200) + expect(await Credential.query().where('type', 'passkey')).toHaveLength(1) + }) + + it('denies security changes without a login', async () => { + const request = createClient(url) + for (const response of [ + ...(await requestChanges(request)), + await post(request, 'confirm-access', { password: 'secret' }) + ]) { + expect(response.status).toBe(401) + } + }) + + it('confirms the access with the password', async () => { + const { request } = await loginWithPassword() + expireAccess() + const confirm = await post(request, 'confirm-access', { + password: 'secret' + }) + expect(confirm.status).toBe(200) + expect(confirm.data).toEqual({ success: true }) + await expectConfirmedAccess(request) + // ...until it expires again: + expireAccess() + expect((await post(request, 'passkey-options')).status).toBe(403) + }) + + it('rejects wrong or missing passwords', async () => { + const { request } = await loginWithPassword() + expireAccess() + for (const body of [{ password: 'wrong' }, {}]) { + const confirm = await post(request, 'confirm-access', body) + expect(confirm.status).toBe(401) + expect(confirm.data).toEqual({ message: 'Password is incorrect' }) + } + expect((await post(request, 'passkey-options')).status).toBe(403) + }) + + describe('with TOTP', () => { + const loginWithTotp = async () => { + const { request } = await loginWithPassword() + const { totp, backupCodes } = await setupTotp(request) + expireAccess() + return { request, totp, backupCodes } + } + + const getFailures = async () => ( + (await Credential.query().findOne({ type: 'totp' }))!.data.failures ?? 0 + ) + + it('asks for the code after the password', async () => { + const { request, totp } = await loginWithTotp() + const password = await post(request, 'confirm-access', { + password: 'secret' + }) + expect(password.status).toBe(200) + expect(password.data).toEqual({ success: false, codeRequired: true }) + expect((await post(request, 'passkey-options')).status).toBe(403) + const code = await post(request, 'confirm-access', { + password: 'secret', + code: getNextCode(totp) + }) + expect(code.data).toEqual({ success: true }) + await expectConfirmedAccess(request) + }) + + it('confirms the access with a backup code', async () => { + const { request, backupCodes } = await loginWithTotp() + const confirm = await post(request, 'confirm-access', { + password: 'secret', + code: backupCodes[0] + }) + expect(confirm.data).toEqual({ success: true }) + await expectConfirmedAccess(request) + }) + + it('counts wrong codes, but not wrong passwords', async () => { + const { request, totp } = await loginWithTotp() + const wrongPassword = await post(request, 'confirm-access', { + password: 'wrong', + code: 'wrong' + }) + expect(wrongPassword.data.message).toBe('Password is incorrect') + expect(await getFailures()).toBe(0) + const confirm = (code: string) => + post(request, 'confirm-access', { password: 'secret', code }) + for (let i = 1; i < 10; i++) { + const wrong = await confirm('wrong') + expect(wrong.status).toBe(401) + expect(wrong.data.message).toBe('Code is incorrect') + } + expect(await getFailures()).toBe(9) + const last = await confirm('wrong') + expect(last.status).toBe(400) + expect(last.data.message).toBe('Too many attempts') + const right = await confirm(getNextCode(totp)) + expect(right.data.message).toBe('Too many attempts') + expect((await post(request, 'passkey-options')).status).toBe(403) + }) + + it(`doesn't let sessions guess codes by turning off TOTP`, async () => { + const { request, totp } = await loginWithTotp() + for (let i = 0; i < 10; i++) { + const remove = await request('/members/totp', { + method: 'DELETE', + body: { code: 'wrong' } + }) + expect(remove.status).toBe(403) + } + expect(await getFailures()).toBe(0) + expect(await Credential.query().where('type', 'totp')).toHaveLength(1) + const confirm = await post(request, 'confirm-access', { + password: 'secret', + code: getNextCode(totp) + }) + expect(confirm.data).toEqual({ success: true }) + }) + + it('confirms the access with logins with the code', async () => { + const { totp } = await loginWithTotp() + const { request, login } = await loginWithPassword() + expect(login.data.codeRequired).toBe(true) + const code = await post(request, 'login-code', { + code: getNextCode(totp) + }) + expect(code.data.success).toBe(true) + await expectConfirmedAccess(request) + }) + }) + + describe('with passkeys', () => { + const confirmWithPasskey = async ( + request: Client, + authenticator: ReturnType + ) => { + const options = await post(request, 'passkey-login-options') + return post(request, 'confirm-access', { + passkey: authenticator.authenticate(options.data) + }) + } + + it('confirms the access with a passkey, also of users with TOTP', async () => { + const { request } = await loginWithPassword() + const { authenticator } = await registerPasskey(request) + await setupTotp(request) + expireAccess() + const confirm = await confirmWithPasskey(request, authenticator) + expect(confirm.status).toBe(200) + expect(confirm.data).toEqual({ success: true }) + await expectConfirmedAccess(request) + }) + + it('rejects the passkeys of other users and unknown ones', async () => { + const other = await Member.query().insert({ + username: 'bo', + password: 'secret' + } as any) + try { + const { request: otherRequest } = await loginWithPassword('bo') + const { authenticator } = await registerPasskey(otherRequest) + const { request } = await loginWithPassword() + expireAccess() + for (const passkeyAuthenticator of [ + authenticator, + createPasskeyAuthenticator() + ]) { + const confirm = await confirmWithPasskey( + request, + passkeyAuthenticator + ) + expect(confirm.status).toBe(401) + expect(confirm.data).toEqual({ message: 'Passkey is incorrect' }) + } + expect((await post(request, 'passkey-options')).status).toBe(403) + } finally { + await Member.query().deleteById(other.id) + } + }) + + it('keeps pending registrations when confirming the access', async () => { + const { request } = await loginWithPassword() + const { authenticator } = await registerPasskey(request) + const other = createPasskeyAuthenticator() + const options = await post(request, 'passkey-options') + expect((await confirmWithPasskey(request, authenticator)).status).toBe( + 200 + ) + const passkey = await post( + request, + 'passkeys', + other.register(options.data) + ) + expect(passkey.status).toBe(200) + }) + + it('confirms the access with passkey logins', async () => { + const { request } = await loginWithPassword() + const { authenticator } = await registerPasskey(request) + expireAccess() + const client = createClient(url) + const options = await post(client, 'passkey-login-options') + const login = await post( + client, + 'passkey-login', + authenticator.authenticate(options.data) + ) + expect(login.data.success).toBe(true) + await expectConfirmedAccess(client) + }) + }) + + describe('username changes', () => { + const patchSelf = (request: Client, body: Record) => + request('/members/self', { method: 'PATCH', body }) + + afterEach(async () => { + await Member.query().where('username', 'eve').patch({ username: 'ada' }) + }) + + it('need confirmed access', async () => { + const { request } = await loginWithPassword() + expireAccess() + for (const response of [ + await patchSelf(request, { username: 'eve' }), + await request('/members/self', { + method: 'PUT', + body: { username: 'eve' } + }) + ]) { + expect(response.status).toBe(403) + expect(response.data.confirmationRequired).toBe(true) + } + expect(await Member.query().findOne('username', 'ada')).toBeDefined() + await post(request, 'confirm-access', { password: 'secret' }) + const patch = await patchSelf(request, { username: 'eve' }) + expect(patch.status).toBe(200) + expect(patch.data.username).toBe('eve') + }) + + it(`don't include unchanged usernames`, async () => { + const { request } = await loginWithPassword() + expireAccess() + expect((await patchSelf(request, { username: 'ada' })).status).toBe(200) + }) + }) + + it('confirms the access with logins again', async () => { + const { request } = await loginWithPassword() + expireAccess() + expect((await post(request, 'passkey-options')).status).toBe(403) + const login = await post(request, 'login', { + username: 'ada', + password: 'secret' + }) + expect(login.data.success).toBe(true) + await expectConfirmedAccess(request) + }) + + it('confirms the access only for the user who confirmed it', async () => { + const other = await Member.query().insert({ + username: 'bo', + password: 'secret' + } as any) + try { + const { request } = await loginWithPassword() + await request('/impersonations', { + method: 'POST', + body: { username: 'bo' } + }) + expect((await request('/members/session')).data.user.id).toBe(other.id) + expect((await post(request, 'passkey-options')).status).toBe(403) + } finally { + await Member.query().deleteById(other.id) + } + }) +}) diff --git a/tests/server/passkeys.test.ts b/tests/server/passkeys.test.ts index df68c903b..053f5f4c3 100644 --- a/tests/server/passkeys.test.ts +++ b/tests/server/passkeys.test.ts @@ -299,13 +299,13 @@ describe('Passkeys', () => { }) // Let the challenge of the stored session expire: const session = (await Session.query()).find( - session => session.value.passkeyChallenge + session => session.value.authenticationChallenge )! await session.$query().patch({ value: { ...session.value, - passkeyChallenge: { - ...session.value.passkeyChallenge, + authenticationChallenge: { + ...session.value.authenticationChallenge, expires: Date.now() - 1 } } diff --git a/tests/server/password-reset.test.ts b/tests/server/password-reset.test.ts index 7594c4aff..64a617d4e 100644 --- a/tests/server/password-reset.test.ts +++ b/tests/server/password-reset.test.ts @@ -57,10 +57,6 @@ class Guest extends UserModel { class Members extends UsersController { override modelClass = Member - override member: any = { - allow: ['patch'] - } - // Not async, so that its errors are thrown right away: override sendPasswordReset(ctx: any, user: Member, url: string) { if (user.username === 'broken') { @@ -343,18 +339,15 @@ describe('Password reset', () => { }) it('deletes the tokens of the user with password changes', async () => { - for (const [path, method, body] of [ - ['password', 'POST', { currentPassword: 'secret', password: 'changed' }], - ['self', 'PATCH', { password: 'changed' }] - ] as const) { - await Member.query().patchById(member.id, { password: 'secret' } as any) - const token = await getToken() - const { request } = await login('secret') - const response = await request(`/members/${path}`, { method, body }) - expect(response.status).toBe(200) - expect(await Credential.query()).toEqual([]) - expect((await confirmReset(token, 'another')).status).toBe(400) - } + const token = await getToken() + const { request } = await login('secret') + const response = await request('/members/password', { + method: 'POST', + body: { currentPassword: 'secret', password: 'changed' } + }) + expect(response.status).toBe(200) + expect(await Credential.query()).toEqual([]) + expect((await confirmReset(token, 'another')).status).toBe(400) }) it('rejects expired tokens', async () => { diff --git a/tests/server/totp.test.ts b/tests/server/totp.test.ts index 451b72c5e..c002f03d3 100644 --- a/tests/server/totp.test.ts +++ b/tests/server/totp.test.ts @@ -385,21 +385,6 @@ describe('TOTP', () => { expect(right.data.error).toBe('Too many attempts') }) - it('count when turning off TOTP too', async () => { - const { request: self, totp } = await setupTotp() - const remove = (code: string) => - self('/members/totp', { method: 'DELETE', body: { code } }) - for (let i = 0; i < 9; i++) { - expect((await remove('wrong')).status).toBe(401) - } - const { request } = await loginWithPassword() - const last = await postCode(request, 'wrong') - expect(last.data.error).toBe('Too many attempts') - const right = await remove(getNextCode(totp)) - expect(right.status).toBe(400) - expect(right.data.message).toBe('Too many attempts') - }) - it('start anew after a right code', async () => { const { totp } = await setupTotp() const { request } = await loginWithPassword() @@ -565,33 +550,15 @@ describe('TOTP', () => { expect(login.data).toMatchObject({ success: true, authenticated: true }) }) - it('turns off TOTP with a code', async () => { - const { request, totp } = await setupTotp() - const wrong = await request('/members/totp', { - method: 'DELETE', - body: { code: 'wrong' } - }) - expect(wrong.status).toBe(401) - const remove = await request('/members/totp', { - method: 'DELETE', - body: { code: getNextCode(totp) } - }) + it('turns off TOTP with confirmed access, without a code', async () => { + const { request } = await setupTotp() + const remove = await request('/members/totp', { method: 'DELETE' }) expect(remove.data).toEqual({ success: true }) expect(await Credential.query()).toEqual([]) const { login } = await loginWithPassword() expect(login.data).toMatchObject({ success: true, authenticated: true }) }) - it('turns off TOTP with a backup code', async () => { - const { request, backupCodes } = await setupTotp() - const remove = await request('/members/totp', { - method: 'DELETE', - body: { code: backupCodes[3] } - }) - expect(remove.data).toEqual({ success: true }) - expect(await Credential.query()).toEqual([]) - }) - it('only lets logged-in users manage their TOTP', async () => { const request: Client = createClient(url) for (const [method, path] of [ diff --git a/tests/server/users-controller.test.ts b/tests/server/users-controller.test.ts index e3647b546..6abeb0dfa 100644 --- a/tests/server/users-controller.test.ts +++ b/tests/server/users-controller.test.ts @@ -150,12 +150,38 @@ class Profiles extends UsersController { } } -// Members that patch any member, also their password: +// Members that create, update and patch any member: class Moderators extends UsersController { + override modelClass = Member + override collection: any = { + allow: ['post', 'put', 'patch'], + authorize: { post: true, put: true, patch: true } + } + + override member: any = { + allow: ['put', 'patch'], + authorize: { put: true, patch: true } + } +} + +// Members that relate existing members when posting them with ids: +class Relaters extends UsersController { + override modelClass = Member + override relate = true + override collection: any = { + allow: ['post'], + authorize: { post: true } + } +} + +// Members that delete themselves with confirmed access, as the docs show: +class Deleters extends UsersController { override modelClass = Member override member: any = { - allow: ['patch'], - authorize: { patch: true } + allow: ['delete'], + authorize: { + delete: (ctx: any) => this.hasConfirmedAccess(ctx) && '$self' + } } } @@ -192,7 +218,9 @@ describe('UsersController', () => { SubWriters, Editors, Profiles, - Moderators + Moderators, + Relaters, + Deleters } }) @@ -463,23 +491,6 @@ describe('UsersController', () => { expect((await request('/members/session')).data.authenticated).toBe(true) }) - it(`keeps the session that changes others' passwords`, async () => { - const request = createClient(url) - await request('/members/login', { - method: 'POST', - body: { username: 'ada', password: 'secret' } - }) - const response = await request(`/moderators/${other.id}`, { - method: 'PATCH', - body: { password: 'changed' } - }) - expect(response.status).toBe(200) - expect((await request('/members/session')).data).toMatchObject({ - authenticated: true, - user: { id: member.id } - }) - }) - it('passes on errors of the session query', async context => { context.skip(isSQLiteTest, 'SQLite compares text with integer ids') const request = await loginOther() @@ -566,22 +577,126 @@ describe('UsersController', () => { }) const self = await request('/members/self', { method: 'PATCH', - body: { nickname: 'Bobo', password: 'changed' } + body: { nickname: 'Bobo' } }) expect(self.status).toBe(200) expect(self.data).toMatchObject({ id: other.id, nickname: 'Bobo' }) expect(self.data).not.toHaveProperty('hash') expect((await Member.query().findById(member.id))!.nickname).toBeNull() - // Changing the own password keeps the session: - expect((await request('/members/session')).data.authenticated).toBe(true) - // The password is hashed like in other patches: + } finally { + await Member.query().deleteById(other.id) + } + }) + + it('only changes passwords through `post password`', async () => { + const request = createClient(url) + await request('/members/login', { + method: 'POST', + body: { username: 'ada', password: 'secret' } + }) + for (const [path, method, body] of [ + ['/members/self', 'PATCH', { nickname: 'Countess', password: 'changed' }], + [`/moderators/${member.id}`, 'PATCH', { password: 'changed' }], + [`/moderators/${member.id}`, 'PUT', { username: 'ada', password: 'x' }], + ['/moderators', 'PATCH', [{ id: member.id, password: 'changed' }]], + ['/moderators', 'PUT', [{ id: member.id, password: 'changed' }]] + ] as const) { + const response = await request(path, { method, body }) + expect(response.status).toBe(400) + expect(response.data).toEqual({ + type: 'BodyValidation', + message: `Unable to change 'password' outside of 'post password'` + }) + } + const stored = (await Member.query().findById(member.id))! + expect(stored.nickname).toBeNull() + expect(await stored.$verifyPassword('secret')).toBe(true) + }) + + it('never sets the hash of the password directly', async () => { + const request = createClient(url) + await request('/members/login', { + method: 'POST', + body: { username: 'ada', password: 'secret' } + }) + // A real hash, of the password 'changed': + const { hash } = Member.fromJson({ + username: 'ada', + password: 'changed' + } as any) + for (const [path, method, body] of [ + ['/members/self', 'PATCH', { hash }], + [`/moderators/${member.id}`, 'PUT', { username: 'ada', hash }], + ['/moderators', 'PATCH', [{ id: member.id, hash }]], + ['/moderators', 'PUT', [{ id: member.id, hash }]], + ['/moderators', 'POST', { username: 'cy', hash }] + ] as const) { + const response = await request(path, { method, body }) + expect(response.status).toBe(400) + expect(response.data).toEqual({ + type: 'BodyValidation', + message: `Unable to change 'hash' outside of 'post password'` + }) + } + const stored = (await Member.query().findById(member.id))! + expect(await stored.$verifyPassword('secret')).toBe(true) + expect(await Member.query().findOne('username', 'cy')).toBeUndefined() + }) + + it('validates related users with ids like updates', async () => { + // Without a login, so without confirmed access: + const request = createClient(url) + for (const [body, status] of [ + [{ id: member.id, hash: 'pwned' }, 400], + [{ id: member.id, password: 'changed' }, 400], + [{ id: member.id, username: 'eve' }, 403] + ] as const) { + const response = await request('/relaters', { method: 'POST', body }) + expect(response.status).toBe(status) + } + const stored = (await Member.query().findById(member.id))! + expect(stored.username).toBe('ada') + expect(await stored.$verifyPassword('secret')).toBe(true) + }) + + it('only lets users delete themselves with confirmed access', async () => { + const [cy, dee] = await Promise.all( + ['cy', 'dee'].map(username => + Member.query().insert({ username, password: 'secret' } as any) + ) + ) + try { + const request = createClient(url) + await request('/members/login', { + method: 'POST', + body: { username: 'cy', password: 'secret' } + }) + const other = await request(`/deleters/${dee.id}`, { method: 'DELETE' }) + expect(other.status).toBe(401) + const self = await request('/deleters/self', { method: 'DELETE' }) + expect(self.status).toBe(200) + expect(await Member.query().findById(cy.id)).toBeUndefined() + expect(await Member.query().findById(dee.id)).toBeDefined() + } finally { + await Member.query().whereIn('id', [cy.id, dee.id]).delete() + } + }) + + it('creates users with passwords', async () => { + const request = createClient(url) + const response = await request('/moderators', { + method: 'POST', + body: { username: 'cy', password: 'secret' } + }) + expect(response.status).toBe(201) + try { const login = await createClient(url)('/members/login', { method: 'POST', - body: { username: 'bo', password: 'changed' } + body: { username: 'cy', password: 'secret' } }) expect(login.data.success).toBe(true) } finally { - await Member.query().deleteById(other.id) + await Member.query().deleteById(response.data.id) } }) diff --git a/tests/types/server/application.test-d.ts b/tests/types/server/application.test-d.ts index 8ac6242bb..3f2bf22e4 100644 --- a/tests/types/server/application.test-d.ts +++ b/tests/types/server/application.test-d.ts @@ -87,4 +87,12 @@ describe('Application', () => { } }) }) + + it('accepts the confirmed access in app.auth', () => { + assertType({ + app: { + auth: { reconfirmAccessAfter: '5m' } + } + }) + }) })