From 10e48a1331cf4e96d544a7e33dff4f570bc56def Mon Sep 17 00:00:00 2001 From: Steffen Date: Mon, 18 Feb 2019 07:31:25 +0000 Subject: [PATCH] add support for additional SSH keys --- README.md | 1 - src/api/handlers/projects/sshkeys.py | 122 ++++++++++++++++++ .../src/components/project/SSHKeys.vue | 97 ++++++++++++++ .../src/components/project/Settings.vue | 13 +- src/dashboard-client/src/models/Project.js | 17 +++ src/dashboard-client/src/store.js | 7 + src/db/migrations/00028.sql | 2 +- src/db/migrations/00029.sql | 9 ++ .../policies/projects_sshkeys.rego | 37 ++++++ src/scheduler/kubernetes/scheduler.py | 29 ++++- 10 files changed, 324 insertions(+), 10 deletions(-) create mode 100644 src/api/handlers/projects/sshkeys.py create mode 100644 src/dashboard-client/src/components/project/SSHKeys.vue create mode 100644 src/db/migrations/00029.sql create mode 100644 src/openpolicyagent/policies/projects_sshkeys.rego diff --git a/README.md b/README.md index 8caacdadf..7cc7c24d0 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ Some of InfraBox' features are: - [Set resource limits (CPU and memory) for each task](https://github.com/SAP/infrabox-examples) - [GitHub integration](docs/install/configure/github.md) - [Gerrit integration](docs/install/configure/gerrit.md) -- GitLab (coming soon) - [LDAP support](docs/install/configure/ldap.md) - [Periodically schedule builds](docs/cronjobs.md) - [and many more, see our examples](https://github.com/SAP/infrabox-examples) diff --git a/src/api/handlers/projects/sshkeys.py b/src/api/handlers/projects/sshkeys.py new file mode 100644 index 000000000..d331b3784 --- /dev/null +++ b/src/api/handlers/projects/sshkeys.py @@ -0,0 +1,122 @@ +import re + +from flask import request, g, abort +from flask_restplus import Resource, fields + +from pyinfrabox.utils import validate_uuid +from pyinfraboxutils.ibflask import OK +from pyinfraboxutils.ibrestplus import api, response_model + +from croniter import croniter + +ns = api.namespace('SSHKeys', + path='/api/v1/projects//sshkeys', + description='SSH Key related operations') + +sshkey_model = api.model('CronJob', { + 'name': fields.String(required=True), + 'id': fields.String(required=True), + 'secret': fields.String(required=True), +}) + +add_sshkey_model = api.model('AddCronJob', { + 'name': fields.String(required=True, max_length=255), + 'secret': fields.String(required=True, max_length=255), +}) + +@ns.route('/') +@api.doc(responses={403: 'Not Authorized'}) +class SSHKeys(Resource): + + name_pattern = re.compile('^[a-zA-Z0-9_]+$') + + @api.marshal_list_with(sshkey_model) + def get(self, project_id): + ''' + Returns project's sshkeys + ''' + p = g.db.execute_many_dict(''' + SELECT k.id, k.name, s.name as secret + FROM sshkey k + JOIN secret s + ON s.id = k.secret_id + WHERE s.project_id = %s + AND k.project_id = %s + ''', [project_id, project_id]) + return p + + @api.expect(add_sshkey_model) + @api.response(200, 'Success', response_model) + def post(self, project_id): + ''' + Add new sshkey + ''' + b = request.get_json() + + if not CronJobs.name_pattern.match(b['name']): + abort(400, 'CronJob name must be not empty alphanumeric string.') + + result = g.db.execute_one_dict(""" + SELECT id + FROM secret + WHERE project_id = %s + AND name = %s + """, [project_id, b['secret']]) + + if not result: + abort(400, 'Secret does not exist') + + secret_id = result['id'] + + result = g.db.execute_one_dict(""" + SELECT COUNT(*) as cnt + FROM sshkey + WHERE project_id = %s + """, [project_id]) + + if result['cnt'] > 50: + abort(400, 'Too many sshkeys.') + + r = g.db.execute_one(""" + SELECT count(*) + FROM sshkey + WHERE project_id = %s + AND name = %s + """, [project_id, b['name']]) + + if r[0] > 0: + abort(400, 'SSH Key with this name already exist') + + g.db.execute(''' + INSERT INTO sshkey (project_id, name, secret_id) VALUES(%s, %s, %s) + ''', [project_id, b['name'], secret_id]) + + g.db.commit() + + return OK('Successfully added SSH Key') + +@ns.route('/') +@api.doc(responses={403: 'Not Authorized'}) +class CronJob(Resource): + @api.response(200, 'Success', response_model) + def delete(self, project_id, sshkey_id): + ''' + Delete a sshkey + ''' + if not validate_uuid(sshkey_id): + abort(400, "Invalid sshkey uuid.") + + num_sshkeys = g.db.execute_one(""" + SELECT COUNT(*) FROM sshkey + WHERE project_id = %s and id = %s + """, [project_id, sshkey_id])[0] + + if num_sshkeys == 0: + return abort(400, 'SSH Key does not exist.') + + g.db.execute(""" + DELETE FROM sshkey WHERE project_id = %s and id = %s + """, [project_id, sshkey_id]) + g.db.commit() + + return OK('Successfully deleted SSH Key.') diff --git a/src/dashboard-client/src/components/project/SSHKeys.vue b/src/dashboard-client/src/components/project/SSHKeys.vue new file mode 100644 index 000000000..935da44b1 --- /dev/null +++ b/src/dashboard-client/src/components/project/SSHKeys.vue @@ -0,0 +1,97 @@ + + + diff --git a/src/dashboard-client/src/components/project/Settings.vue b/src/dashboard-client/src/components/project/Settings.vue index 0469b9c70..855a74f9b 100644 --- a/src/dashboard-client/src/components/project/Settings.vue +++ b/src/dashboard-client/src/components/project/Settings.vue @@ -1,14 +1,17 @@