mirror of
https://github.com/status-im/spiff-arena.git
synced 2025-01-13 19:55:24 +00:00
Auth for secrets (#1369)
* added new api to show secrets so we can use that in permissions * updated frontend to use new secret show value api * cleaned up secret_show method --------- Co-authored-by: jasquat <jasquat@users.noreply.github.com>
This commit is contained in:
parent
9458500b8a
commit
293aa867a1
@ -17,6 +17,10 @@ function log_info() {
|
||||
# run migrations
|
||||
export FLASK_APP=/app/src/spiffworkflow_backend
|
||||
|
||||
if [[ -z "${FLASK_DEBUG:-}" ]]; then
|
||||
export FLASK_DEBUG=0
|
||||
fi
|
||||
|
||||
if [[ "${SPIFFWORKFLOW_BACKEND_WAIT_FOR_DB_TO_BE_READY:-}" == "true" ]]; then
|
||||
echo 'Waiting for db to be ready...'
|
||||
poetry run python ./bin/wait_for_db_to_be_ready.py
|
||||
|
@ -68,6 +68,7 @@ if [[ -z "${SPIFFWORKFLOW_BACKEND_ENV:-}" ]]; then
|
||||
fi
|
||||
|
||||
export FLASK_SESSION_SECRET_KEY="e7711a3ba96c46c68e084a86952de16f"
|
||||
export FLASK_DEBUG=1
|
||||
|
||||
if [[ -z "${SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER_IN_CREATE_APP:-}" ]]; then
|
||||
SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER_IN_CREATE_APP=true
|
||||
|
@ -2954,7 +2954,7 @@ paths:
|
||||
type: string
|
||||
get:
|
||||
operationId: spiffworkflow_backend.routes.secrets_controller.secret_show
|
||||
summary: Return a secret value for a key
|
||||
summary: Return info about a secret for a key. Does not include the value.
|
||||
tags:
|
||||
- Secrets
|
||||
responses:
|
||||
@ -2998,6 +2998,27 @@ paths:
|
||||
"404":
|
||||
description: Secret does not exist
|
||||
|
||||
/secrets/show-value/{key}:
|
||||
parameters:
|
||||
- name: key
|
||||
in: path
|
||||
required: true
|
||||
description: The key we are using
|
||||
schema:
|
||||
type: string
|
||||
get:
|
||||
operationId: spiffworkflow_backend.routes.secrets_controller.secret_show_value
|
||||
summary: Return a secret value for a key
|
||||
tags:
|
||||
- Secrets
|
||||
responses:
|
||||
"200":
|
||||
description: We return a secret
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Secret"
|
||||
|
||||
/permissions-check:
|
||||
post:
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.permissions_check
|
||||
|
@ -16,6 +16,11 @@ from spiffworkflow_backend.services.user_service import UserService
|
||||
|
||||
def secret_show(key: str) -> Response:
|
||||
secret = SecretService.get_secret(key)
|
||||
return make_response(jsonify(secret), 200)
|
||||
|
||||
|
||||
def secret_show_value(key: str) -> Response:
|
||||
secret = SecretService.get_secret(key)
|
||||
|
||||
# normal serialization does not include the secret value, but this is the one endpoint where we want to return the goods
|
||||
secret_as_dict = secret.serialized()
|
||||
|
@ -40,7 +40,7 @@ class TestSecretsController(SecretServiceTestHelpers):
|
||||
assert SecretService._decrypt(secret["value"]) == self.test_value
|
||||
assert secret["user_id"] == with_super_admin_user.id
|
||||
|
||||
def test_get_secret(
|
||||
def test_get_secret_api(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
@ -56,6 +56,24 @@ class TestSecretsController(SecretServiceTestHelpers):
|
||||
assert secret_response
|
||||
assert secret_response.status_code == 200
|
||||
assert secret_response.json
|
||||
assert "value" not in secret_response.json
|
||||
|
||||
def test_get_secret_value(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
with_super_admin_user: UserModel,
|
||||
) -> None:
|
||||
"""Test get secret."""
|
||||
self.add_test_secret(with_super_admin_user)
|
||||
secret_response = client.get(
|
||||
f"/v1.0/secrets/show-value/{self.test_key}",
|
||||
headers=self.logged_in_headers(with_super_admin_user),
|
||||
)
|
||||
assert secret_response
|
||||
assert secret_response.status_code == 200
|
||||
assert secret_response.json
|
||||
assert SecretService._decrypt(secret_response.json["value"]) == self.test_value
|
||||
|
||||
def test_update_secret(
|
||||
|
@ -37,15 +37,18 @@ export const useUriListForPermissions = () => {
|
||||
processModelShowPath: `/v1.0/process-models/${params.process_model_id}`,
|
||||
processModelTestsPath: `/v1.0/process-model-tests/run/${params.process_model_id}`,
|
||||
secretListPath: `/v1.0/secrets`,
|
||||
secretShowPath: `/v1.0/secrets/${params.secret_identifier}`,
|
||||
secretShowValuePath: `/v1.0/secrets/show-value/${params.secret_identifier}`,
|
||||
userSearch: `/v1.0/users/search`,
|
||||
userExists: `/v1.0/users/exists/by-username`,
|
||||
};
|
||||
}, [
|
||||
params.process_model_id,
|
||||
params.secret_identifier,
|
||||
params.file_name,
|
||||
params.page_identifier,
|
||||
params.process_group_id,
|
||||
params.process_instance_id,
|
||||
params.page_identifier,
|
||||
params.process_model_id,
|
||||
]);
|
||||
|
||||
return { targetUris };
|
||||
|
@ -16,8 +16,8 @@ export interface ApiActions {
|
||||
export interface Secret {
|
||||
id: number;
|
||||
key: string;
|
||||
value: string;
|
||||
creator_user_id: string;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export interface Onboarding {
|
||||
|
@ -98,7 +98,7 @@ export default function Configuration({ extensionUxElements }: OwnProps) {
|
||||
<Route path="/" element={<SecretList />} />
|
||||
<Route path="secrets" element={<SecretList />} />
|
||||
<Route path="secrets/new" element={<SecretNew />} />
|
||||
<Route path="secrets/:key" element={<SecretShow />} />
|
||||
<Route path="secrets/:secret_identifier" element={<SecretShow />} />
|
||||
<Route path="authentications" element={<AuthenticationList />} />
|
||||
<Route
|
||||
path="extension/:page_identifier"
|
||||
|
@ -3,9 +3,12 @@ import { useParams, useNavigate } from 'react-router-dom';
|
||||
// @ts-ignore
|
||||
import { Stack, Table, Button, TextInput } from '@carbon/react';
|
||||
import HttpService from '../services/HttpService';
|
||||
import { Secret } from '../interfaces';
|
||||
import { PermissionsToCheck, Secret } from '../interfaces';
|
||||
import { Notification } from '../components/Notification';
|
||||
import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
|
||||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||
import { Can } from '../contexts/Can';
|
||||
|
||||
export default function SecretShow() {
|
||||
const navigate = useNavigate();
|
||||
@ -16,12 +19,21 @@ export default function SecretShow() {
|
||||
const [showSuccessNotification, setShowSuccessNotification] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const { targetUris } = useUriListForPermissions();
|
||||
const permissionRequestData: PermissionsToCheck = {
|
||||
[targetUris.secretShowPath]: ['PUT', 'DELETE', 'GET'],
|
||||
[targetUris.secretShowValuePath]: ['GET'],
|
||||
};
|
||||
const { ability, permissionsLoaded } = usePermissionFetcher(
|
||||
permissionRequestData
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/secrets/${params.key}`,
|
||||
path: `/secrets/${params.secret_identifier}`,
|
||||
successCallback: setSecret,
|
||||
});
|
||||
}, [params.key]);
|
||||
}, [params.secret_identifier]);
|
||||
|
||||
const handleSecretValueChange = (event: any) => {
|
||||
if (secret) {
|
||||
@ -67,26 +79,63 @@ export default function SecretShow() {
|
||||
/>
|
||||
);
|
||||
|
||||
if (secret) {
|
||||
const handleShowSecretValue = () => {
|
||||
if (secret === null) {
|
||||
return;
|
||||
}
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/secrets/show-value/${secret.key}`,
|
||||
successCallback: (result: Secret) => {
|
||||
setSecret(result);
|
||||
setDisplaySecretValue(true);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (secret && permissionsLoaded) {
|
||||
return (
|
||||
<>
|
||||
{showSuccessNotification && successNotificationComponent}
|
||||
<h1>Secret Key: {secret.key}</h1>
|
||||
<Stack orientation="horizontal" gap={3}>
|
||||
<ButtonWithConfirmation
|
||||
description="Delete Secret?"
|
||||
onConfirmation={deleteSecret}
|
||||
buttonLabel="Delete"
|
||||
/>
|
||||
<Button
|
||||
disabled={displaySecretValue}
|
||||
variant="warning"
|
||||
onClick={() => {
|
||||
setDisplaySecretValue(true);
|
||||
}}
|
||||
<Can I="DELETE" a={targetUris.secretShowPath} ability={ability}>
|
||||
<ButtonWithConfirmation
|
||||
description="Delete Secret?"
|
||||
onConfirmation={deleteSecret}
|
||||
buttonLabel="Delete"
|
||||
/>
|
||||
</Can>
|
||||
<Can
|
||||
I="GET"
|
||||
a={targetUris.secretShowValuePath}
|
||||
ability={ability}
|
||||
passThrough
|
||||
>
|
||||
Retrieve secret value
|
||||
</Button>
|
||||
{(secretReadAllowed: boolean) => {
|
||||
if (secretReadAllowed) {
|
||||
return (
|
||||
<Button
|
||||
disabled={displaySecretValue}
|
||||
variant="warning"
|
||||
onClick={handleShowSecretValue}
|
||||
>
|
||||
Retrieve secret value
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Can I="PUT" a={targetUris.secretShowPath} ability={ability}>
|
||||
<Button
|
||||
disabled={displaySecretValue}
|
||||
variant="warning"
|
||||
onClick={() => setDisplaySecretValue(true)}
|
||||
>
|
||||
Edit secret value
|
||||
</Button>
|
||||
</Can>
|
||||
);
|
||||
}}
|
||||
</Can>
|
||||
</Stack>
|
||||
<div>
|
||||
<Table striped bordered>
|
||||
@ -103,7 +152,7 @@ export default function SecretShow() {
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{params.key}</td>
|
||||
<td>{params.secret_identifier}</td>
|
||||
{displaySecretValue && (
|
||||
<>
|
||||
<td aria-label="Secret value">
|
||||
@ -112,14 +161,23 @@ export default function SecretShow() {
|
||||
name="secret_value"
|
||||
value={secret.value}
|
||||
onChange={handleSecretValueChange}
|
||||
disabled={
|
||||
!ability.can('PUT', targetUris.secretShowPath)
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
{displaySecretValue && (
|
||||
<Button variant="warning" onClick={updateSecretValue}>
|
||||
Update Value
|
||||
</Button>
|
||||
)}
|
||||
<Can
|
||||
I="PUT"
|
||||
a={targetUris.secretShowPath}
|
||||
ability={ability}
|
||||
>
|
||||
{displaySecretValue && (
|
||||
<Button variant="warning" onClick={updateSecretValue}>
|
||||
Update Value
|
||||
</Button>
|
||||
)}
|
||||
</Can>
|
||||
</td>
|
||||
</>
|
||||
)}
|
||||
|
Loading…
x
Reference in New Issue
Block a user