Compare commits
15 Commits
templates/
...
chore/plug
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69ae87b866 | ||
|
|
9918ab0cdd | ||
|
|
38258d575e | ||
|
|
1eac936008 | ||
|
|
a3b3cb38cd | ||
|
|
b6792b7855 | ||
|
|
ac67fdc3d6 | ||
|
|
a7b2638d52 | ||
|
|
850af9b1a8 | ||
|
|
7d0603046d | ||
|
|
37b2abf0e6 | ||
|
|
d615ff826a | ||
|
|
dec5180bca | ||
|
|
0dea38dfa4 | ||
|
|
045657d918 |
10
packages/plugin-password-protection/.editorconfig
Normal file
10
packages/plugin-password-protection/.editorconfig
Normal file
@@ -0,0 +1,10 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
max_line_length = null
|
||||
3
packages/plugin-password-protection/.eslintrc.js
Normal file
3
packages/plugin-password-protection/.eslintrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ['@payloadcms'],
|
||||
}
|
||||
3
packages/plugin-password-protection/.gitignore
vendored
Normal file
3
packages/plugin-password-protection/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
.env
|
||||
dist
|
||||
8
packages/plugin-password-protection/.prettierrc.js
Normal file
8
packages/plugin-password-protection/.prettierrc.js
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
printWidth: 100,
|
||||
parser: "typescript",
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
trailingComma: "all",
|
||||
arrowParens: "avoid",
|
||||
};
|
||||
51
packages/plugin-password-protection/README.md
Normal file
51
packages/plugin-password-protection/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Payload Password Protection Plugin
|
||||
|
||||
[](https://www.npmjs.com/package/payload-plugin-password-protection)
|
||||
|
||||
A plugin for [Payload](https://github.com/payloadcms/payload) to easily allow for documents to be secured behind a layer of password protection.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
yarn add payload-plugin-password-protection
|
||||
# OR
|
||||
npm i payload-plugin-password-protection
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
In the `plugins` array of your [Payload config](https://payloadcms.com/docs/configuration/overview), call the plugin with [options](#options):
|
||||
|
||||
```js
|
||||
import { buildConfig } from 'payload/config';
|
||||
import passwordProtection from 'payload-plugin-password-protection';
|
||||
|
||||
const config = buildConfig({
|
||||
collections: [
|
||||
plugins: [
|
||||
passwordProtection({
|
||||
collections: ['pages'],
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
export default config;
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
#### `collections`
|
||||
|
||||
An array of collections slugs to enable password protection.
|
||||
|
||||
## TypeScript
|
||||
|
||||
All types can be directly imported:
|
||||
|
||||
```js
|
||||
import { PasswordProtectionConfig } from "payload-plugin-password-protection/dist/types";
|
||||
```
|
||||
|
||||
## Screenshots
|
||||
|
||||
<!--  -->
|
||||
4
packages/plugin-password-protection/demo/nodemon.json
Normal file
4
packages/plugin-password-protection/demo/nodemon.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"ext": "ts",
|
||||
"exec": "ts-node src/server.ts"
|
||||
}
|
||||
27
packages/plugin-password-protection/demo/package.json
Normal file
27
packages/plugin-password-protection/demo/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "payload-starter-typescript",
|
||||
"description": "Blank template - no collections",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/server.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
|
||||
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
|
||||
"build:server": "tsc",
|
||||
"build": "yarn build:payload && yarn build:server",
|
||||
"serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js",
|
||||
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"payload": "^1.8.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.9",
|
||||
"cross-env": "^7.0.3",
|
||||
"nodemon": "^2.0.6",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.1.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// const payload = require('payload');
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
labels: {
|
||||
singular: 'Page',
|
||||
plural: 'Pages',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
label: 'Slug',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Users: CollectionConfig = {
|
||||
slug: 'users',
|
||||
auth: true,
|
||||
admin: {
|
||||
useAsTitle: 'email',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
// Email added by default
|
||||
// Add more fields as needed
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import path from 'path'
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
// import passwordProtection from '../../dist';
|
||||
import passwordProtection from '../../src'
|
||||
import { Pages } from './collections/Pages'
|
||||
import { Users } from './collections/Users'
|
||||
|
||||
export default buildConfig({
|
||||
serverURL: 'http://localhost:3000',
|
||||
admin: {
|
||||
user: Users.slug,
|
||||
webpack: config => {
|
||||
const newConfig = {
|
||||
...config,
|
||||
resolve: {
|
||||
...config.resolve,
|
||||
alias: {
|
||||
...config.resolve.alias,
|
||||
react: path.join(__dirname, '../node_modules/react'),
|
||||
'react-dom': path.join(__dirname, '../node_modules/react-dom'),
|
||||
payload: path.join(__dirname, '../node_modules/payload'),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return newConfig
|
||||
},
|
||||
},
|
||||
collections: [Users, Pages],
|
||||
plugins: [
|
||||
passwordProtection({
|
||||
collections: ['pages'],
|
||||
}),
|
||||
],
|
||||
typescript: {
|
||||
outputFile: path.resolve(__dirname, 'payload-types.ts'),
|
||||
},
|
||||
})
|
||||
28
packages/plugin-password-protection/demo/src/server.ts
Normal file
28
packages/plugin-password-protection/demo/src/server.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import dotenv from 'dotenv'
|
||||
dotenv.config()
|
||||
|
||||
import express from 'express'
|
||||
import payload from 'payload'
|
||||
|
||||
const app = express()
|
||||
|
||||
// Redirect root to Admin panel
|
||||
app.get('/', (_, res) => {
|
||||
res.redirect('/admin')
|
||||
})
|
||||
|
||||
// Initialize Payload
|
||||
const start = async (): Promise<void> => {
|
||||
await payload.init({
|
||||
secret: process.env.PAYLOAD_SECRET,
|
||||
mongoURL: process.env.MONGODB_URI,
|
||||
express: app,
|
||||
onInit: () => {
|
||||
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`)
|
||||
},
|
||||
})
|
||||
|
||||
app.listen(3000)
|
||||
}
|
||||
|
||||
start()
|
||||
19
packages/plugin-password-protection/demo/tsconfig.json
Normal file
19
packages/plugin-password-protection/demo/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "../",
|
||||
"jsx": "react",
|
||||
},
|
||||
"ts-node": {
|
||||
"transpileOnly": true
|
||||
}
|
||||
}
|
||||
6581
packages/plugin-password-protection/demo/yarn.lock
Normal file
6581
packages/plugin-password-protection/demo/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
56
packages/plugin-password-protection/package.json
Normal file
56
packages/plugin-password-protection/package.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "payload-plugin-password-protection",
|
||||
"version": "0.0.2",
|
||||
"description": "Password protection plugin for Payload",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint --fix --ext .ts,.tsx src"
|
||||
},
|
||||
"keywords": [
|
||||
"payload",
|
||||
"cms",
|
||||
"plugin",
|
||||
"typescript",
|
||||
"react",
|
||||
"password",
|
||||
"protection",
|
||||
"pages"
|
||||
],
|
||||
"author": "dev@trbl.design",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"payload": "^0.15.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "^0.0.1",
|
||||
"@types/escape-html": "^1.0.1",
|
||||
"@types/express": "^4.17.9",
|
||||
"@types/node": "18.11.3",
|
||||
"@types/react": "18.0.21",
|
||||
"@typescript-eslint/eslint-plugin": "^5.51.0",
|
||||
"@typescript-eslint/parser": "^5.51.0",
|
||||
"copyfiles": "^2.4.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.19.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-filenames": "^1.3.2",
|
||||
"eslint-plugin-import": "2.25.4",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"nodemon": "^2.0.6",
|
||||
"payload": "^1.8.2",
|
||||
"prettier": "^2.7.1",
|
||||
"react": "^18.0.0",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.8.4"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
150
packages/plugin-password-protection/src/index.ts
Normal file
150
packages/plugin-password-protection/src/index.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import path from 'path'
|
||||
import type { Config } from 'payload/config'
|
||||
import type { CollectionConfig } from 'payload/dist/collections/config/types'
|
||||
import type { CollectionBeforeReadHook } from 'payload/types'
|
||||
|
||||
import type { PasswordProtectionConfig } from './types'
|
||||
import getCookiePrefix from './utilities/getCookiePrefix'
|
||||
import getMutation from './utilities/getMutation'
|
||||
import getRouter from './utilities/getRouter'
|
||||
import parseCookies from './utilities/parseCookies'
|
||||
|
||||
const collectionPasswords =
|
||||
(incomingOptions: PasswordProtectionConfig) =>
|
||||
(incomingConfig: Config): Config => {
|
||||
const { collections } = incomingOptions
|
||||
|
||||
const options = {
|
||||
collections,
|
||||
routePath: incomingOptions.routePath || '/validate-password',
|
||||
expiration: incomingOptions.expiration || 7200,
|
||||
whitelistUsers:
|
||||
incomingOptions.whitelistUsers ||
|
||||
(({ payloadAPI, user }) => Boolean(user) || payloadAPI === 'local'),
|
||||
passwordFieldName: incomingOptions.passwordFieldName || 'docPassword',
|
||||
passwordProtectedFieldName: incomingOptions.passwordFieldName || 'passwordProtected',
|
||||
mutationName: incomingOptions.mutationName || 'validatePassword',
|
||||
}
|
||||
|
||||
const config: Config = {
|
||||
...incomingConfig,
|
||||
graphQL: {
|
||||
...incomingConfig.graphQL,
|
||||
mutations: (GraphQL, payload) => ({
|
||||
...(typeof incomingConfig?.graphQL?.mutations === 'function'
|
||||
? incomingConfig.graphQL.mutations(GraphQL, payload)
|
||||
: {}),
|
||||
[options.mutationName]: getMutation(GraphQL, payload, incomingConfig, options),
|
||||
}),
|
||||
},
|
||||
express: {
|
||||
...incomingConfig?.express,
|
||||
middleware: [
|
||||
...(incomingConfig?.express?.middleware || []),
|
||||
getRouter(incomingConfig, options),
|
||||
],
|
||||
},
|
||||
admin: {
|
||||
...incomingConfig.admin,
|
||||
webpack: webpackConfig => {
|
||||
let newWebpackConfig = { ...webpackConfig }
|
||||
if (typeof incomingConfig?.admin?.webpack === 'function')
|
||||
newWebpackConfig = incomingConfig.admin.webpack(webpackConfig)
|
||||
|
||||
const webpackMock = path.resolve(__dirname, './utilities/webpackMock.js')
|
||||
|
||||
return {
|
||||
...newWebpackConfig,
|
||||
resolve: {
|
||||
...newWebpackConfig.resolve,
|
||||
alias: {
|
||||
...(newWebpackConfig?.resolve?.alias || {}),
|
||||
[path.resolve(__dirname, 'utilities/getRouter')]: webpackMock,
|
||||
[path.resolve(__dirname, 'utilities/getMutation')]: webpackMock,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
config.collections =
|
||||
config?.collections?.map(collectionConfig => {
|
||||
if (collections?.includes(collectionConfig.slug)) {
|
||||
const cookiePrefix = getCookiePrefix(config.cookiePrefix || '', collectionConfig.slug)
|
||||
|
||||
const beforeReadHook: CollectionBeforeReadHook = async ({ req, doc }) => {
|
||||
const whitelistUsersResponse =
|
||||
typeof options.whitelistUsers === 'function'
|
||||
? await options.whitelistUsers(req)
|
||||
: false
|
||||
|
||||
if (!doc[options.passwordFieldName] || whitelistUsersResponse) return doc
|
||||
|
||||
const cookies = parseCookies(req)
|
||||
const cookiePassword = cookies[`${cookiePrefix}-${doc.id}`]
|
||||
|
||||
if (cookiePassword === doc[options.passwordFieldName]) {
|
||||
return doc
|
||||
}
|
||||
|
||||
return {
|
||||
id: doc.id,
|
||||
[options.passwordProtectedFieldName]: true,
|
||||
}
|
||||
}
|
||||
|
||||
const collectionWithPasswordProtection: CollectionConfig = {
|
||||
...collectionConfig,
|
||||
hooks: {
|
||||
...collectionConfig?.hooks,
|
||||
beforeRead: [...(collectionConfig?.hooks?.beforeRead || []), beforeReadHook],
|
||||
},
|
||||
fields: [
|
||||
...collectionConfig?.fields.map(field => {
|
||||
const newField = { ...field }
|
||||
newField.admin = {
|
||||
...newField.admin,
|
||||
condition: (data, siblingData) => {
|
||||
const existingConditionResult = field?.admin?.condition
|
||||
? field.admin.condition(data, siblingData)
|
||||
: true
|
||||
return data?.[options.passwordProtectedFieldName]
|
||||
? false
|
||||
: existingConditionResult
|
||||
},
|
||||
}
|
||||
|
||||
return newField
|
||||
}),
|
||||
{
|
||||
name: options.passwordFieldName,
|
||||
label: 'Password',
|
||||
type: 'text',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: options.passwordProtectedFieldName,
|
||||
type: 'checkbox',
|
||||
hooks: {
|
||||
beforeChange: [({ value }) => (value ? null : undefined)],
|
||||
},
|
||||
admin: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
return collectionWithPasswordProtection
|
||||
}
|
||||
|
||||
return collectionConfig
|
||||
}) || []
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
export default collectionPasswords
|
||||
23
packages/plugin-password-protection/src/types.ts
Normal file
23
packages/plugin-password-protection/src/types.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { PayloadRequest } from 'payload/dist/express/types'
|
||||
|
||||
export type AllowUsers = (req: PayloadRequest) => Promise<boolean> | boolean
|
||||
|
||||
export interface PasswordProtectionConfig {
|
||||
passwordFieldName?: string
|
||||
passwordProtectedFieldName?: string
|
||||
whitelistUsers?: AllowUsers
|
||||
routePath?: string
|
||||
mutationName?: string
|
||||
expiration?: number
|
||||
collections?: string[]
|
||||
}
|
||||
|
||||
export interface PasswordProtectionOptions {
|
||||
collections?: string[]
|
||||
routePath: string
|
||||
expiration: number
|
||||
whitelistUsers: AllowUsers
|
||||
passwordFieldName: string
|
||||
passwordProtectedFieldName: string
|
||||
mutationName: string
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export default (cookiePrefix: string, collectionSlug: string): string =>
|
||||
`${cookiePrefix}-${collectionSlug}-password-`
|
||||
@@ -0,0 +1,63 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import type { Response } from 'express'
|
||||
import type { GraphQLFieldConfig } from 'graphql'
|
||||
import type GraphQL from 'graphql'
|
||||
import type { Payload } from 'payload'
|
||||
import type { Config } from 'payload/config'
|
||||
import type { PayloadRequest } from 'payload/dist/express/types'
|
||||
|
||||
import type { PasswordProtectionOptions } from '../types'
|
||||
import operation from './operation'
|
||||
|
||||
interface Args {
|
||||
collection: string
|
||||
password: string
|
||||
id: string
|
||||
}
|
||||
|
||||
type MutationType = GraphQLFieldConfig<void, { req: PayloadRequest; res: Response }, Args>
|
||||
|
||||
const getMutation = (
|
||||
GraphQLArg: typeof GraphQL,
|
||||
payload: Payload,
|
||||
config: Config,
|
||||
options: PasswordProtectionOptions,
|
||||
): MutationType => {
|
||||
const { GraphQLBoolean, GraphQLString, GraphQLNonNull } = GraphQLArg
|
||||
|
||||
return {
|
||||
type: GraphQLBoolean,
|
||||
args: {
|
||||
collection: {
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
},
|
||||
password: {
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
},
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
},
|
||||
},
|
||||
resolve: async (_, args, context) => {
|
||||
const { collection, password, id } = args
|
||||
|
||||
try {
|
||||
await operation({
|
||||
config,
|
||||
payload,
|
||||
options,
|
||||
collection,
|
||||
password,
|
||||
id,
|
||||
res: context.res,
|
||||
})
|
||||
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default getMutation
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { Router } from 'express'
|
||||
import express from 'express'
|
||||
import type { Config as PayloadConfig } from 'payload/config'
|
||||
import type { PayloadRequest } from 'payload/dist/express/types'
|
||||
|
||||
import type { PasswordProtectionOptions } from '../types'
|
||||
import operation from './operation'
|
||||
|
||||
export default (config: PayloadConfig, options: PasswordProtectionOptions): Router => {
|
||||
const router = express.Router()
|
||||
|
||||
// TODO: the second argument of router.post() needs to be typed correctly
|
||||
// @ts-expect-error
|
||||
router.post(options.routePath || '/validate-password', async (req: PayloadRequest, res) => {
|
||||
try {
|
||||
const { body: { collection, password, id } = {}, payload } = req
|
||||
|
||||
await operation({
|
||||
config,
|
||||
payload,
|
||||
options,
|
||||
collection,
|
||||
password,
|
||||
id,
|
||||
res,
|
||||
})
|
||||
|
||||
res.status(200).send()
|
||||
} catch (e: unknown) {
|
||||
res.status(401).send()
|
||||
}
|
||||
})
|
||||
|
||||
return router
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { Response } from 'express'
|
||||
import type { Payload } from 'payload'
|
||||
import type { Config as PayloadConfig } from 'payload/config'
|
||||
import APIError from 'payload/dist/errors/APIError'
|
||||
|
||||
import type { PasswordProtectionOptions } from '../types'
|
||||
import getCookiePrefix from './getCookiePrefix'
|
||||
|
||||
interface Args {
|
||||
config: PayloadConfig
|
||||
payload: Payload
|
||||
options: PasswordProtectionOptions
|
||||
collection: string
|
||||
password: string
|
||||
id: string
|
||||
res: Response
|
||||
}
|
||||
|
||||
const validatePassword = async ({
|
||||
config,
|
||||
payload,
|
||||
options,
|
||||
collection,
|
||||
password,
|
||||
id,
|
||||
res,
|
||||
}: Args): Promise<void> => {
|
||||
const doc = await payload.findByID({
|
||||
id,
|
||||
collection,
|
||||
})
|
||||
|
||||
if (doc[options.passwordFieldName] === password) {
|
||||
const expires = new Date()
|
||||
expires.setSeconds(expires.getSeconds() + options.expiration || 7200)
|
||||
|
||||
const cookiePrefix = getCookiePrefix(config.cookiePrefix || '', collection)
|
||||
|
||||
const cookieOptions = {
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
expires,
|
||||
domain: undefined,
|
||||
}
|
||||
|
||||
res.cookie(`${cookiePrefix}-${id}`, password, cookieOptions)
|
||||
return
|
||||
}
|
||||
|
||||
throw new APIError('The password provided is incorrect.', 400)
|
||||
}
|
||||
|
||||
export default validatePassword
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { Request } from 'express'
|
||||
|
||||
function parseCookies(req: Request): { [key: string]: string } {
|
||||
const list: { [key: string]: any } = {}
|
||||
const rc = req.headers.cookie
|
||||
|
||||
if (rc) {
|
||||
rc.split(';').forEach(cookie => {
|
||||
const parts = cookie.split('=')
|
||||
const keyToUse = parts.shift()?.trim() || ''
|
||||
list[keyToUse] = decodeURI(parts.join('='))
|
||||
})
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
export default parseCookies
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = () => config => config
|
||||
18
packages/plugin-password-protection/tsconfig.json
Normal file
18
packages/plugin-password-protection/tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"outDir": "./dist",
|
||||
"allowJs": true,
|
||||
"module": "commonjs",
|
||||
"sourceMap": true,
|
||||
"jsx": "react",
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"declarationDir": "./dist",
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
}
|
||||
10261
packages/plugin-password-protection/yarn-error.log
Normal file
10261
packages/plugin-password-protection/yarn-error.log
Normal file
File diff suppressed because it is too large
Load Diff
6851
packages/plugin-password-protection/yarn.lock
Normal file
6851
packages/plugin-password-protection/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user