-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: support queries to generate self-service api-keys #428
Conversation
readonly ACTIVE: 'ACTIVE'; | ||
readonly INACTIVE: 'INACTIVE'; | ||
}; | ||
|
||
// TODO: introduce AuditType interface or Scores interface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intentional TODO?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like it was already present in the codebase.
@@ -29,6 +29,8 @@ export const ApiKeyDto = { | |||
createdAt: apiKey.getCreatedAt(), | |||
expiresAt: apiKey.getExpiresAt(), | |||
revokedAt: apiKey.getRevokedAt(), | |||
deletedAt: apiKey.getDeletedAt(), | |||
status: apiKey.getStatus(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does status
represent? A sample API Key object in the PR description with details on each field would be helpful.
readonly ACTIVE: 'ACTIVE'; | ||
readonly INACTIVE: 'INACTIVE'; | ||
}; | ||
|
||
// TODO: introduce AuditType interface or Scores interface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like it was already present in the codebase.
getApiKeyByImsUserIdAndImsOrgId: ( | ||
imsUserId: string, | ||
imsOrgId: string, | ||
) => Promise<ApiKey | null>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if a user has more than 1 API key? IIRC, we were going to allow 3 per IMS User ID, so this query could return up to 3 results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Typo for the interface. I'll fix it.
packages/spacecat-shared-data-access/src/models/api-key/api-key-constants.js
Outdated
Show resolved
Hide resolved
* Api Key Status Types | ||
* Any changes to this object needs to be reflected in the index.d.ts file as well. | ||
*/ | ||
export const ApiKeyStatus = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we depend on this status property? For instance, if an API key "expires" by the passing of time but its entity in the DB is not updated to reflect that it is now INACTIVE
, is the key still considered ACTIVE
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The key is still considered ACTIVE
in that case. It is updated to INACTIVE
when keys are deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just thinking out loud: would we be any worse off if we didn't have the status
column? We'll have to check the deletedAt
, revokedAt
, and expiresAt
anyways to determine if a key is still valid, so status
seems potentially unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I could remove status for the api-key.
*/ | ||
self.updateDeletedAt = (deletedAt) => updateState('deletedAt', deletedAt, (value) => { | ||
if (!isIsoDate(value)) { | ||
throw new Error(`Invalid deletedAt during update: ${value}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth mentioning the expected format in this error?
*/ | ||
export const createNewApiKey = async (apiKey, dynamoClient, config, log) => { | ||
const item = ApiKeyDto.toDynamoItem(apiKey); | ||
await dynamoClient.putItem(config.tableNameApiKeys, item, log); | ||
const item = createApiKey(apiKey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this function was previously invoked with an API key object. Has it changed to accept a POJO instead? If so, the JSDoc should be updated to reflect that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we were not using this method before. I refactored it to work similar to how import-job works now for consistency.
Co-authored-by: Bruce Lefebvre <[email protected]>
…y-constants.js Co-authored-by: Bruce Lefebvre <[email protected]>
This PR will trigger a minor release when merged. |
"AttributeType": "S" | ||
}, | ||
{ | ||
"AttributeName": "status", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Status is still present here.
* imsOrgId: *, expiresAt: *}} | ||
* @returns {{createdAt: string, name: string, imsUserId: string, | ||
* scopes: array<object>, revokedAt: string, deletedAt: string, | ||
* hashedApiKey: *, imsOrgId: *, expiresAt: *}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add types.
packages/spacecat-shared-data-access/src/service/api-key/index.js
Outdated
Show resolved
Hide resolved
self.getScopes = () => self.state.scopes; | ||
|
||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this model be the right place for an isValid()
method, which could check all the internal properties to ensure that a key is still valid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or isActive()
, alternatively?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could do that in the service. Do we need it in spacecat-shared?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do so in the service than anywhere that re-uses this model will need to implement something similar. I see it as a direct concern of the model itself, "am I still valid", and think that it makes sense to have that logic live alongside the model.
I also like the pattern of "asking" the entity itself, are you still valid: if (apiKeyInstance.isValid()) {
— nice and clean.
Co-authored-by: Bruce Lefebvre <[email protected]>
self.isValid = () => { | ||
const now = new Date(); | ||
|
||
if (self.state.deletedAt && new Date(self.state.deletedAt) > now) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this comparison correct? If deletedAt is > now, that would mean it was deleted in the future — right? And therefore still valid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will be deleted in future. So, the key is still valid.
if (self.state.revokedAt && new Date(self.state.revokedAt) > now) { | ||
return false; | ||
} | ||
|
||
if (self.state.expiresAt && new Date(self.state.expiresAt) > now) { | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue with these two, I think.
If an expiry is set when the key is created (as it must) than it will always be in the future, which would mean that this check fails and the key is not considered valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
}); | ||
|
||
it('returns false if deletedAt is after the current time', () => { | ||
apiKey.updateDeletedAt(new Date(Date.now() + 10000).toISOString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can concrete values please be used for these test cases so they are easier for a human to reason about?
ie. if a key expires in 2025 and it is 2024, it should still be valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
expect(apiKey.isValid()).to.be.false; | ||
}); | ||
|
||
it('returns true if deletedAt, revokedAt, and expiresAt are before the current time', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the core of the issue, I think.
- if expiresAt is in the future: still valid
- if deletedAt or revokedAt are in the past: no longer valid
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In other words: expiresAt is the only property we should expect to be in the future. If ANY of these properties are set to a date before now
, the key should not be considered valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor item about dates in test cases.
apiKey.updateDeletedAt('2025-05-27T14:26:00.000Z'); | ||
apiKey.updateRevokedAt('2025-05-27T14:26:00.000Z'); | ||
apiKey.updateExpiresAt('2025-05-27T14:26:00.000Z'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test will start failing next May :)
Can we bump the year to 3025 please?
# [@adobe/spacecat-shared-data-access-v1.50.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.49.8...@adobe/spacecat-shared-data-access-v1.50.0) (2024-11-08) ### Features * support queries to generate self-service api-keys ([#428](#428)) ([90a46c2](90a46c2))
🎉 This PR is included in version @adobe/spacecat-shared-data-access-v1.50.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Related Issues
SITES-25915
Supporting the following queries with this PR:
getApiKeysByImsUserIdAndImsOrgId
getApiKeyById
updateApiKey
Sample apiKey entry in the DB: