diff --git a/src/api/handlers/projects/secrets.py b/src/api/handlers/projects/secrets.py index dd9f1c914..ac6bc9237 100644 --- a/src/api/handlers/projects/secrets.py +++ b/src/api/handlers/projects/secrets.py @@ -7,7 +7,7 @@ from pyinfrabox.utils import validate_uuid from pyinfraboxutils.ibflask import OK from pyinfraboxutils.ibrestplus import api, response_model -from pyinfraboxutils.secrets import encrypt_secret +from pyinfraboxutils.secrets import encrypt_secret, decrypt_secret ns = api.namespace('Secrets', path='/api/v1/projects//secrets', @@ -110,3 +110,42 @@ def delete(self, project_id, secret_id): g.db.commit() return OK('Successfully deleted secret.') + + +secret_value_model = api.model('SecretValue', { + 'name': fields.String(required=True), + 'value': fields.String(required=True), +}) + + +@ns.route('/values') +@api.doc(responses={403: 'Not Authorized', 404: 'Project not found'}) +class SecretValues(Resource): + + @api.marshal_list_with(secret_value_model) + def get(self, project_id): + ''' + Returns project's secrets with decrypted values. + + WARNING: this endpoint exposes plaintext secret values and is + restricted to project administrators by the OPA policy. + ''' + if not validate_uuid(project_id): + abort(400, 'Invalid project uuid.') + + project = g.db.execute_one_dict(''' + SELECT id FROM project WHERE id = %s + ''', [project_id]) + + if not project: + abort(404, 'Project not found.') + + secrets = g.db.execute_many_dict(''' + SELECT name, value FROM secret + WHERE project_id = %s + ''', [project_id]) + + for secret in secrets: + secret['value'] = decrypt_secret(secret['value']) + + return secrets diff --git a/src/openpolicyagent/policies/projects_secrets.rego b/src/openpolicyagent/policies/projects_secrets.rego index abecd4fee..dbd40e56e 100644 --- a/src/openpolicyagent/policies/projects_secrets.rego +++ b/src/openpolicyagent/policies/projects_secrets.rego @@ -20,6 +20,14 @@ allow { projects_secrets_administrator([api.token.user.id, project_id]) } +# Allow GET access to /api/v1/projects//secrets/values for project administrators +allow { + api.method = "GET" + api.path = ["api", "v1", "projects", project_id, "secrets", "values"] + api.token.type = "user" + projects_secrets_administrator([api.token.user.id, project_id]) +} + # Allow POST access to /api/v1/projects//secrets for project administrators allow { api.method = "POST"