Skip to content

Commit

Permalink
[RND-422] Client Management API: authentication and token management …
Browse files Browse the repository at this point in the history
…with PostgreSQL (#301)

* [BIA-422] Client Management API: authentication and token management with PostgreSQL

Update functions to move sql scripts to SqlHelper.
Add tests

* Add @edfi/meadowlark-authz-server

* Update SqlHelper.ts

Fix linting error

* Update test and fix problems

* Update CreateAuthorizationClient.test.ts

Update test

* [RND-422] add contributor

* Update e2e to use authorization

Update NoAuthorizationDocument to use Object.freeze.
Update e2e to use AUTHORIZATION_STORE_PLUGIN with postgres

* Update on-pullrequest.yml

Fix typo

* Update to add to env postgres or mongo authorization (e2e test).

* Update SqlHelper.ts

Update bootstrap validation

* Update SqlHelper.ts

* Update max pool size

* Release pg connection

* Update code to fix test errors

* Fix integration tests

---------

Co-authored-by: Brad Banister <[email protected]>
  • Loading branch information
jleiva-gap and bradbanister authored Sep 29, 2023
1 parent 4b452e4 commit f339095
Show file tree
Hide file tree
Showing 23 changed files with 1,165 additions and 3 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/on-pullrequest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,10 @@ jobs:
echo OAUTH_SIGNING_KEY="$( openssl rand -base64 256 | tr -d '\n' )" >> .env-e2e
working-directory: Meadowlark-js/tests/e2e/setup/

- name: End to End tests for ${{matrix.store.db}} as store and ${{matrix.query_handler.provider}} as query handler
- name: End to End tests for ${{matrix.store.db}} as store, ${{matrix.store.plugin}} as authorization and ${{matrix.query_handler.provider}} as query handler
run: npm run test:e2e:build -- --ci
env:
AUTHORIZATION_STORE_PLUGIN: ${{ matrix.store.plugin }}
DOCUMENT_STORE_PLUGIN: ${{ matrix.store.plugin }}
QUERY_HANDLER_PLUGIN: ${{ matrix.query_handler.plugin }}
LISTENER1_PLUGIN: ${{ matrix.query_handler.plugin }}
Expand Down
3 changes: 3 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ contributors
graph](https://github.com/Ed-Fi-Exchange-OSS/Meadowlark/network/dependencies).
New contributors should add their name and e-mail address or link to GitHub
profile to this file with their first pull request.

Max Paulson <[email protected]>

Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"build:copy-non-ts": "copyfiles -u 1 -e \"**/*.ts\" \"src/**/*\" dist --verbose"
},
"dependencies": {
"@edfi/meadowlark-authz-server": "^0.3.6-pre-36",
"@edfi/meadowlark-core": "^v0.3.6-pre-36",
"@edfi/meadowlark-utilities": "^v0.3.6-pre-36",
"pg": "^8.11.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,32 @@ import {
UpsertRequest,
UpsertResult,
} from '@edfi/meadowlark-core';
import {
CreateAuthorizationClientRequest,
CreateAuthorizationClientResult,
GetAuthorizationClientRequest,
GetAuthorizationClientResult,
GetAllAuthorizationClientsResult,
UpdateAuthorizationClientRequest,
UpdateAuthorizationClientResult,
ResetAuthorizationClientSecretResult,
ResetAuthorizationClientSecretRequest,
TryCreateBootstrapAuthorizationAdminResult,
} from '@edfi/meadowlark-authz-server';
import type { PoolClient } from 'pg';

import * as Upsert from './repository/Upsert';
import * as Delete from './repository/Delete';
import * as Get from './repository/Get';
import * as Update from './repository/Update';

import * as CreateAuthorizationClient from './repository/authorization/CreateAuthorizationClient';
import * as TryCreateBootstrapAuthorizationAdmin from './repository/authorization/TryCreateBootstrapAuthorizationAdmin';
import * as GetAuthorizationClient from './repository/authorization/GetAuthorizationClient';
import * as GetAllAuthorizationClients from './repository/authorization/GetAllAuthorizationClients';
import * as UpdateAuthorizationClient from './repository/authorization/UpdateAuthorizationClient';
import * as ResetAuthorizationClientSecret from './repository/authorization/ResetAuthorizationClientSecret';

import { getSharedClient, closeSharedConnection } from './repository/Db';
import * as SecurityMiddleware from './security/SecurityMiddleware';

Expand Down Expand Up @@ -68,6 +88,72 @@ export async function securityMiddleware(middlewareModel: MiddlewareModel): Prom
}
}

// AuthorizationStore implementation
export async function createAuthorizationClientDocument(
request: CreateAuthorizationClientRequest,
): Promise<CreateAuthorizationClientResult> {
const poolClient: PoolClient = await getSharedClient();
try {
return CreateAuthorizationClient.createAuthorizationClientDocument(request, poolClient);
} finally {
poolClient.release();
}
}

