-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #193 from IQSS/190-api-tokens
Api token management use cases
- Loading branch information
Showing
20 changed files
with
487 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface ApiTokenInfo { | ||
apiToken: string | ||
expirationDate: Date | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,9 @@ | ||
import { ApiTokenInfo } from '../models/ApiTokenInfo' | ||
import { AuthenticatedUser } from '../models/AuthenticatedUser' | ||
|
||
export interface IUsersRepository { | ||
getCurrentAuthenticatedUser(): Promise<AuthenticatedUser> | ||
recreateCurrentApiToken(): Promise<ApiTokenInfo> | ||
getCurrentApiToken(): Promise<ApiTokenInfo> | ||
deleteCurrentApiToken(): Promise<void> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { UseCase } from '../../../core/domain/useCases/UseCase' | ||
import { IUsersRepository } from '../repositories/IUsersRepository' | ||
|
||
export class DeleteCurrentApiToken implements UseCase<void> { | ||
private usersRepository: IUsersRepository | ||
|
||
constructor(usersRepository: IUsersRepository) { | ||
this.usersRepository = usersRepository | ||
} | ||
|
||
/** | ||
* Deletes the API token of the current authenticated user. | ||
* | ||
* @returns {Promise<void>} | ||
*/ | ||
async execute(): Promise<void> { | ||
return await this.usersRepository.deleteCurrentApiToken() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { UseCase } from '../../../core/domain/useCases/UseCase' | ||
import { ApiTokenInfo } from '../models/ApiTokenInfo' | ||
import { IUsersRepository } from '../repositories/IUsersRepository' | ||
|
||
export class GetCurrentApiToken implements UseCase<ApiTokenInfo> { | ||
private usersRepository: IUsersRepository | ||
|
||
constructor(usersRepository: IUsersRepository) { | ||
this.usersRepository = usersRepository | ||
} | ||
|
||
/** | ||
* Returns the current ApiTokenInfo corresponding to the current authenticated user. | ||
* | ||
* @returns {Promise<ApiTokenInfo>} | ||
*/ | ||
async execute(): Promise<ApiTokenInfo> { | ||
return await this.usersRepository.getCurrentApiToken() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { UseCase } from '../../../core/domain/useCases/UseCase' | ||
import { ApiTokenInfo } from '../models/ApiTokenInfo' | ||
import { IUsersRepository } from '../repositories/IUsersRepository' | ||
|
||
export class RecreateCurrentApiToken implements UseCase<ApiTokenInfo> { | ||
private usersRepository: IUsersRepository | ||
|
||
constructor(usersRepository: IUsersRepository) { | ||
this.usersRepository = usersRepository | ||
} | ||
|
||
/** | ||
* Reacreates the API token of the current authenticated user and returns the new ApiTokenInfo. | ||
* | ||
* @returns {Promise<ApiTokenInfo>} | ||
*/ | ||
async execute(): Promise<ApiTokenInfo> { | ||
return await this.usersRepository.recreateCurrentApiToken() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,21 @@ | ||
import { UsersRepository } from './infra/repositories/UsersRepository' | ||
import { GetCurrentAuthenticatedUser } from './domain/useCases/GetCurrentAuthenticatedUser' | ||
import { RecreateCurrentApiToken } from './domain/useCases/RecreateCurrentApiToken' | ||
import { GetCurrentApiToken } from './domain/useCases/GetCurrentApiToken' | ||
import { DeleteCurrentApiToken } from './domain/useCases/DeleteCurrentApiToken' | ||
|
||
const getCurrentAuthenticatedUser = new GetCurrentAuthenticatedUser(new UsersRepository()) | ||
const usersRepository = new UsersRepository() | ||
|
||
export { getCurrentAuthenticatedUser } | ||
const getCurrentAuthenticatedUser = new GetCurrentAuthenticatedUser(usersRepository) | ||
const recreateCurrentApiToken = new RecreateCurrentApiToken(usersRepository) | ||
const getCurrentApiToken = new GetCurrentApiToken(usersRepository) | ||
const deleteCurrentApiToken = new DeleteCurrentApiToken(usersRepository) | ||
|
||
export { | ||
getCurrentAuthenticatedUser, | ||
recreateCurrentApiToken, | ||
getCurrentApiToken, | ||
deleteCurrentApiToken | ||
} | ||
export { AuthenticatedUser } from './domain/models/AuthenticatedUser' | ||
export { ApiTokenInfo } from './domain/models/ApiTokenInfo' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
src/users/infra/repositories/transformers/apiTokenInfoTransformers.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { AxiosResponse } from 'axios' | ||
import { ApiTokenInfo } from '../../../domain/models/ApiTokenInfo' | ||
|
||
export const transformGetApiTokenResponseToApiTokenInfo = ( | ||
response: AxiosResponse | ||
): ApiTokenInfo => { | ||
const apiTokenInfoPayload = response.data.data | ||
const messageParts = apiTokenInfoPayload.message.split(' ') | ||
const expirationDateFormattedTimestamp = `${messageParts[4]}T${messageParts[5]}` | ||
return { | ||
apiToken: messageParts[1], | ||
expirationDate: new Date(expirationDateFormattedTimestamp) | ||
} | ||
} | ||
|
||
export const transformRecreateApiTokenResponseToApiTokenInfo = ( | ||
response: AxiosResponse | ||
): ApiTokenInfo => { | ||
const apiTokenInfoPayload = response.data.data | ||
const messageParts = apiTokenInfoPayload.message.split(' ') | ||
const expirationDateFormattedTimestamp = `${messageParts[9]}T${messageParts[10]}` | ||
return { | ||
apiToken: messageParts[5], | ||
expirationDate: new Date(expirationDateFormattedTimestamp) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
POSTGRES_VERSION=13 | ||
DATAVERSE_DB_USER=dataverse | ||
SOLR_VERSION=9.3.0 | ||
DATAVERSE_IMAGE_REGISTRY=docker.io | ||
DATAVERSE_IMAGE_TAG=unstable | ||
DATAVERSE_IMAGE_REGISTRY=ghcr.io | ||
DATAVERSE_IMAGE_TAG=10857-add-expiration-date-to-recreate-token-api | ||
DATAVERSE_BOOTSTRAP_TIMEOUT=5m |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { ApiConfig, WriteError, deleteCurrentApiToken } from '../../../src' | ||
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' | ||
import { TestConstants } from '../../testHelpers/TestConstants' | ||
import { createApiTokenViaApi } from '../../testHelpers/users/apiTokenHelper' | ||
|
||
describe('execute', () => { | ||
beforeAll(async () => { | ||
ApiConfig.init( | ||
TestConstants.TEST_API_URL, | ||
DataverseApiAuthMechanism.API_KEY, | ||
process.env.TEST_API_KEY | ||
) | ||
}) | ||
|
||
afterAll(async () => { | ||
ApiConfig.init( | ||
TestConstants.TEST_API_URL, | ||
DataverseApiAuthMechanism.API_KEY, | ||
process.env.TEST_API_KEY | ||
) | ||
}) | ||
|
||
test('should successfully recreate the API token', async () => { | ||
const testApiToken = await createApiTokenViaApi('deleteCurrentApiTokenFTUser') | ||
ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, testApiToken) | ||
await deleteCurrentApiToken.execute() | ||
// Since the token has been deleted, the next call using it should return a WriteError | ||
await expect(deleteCurrentApiToken.execute()).rejects.toBeInstanceOf(WriteError) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { ApiConfig, getCurrentApiToken } from '../../../src' | ||
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' | ||
import { TestConstants } from '../../testHelpers/TestConstants' | ||
|
||
describe('execute', () => { | ||
beforeAll(async () => { | ||
ApiConfig.init( | ||
TestConstants.TEST_API_URL, | ||
DataverseApiAuthMechanism.API_KEY, | ||
process.env.TEST_API_KEY | ||
) | ||
}) | ||
|
||
test('should return the current API token', async () => { | ||
const actualTokenInfo = await getCurrentApiToken.execute() | ||
expect(actualTokenInfo.apiToken).toBe(process.env.TEST_API_KEY) | ||
expect(typeof actualTokenInfo.expirationDate).toBe('object') | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { ApiConfig, recreateCurrentApiToken } from '../../../src' | ||
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' | ||
import { TestConstants } from '../../testHelpers/TestConstants' | ||
import { createApiTokenViaApi } from '../../testHelpers/users/apiTokenHelper' | ||
|
||
describe('execute', () => { | ||
beforeAll(async () => { | ||
ApiConfig.init( | ||
TestConstants.TEST_API_URL, | ||
DataverseApiAuthMechanism.API_KEY, | ||
process.env.TEST_API_KEY | ||
) | ||
}) | ||
|
||
afterAll(async () => { | ||
ApiConfig.init( | ||
TestConstants.TEST_API_URL, | ||
DataverseApiAuthMechanism.API_KEY, | ||
process.env.TEST_API_KEY | ||
) | ||
}) | ||
|
||
test('should successfully recreate the API token', async () => { | ||
const testApiToken = await createApiTokenViaApi('recreateCurrentApiTokenFTUser') | ||
ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, testApiToken) | ||
const actualRecreatedApiTokenInfo = await recreateCurrentApiToken.execute() | ||
expect(actualRecreatedApiTokenInfo.apiToken).not.toBeUndefined() | ||
expect(actualRecreatedApiTokenInfo.apiToken).not.toBe(testApiToken) | ||
expect(typeof actualRecreatedApiTokenInfo.expirationDate).toBe('object') | ||
}) | ||
}) |
Oops, something went wrong.