fix: disable api key beta (#6021)
This commit is contained in:
@@ -16,8 +16,11 @@ const path = 'apiKey'
|
|||||||
const baseClass = 'api-key'
|
const baseClass = 'api-key'
|
||||||
const fieldBaseClass = 'field-type'
|
const fieldBaseClass = 'field-type'
|
||||||
|
|
||||||
export const APIKey: React.FC<{ readOnly?: boolean }> = ({ readOnly }) => {
|
export const APIKey: React.FC<{ enabled: boolean; readOnly?: boolean }> = ({
|
||||||
const [initialAPIKey, setInitialAPIKey] = useState(null)
|
enabled,
|
||||||
|
readOnly,
|
||||||
|
}) => {
|
||||||
|
const [initialAPIKey] = useState(uuidv4())
|
||||||
const [highlightedField, setHighlightedField] = useState(false)
|
const [highlightedField, setHighlightedField] = useState(false)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const config = useConfig()
|
const config = useConfig()
|
||||||
@@ -70,14 +73,13 @@ export const APIKey: React.FC<{ readOnly?: boolean }> = ({ readOnly }) => {
|
|||||||
const { setValue, value } = fieldType
|
const { setValue, value } = fieldType
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setInitialAPIKey(uuidv4())
|
if (!apiKeyValue && enabled) {
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!apiKeyValue) {
|
|
||||||
setValue(initialAPIKey)
|
setValue(initialAPIKey)
|
||||||
}
|
}
|
||||||
}, [apiKeyValue, setValue, initialAPIKey])
|
if (!enabled) {
|
||||||
|
setValue(null)
|
||||||
|
}
|
||||||
|
}, [apiKeyValue, enabled, setValue, initialAPIKey])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (highlightedField) {
|
if (highlightedField) {
|
||||||
@@ -87,6 +89,10 @@ export const APIKey: React.FC<{ readOnly?: boolean }> = ({ readOnly }) => {
|
|||||||
}
|
}
|
||||||
}, [highlightedField])
|
}, [highlightedField])
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className={[fieldBaseClass, 'api-key', 'read-only'].filter(Boolean).join(' ')}>
|
<div className={[fieldBaseClass, 'api-key', 'read-only'].filter(Boolean).join(' ')}>
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export const Auth: React.FC<Props> = (props) => {
|
|||||||
name="enableAPIKey"
|
name="enableAPIKey"
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
/>
|
/>
|
||||||
{enableAPIKey?.value && <APIKey readOnly={readOnly} />}
|
<APIKey enabled={!!enableAPIKey?.value} readOnly={readOnly} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{verify && (
|
{verify && (
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export default [
|
|||||||
Field: () => null,
|
Field: () => null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultValue: false,
|
|
||||||
label: ({ t }) => t('authentication:enableAPIKey'),
|
label: ({ t }) => t('authentication:enableAPIKey'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -44,15 +43,18 @@ export default [
|
|||||||
hooks: {
|
hooks: {
|
||||||
beforeValidate: [
|
beforeValidate: [
|
||||||
({ data, req, value }) => {
|
({ data, req, value }) => {
|
||||||
|
if (data.apiKey === false || data.apiKey === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (data.enableAPIKey === false || data.enableAPIKey === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
if (data.apiKey) {
|
if (data.apiKey) {
|
||||||
return crypto
|
return crypto
|
||||||
.createHmac('sha1', req.payload.secret)
|
.createHmac('sha1', req.payload.secret)
|
||||||
.update(data.apiKey as string)
|
.update(data.apiKey as string)
|
||||||
.digest('hex')
|
.digest('hex')
|
||||||
}
|
}
|
||||||
if (data.enableAPIKey === false) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return value
|
return value
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ describe('auth', () => {
|
|||||||
user = await payload.create({
|
user = await payload.create({
|
||||||
collection: apiKeysSlug,
|
collection: apiKeysSlug,
|
||||||
data: {
|
data: {
|
||||||
|
apiKey: uuid(),
|
||||||
enableAPIKey: true,
|
enableAPIKey: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -140,7 +141,7 @@ describe('auth', () => {
|
|||||||
const response = await fetch(`${apiURL}/${apiKeysSlug}/me`, {
|
const response = await fetch(`${apiURL}/${apiKeysSlug}/me`, {
|
||||||
headers: {
|
headers: {
|
||||||
...headers,
|
...headers,
|
||||||
Authorization: `${slug} API-Key ${user.apiKey}`,
|
Authorization: `${apiKeysSlug} API-Key ${user.apiKey}`,
|
||||||
},
|
},
|
||||||
}).then((res) => res.json())
|
}).then((res) => res.json())
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ import type { Payload } from 'payload'
|
|||||||
import type { User } from 'payload/auth'
|
import type { User } from 'payload/auth'
|
||||||
|
|
||||||
import { jwtDecode } from 'jwt-decode'
|
import { jwtDecode } from 'jwt-decode'
|
||||||
|
import { v4 as uuid } from 'uuid'
|
||||||
|
|
||||||
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
|
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
|
||||||
|
|
||||||
import { devUser } from '../credentials.js'
|
import { devUser } from '../credentials.js'
|
||||||
import { initPayloadInt } from '../helpers/initPayloadInt.js'
|
import { initPayloadInt } from '../helpers/initPayloadInt.js'
|
||||||
import configPromise from './config.js'
|
import configPromise from './config.js'
|
||||||
import { namedSaveToJWTValue, saveToJWTKey, slug } from './shared.js'
|
import { apiKeysSlug, namedSaveToJWTValue, saveToJWTKey, slug } from './shared.js'
|
||||||
|
|
||||||
let restClient: NextRESTClient
|
let restClient: NextRESTClient
|
||||||
let payload: Payload
|
let payload: Payload
|
||||||
@@ -622,6 +623,99 @@ describe('Auth', () => {
|
|||||||
|
|
||||||
expect(fail.status).toStrictEqual(404)
|
expect(fail.status).toStrictEqual(404)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not remove an API key from a user when updating other fields', async () => {
|
||||||
|
const apiKey = uuid()
|
||||||
|
const user = await payload.create({
|
||||||
|
collection: 'api-keys',
|
||||||
|
data: {
|
||||||
|
apiKey,
|
||||||
|
enableAPIKey: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatedUser = await payload.update({
|
||||||
|
id: user.id,
|
||||||
|
collection: 'api-keys',
|
||||||
|
data: {
|
||||||
|
enableAPIKey: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const userResult = await payload.find({
|
||||||
|
collection: 'api-keys',
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
equals: user.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(updatedUser.apiKey).toStrictEqual(user.apiKey)
|
||||||
|
expect(userResult.docs[0].apiKey).toStrictEqual(user.apiKey)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should disable api key after updating apiKey: null', async () => {
|
||||||
|
const apiKey = uuid()
|
||||||
|
const user = await payload.create({
|
||||||
|
collection: apiKeysSlug,
|
||||||
|
data: {
|
||||||
|
apiKey,
|
||||||
|
enableAPIKey: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatedUser = await payload.update({
|
||||||
|
id: user.id,
|
||||||
|
collection: apiKeysSlug,
|
||||||
|
data: {
|
||||||
|
apiKey: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// use the api key in a fetch to assert that it is disabled
|
||||||
|
const response = await restClient
|
||||||
|
.GET(`/api-keys/me`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `${apiKeysSlug} API-Key ${apiKey}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
|
||||||
|
expect(updatedUser.apiKey).toBeNull()
|
||||||
|
expect(response.user).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should disable api key after updating with enableAPIKey:false', async () => {
|
||||||
|
const apiKey = uuid()
|
||||||
|
const user = await payload.create({
|
||||||
|
collection: apiKeysSlug,
|
||||||
|
data: {
|
||||||
|
apiKey,
|
||||||
|
enableAPIKey: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatedUser = await payload.update({
|
||||||
|
id: user.id,
|
||||||
|
collection: apiKeysSlug,
|
||||||
|
data: {
|
||||||
|
enableAPIKey: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// use the api key in a fetch to assert that it is disabled
|
||||||
|
const response = await restClient
|
||||||
|
.GET(`/api-keys/me`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `${apiKeysSlug} API-Key ${apiKey}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
|
||||||
|
expect(updatedUser.apiKey).toStrictEqual(apiKey)
|
||||||
|
expect(response.user).toBeNull()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Local API', () => {
|
describe('Local API', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user