export async function tryCreateBootstrapAuthorizationAdminDocument(
request: CreateAuthorizationClientRequest,
): Promise<TryCreateBootstrapAuthorizationAdminResult> {
const poolClient: PoolClient = await getSharedClient();
try {
return TryCreateBootstrapAuthorizationAdmin.tryCreateBootstrapAuthorizationAdminDocument(request, poolClient);
} finally {
poolClient.release();
}
}

export async function getAuthorizationClientDocument(
request: GetAuthorizationClientRequest,
): Promise<GetAuthorizationClientResult> {
const poolClient: PoolClient = await getSharedClient();
try {
return GetAuthorizationClient.getAuthorizationClientDocument(request, poolClient);
} finally {
poolClient.release();
}
}

export async function getAllAuthorizationClientDocuments(traceId: string): Promise<GetAllAuthorizationClientsResult> {
const poolClient: PoolClient = await getSharedClient();
try {
return GetAllAuthorizationClients.getAllAuthorizationClientDocuments(traceId, poolClient);
} finally {
poolClient.release();
}
}

export async function updateAuthorizationClientDocument(
request: UpdateAuthorizationClientRequest,
): Promise<UpdateAuthorizationClientResult> {
const poolClient: PoolClient = await getSharedClient();
try {
return UpdateAuthorizationClient.updateAuthorizationClientDocument(request, poolClient);
} finally {
poolClient.release();
}
}

export async function resetAuthorizationClientSecret(
request: ResetAuthorizationClientSecretRequest,
): Promise<ResetAuthorizationClientSecretResult> {
const poolClient: PoolClient = await getSharedClient();
try {
return ResetAuthorizationClientSecret.resetAuthorizationClientSecret(request, poolClient);
} finally {
poolClient.release();
}
}
// End AuthorizationStore implementation

export async function closeConnection(): Promise<void> {
return closeSharedConnection();
}
18 changes: 18 additions & 0 deletions Meadowlark-js/backends/meadowlark-postgresql-backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,19 @@
// See the LICENSE and NOTICES files in the project root for more information.

import { DocumentStorePlugin } from '@edfi/meadowlark-core';
import { AuthorizationStorePlugin } from '@edfi/meadowlark-authz-server';
import {
upsertDocument,
deleteDocumentById,
getDocumentById,
updateDocumentById,
securityMiddleware,
createAuthorizationClientDocument,
updateAuthorizationClientDocument,
getAuthorizationClientDocument,
getAllAuthorizationClientDocuments,
resetAuthorizationClientSecret,
tryCreateBootstrapAuthorizationAdminDocument,
closeConnection,
} from './BackendFacade';

Expand All @@ -24,4 +31,15 @@ export function initializeDocumentStore(): DocumentStorePlugin {
};
}

export function initializeAuthorizationStore(): AuthorizationStorePlugin {
return {
createAuthorizationClient: createAuthorizationClientDocument,
tryCreateBootstrapAuthorizationAdmin: tryCreateBootstrapAuthorizationAdminDocument,
updateAuthorizationClient: updateAuthorizationClientDocument,
getAuthorizationClient: getAuthorizationClientDocument,
resetAuthorizationClientSecret,
getAllAuthorizationClients: getAllAuthorizationClientDocuments,
};
}

export { systemTestSetup, systemTestTeardown } from './repository/SystemTestHelper';
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

// Copied from mongodb backend by MaxP
import { AuthorizationClientRole, CreateAuthorizationClientRequest } from '@edfi/meadowlark-authz-server';

export interface AuthorizationDocument {
/**
* The clientId uuid. This field replaces the built-in MongoDB _id.
*/
_id: string;

/**
* A SHAKE-256 hex hash of the client secret.
*/
clientSecretHashed: string;

/**
* The client name
*/
clientName: string;

/**
* A list of client roles
*/
roles: AuthorizationClientRole[];

/**
* Whether this is the initial admin account created by bootstrapping
*/
isBootstrapAdmin: boolean;

/**
* Whether a client is active or not
*/
active: boolean;
}

export function authorizationDocumentFromCreate(
request: CreateAuthorizationClientRequest,
isBootstrapAdmin: boolean = false,
): AuthorizationDocument {
return {
_id: request.clientId,
clientSecretHashed: request.clientSecretHashed,
clientName: request.clientName,
roles: request.roles,
active: request.active,
isBootstrapAdmin,
};
}

export function bootstrapAdminDocumentFromCreate(request: CreateAuthorizationClientRequest): AuthorizationDocument {
return authorizationDocumentFromCreate(request, true);
}

export /**
* Creates a new empty newMeadowlarkDocument object
*/
const NoAuthorizationDocument: AuthorizationDocument = Object.freeze({
_id: '',
clientSecretHashed: '',
clientName: '',
roles: [],
isBootstrapAdmin: false,
active: false,
});
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ export async function getSharedClient(): Promise<PoolClient> {
await checkExistsAndCreateTables(client);
return client;
}

// Returns new Postgres Client
return singletonDbPool.connect();
}
Expand Down
Loading

0 comments on commit f339095

Please sign in to comment.