Skip to content

Commit b234629

Browse files
authored
API gateway: api-keys in webserver, gateway services and client sdk (#1460)
implements parts of #1268 and #1269 - webserver service: - frontend UI to add API keys (by @odeimaiz ) - extends API specs auth section: see auth/api-keys path - api-gateway service: - exposes basic API with two resources: me and studies (dummy handles in this PR) - customized autodoc with vendor extensions e.g. logo or client-sdk examples - drafts client-sdk calls (marked as tests to skip for this PR) - still not part of the swarm in this PR - only roles>=USER can create API keys This commit requires DB migration!
1 parent 95cc4e8 commit b234629

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1440
-226
lines changed

.github/CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Makefile @pcrespov, @sanderegg
1313
/scripts/demo @odeimaiz, @pcrespov
1414
/scripts/json-schema-to-openapi-schema @sanderegg
1515
/scripts/template-projects @odeimaiz, @pcrespov
16+
/services/api-gateway @pcrespov
1617
/services/catalog @pcrespov
1718
/services/sidecar @pcrespov, @mguidon
1819
/services/web/client @odeimaiz, @oetiker, @ignapas

api/specs/webserver/openapi-auth.yaml

+78
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,70 @@ paths:
210210
"3XX":
211211
description: redirection to specific ui application page
212212

213+
/auth/api-keys:
214+
get:
215+
summary: lists display names of API keys by this user
216+
tags:
217+
- authentication
218+
operationId: list_api_keys
219+
responses:
220+
"200":
221+
description: returns the display names of API keys
222+
content:
223+
application/json:
224+
schema:
225+
type: array
226+
items:
227+
type: string
228+
"401":
229+
description: requires login to list keys
230+
"403":
231+
description: not enough permissions to list keys
232+
233+
post:
234+
summary: creates API keys to access public API
235+
tags:
236+
- authentication
237+
operationId: create_api_key
238+
requestBody:
239+
description: user registration
240+
content:
241+
application/json:
242+
schema:
243+
$ref: "#/components/schemas/ApiKeyName"
244+
responses:
245+
"200":
246+
description: Authorization granted returning API key
247+
content:
248+
application/json:
249+
schema:
250+
$ref: "#/components/schemas/ApiKeyGranted"
251+
"400":
252+
description: key name requested is invalid
253+
"401":
254+
description: requires login to create a key
255+
"403":
256+
description: not enough permissions to create a key
257+
258+
delete:
259+
summary: deletes API key by name
260+
tags:
261+
- authentication
262+
operationId: delete_api_key
263+
requestBody:
264+
description: deletes given api key by name
265+
content:
266+
application/json:
267+
schema:
268+
$ref: "#/components/schemas/ApiKeyName"
269+
responses:
270+
"204":
271+
description: api key successfully deleted
272+
"401":
273+
description: requires login to delete a key
274+
"403":
275+
description: not enough permissions to delete a key
276+
213277
components:
214278
responses:
215279
DefaultErrorResponse:
@@ -221,3 +285,17 @@ components:
221285
client_session_id:
222286
type: string
223287
example: 5ac57685-c40f-448f-8711-70be1936fd63
288+
ApiKeyName:
289+
type: object
290+
properties:
291+
display_name:
292+
type: string
293+
ApiKeyGranted:
294+
type: object
295+
properties:
296+
display_name:
297+
type: string
298+
api_key:
299+
type: string
300+
api_secret:
301+
type: string

api/specs/webserver/openapi.yaml

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
openapi: 3.0.0
22
info:
33
title: "osparc-simcore RESTful API"
4-
version: 0.4.0
4+
version: 0.5.1
55
description: "RESTful API designed for web clients"
66
contact:
77
name: IT'IS Foundation
@@ -49,7 +49,7 @@ paths:
4949
# DIAGNOSTICS ---------------------------------------------------------
5050
/:
5151
$ref: "./openapi-diagnostics.yaml#/paths/~1"
52-
52+
5353
/health:
5454
$ref: "./openapi-diagnostics.yaml#/paths/~1health"
5555

@@ -85,6 +85,8 @@ paths:
8585
/auth/confirmation/{code}:
8686
$ref: "./openapi-auth.yaml#/paths/~1auth~1confirmation~1{code}"
8787

88+
/auth/api-keys:
89+
$ref: "./openapi-auth.yaml#/paths/~1auth~1api-keys"
8890
# USER SETTINGS ------------------------------------------------------------------
8991

9092
/me:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Adds api_keys table
2+
3+
Revision ID: 16ee7d73b9cc
4+
Revises: f3555bb4bc34
5+
Create Date: 2020-04-28 08:11:42.785688+00:00
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = '16ee7d73b9cc'
14+
down_revision = 'f3555bb4bc34'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.create_table('api_keys',
22+
sa.Column('id', sa.BigInteger(), nullable=False),
23+
sa.Column('display_name', sa.String(), nullable=False),
24+
sa.Column('user_id', sa.BigInteger(), nullable=False),
25+
sa.Column('api_key', sa.String(), nullable=False),
26+
sa.Column('api_secret', sa.String(), nullable=False),
27+
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
28+
sa.PrimaryKeyConstraint('id'),
29+
sa.UniqueConstraint('display_name', 'user_id', name='display_name_userid_uniqueness')
30+
)
31+
# ### end Alembic commands ###
32+
33+
34+
def downgrade():
35+
# ### commands auto generated by Alembic - please adjust! ###
36+
op.drop_table('api_keys')
37+
# ### end Alembic commands ###
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
""" API keys to access public gateway
2+
3+
4+
These keys grant the client authorization to the API resources
5+
6+
+--------+ +---------------+
7+
| |--(A)- Authorization Request ->| Resource |
8+
|client | | Owner | Authorization request
9+
| |<-(B)-- Authorization Grant ---| |
10+
+--------+ +---------------+
11+
12+
"""
13+
import sqlalchemy as sa
14+
15+
from .base import metadata
16+
from .users import users
17+
18+
api_keys = sa.Table(
19+
"api_keys",
20+
metadata,
21+
sa.Column("id", sa.BigInteger, nullable=False, primary_key=True),
22+
sa.Column("display_name", sa.String, nullable=False),
23+
sa.Column(
24+
"user_id",
25+
sa.BigInteger,
26+
sa.ForeignKey(users.c.id, ondelete="CASCADE"),
27+
nullable=False,
28+
),
29+
sa.Column("api_key", sa.String, nullable=False),
30+
sa.Column("api_secret", sa.String, nullable=False),
31+
sa.UniqueConstraint(
32+
"display_name", "user_id", name="display_name_userid_uniqueness"
33+
),
34+
)

packages/postgres-database/src/simcore_postgres_database/webserver_models.py

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .models.user_to_projects import user_to_projects
1313
from .models.users import UserRole, UserStatus, users
1414
from .models.tags import tags, study_tags
15+
from .models.api_keys import api_keys
1516

1617
__all__ = [
1718
"users",
@@ -27,4 +28,5 @@
2728
"comp_pipeline",
2829
"tags",
2930
"study_tags",
31+
"api_keys",
3032
]

requirements.txt

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
#
33
# $ make devenv
44
#
5-
pylint
65
black
76
pip-tools
87
rope

scripts/common.Makefile

+5-4
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,12 @@ info: ## displays basic info
7676
@echo ' NOW_TIMESTAMP : ${NOW_TIMESTAMP}'
7777
@echo ' VCS_URL : ${VCS_URL}'
7878
@echo ' VCS_REF : ${VCS_REF}'
79-
# installed
79+
# installed in .venv
8080
@pip list
81-
# version
82-
@cat setup.py | grep name=
83-
@cat setup.py | grep version=
81+
# package
82+
-@echo ' name : ' $(shell python ${CURDIR}/setup.py --name)
83+
-@echo ' version : ' $(shell python ${CURDIR}/setup.py --version)
84+
8485

8586

8687
.PHONY: autoformat

services/api-gateway/.cookiecutterrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ default_context:
1616
project_name: 'Public API Gateway'
1717
project_short_description: "Platform's API Gateway for external clients"
1818
project_slug: 'api-gateway'
19-
version: '0.1.0'
19+
version: '0.1.1'
2020
year: '2020'

services/api-gateway/.env-devel

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#
2+
# Environment variables used to configure this service
3+
#
4+
5+
# SEE services/api-gateway/src/simcore_service_api_gateway/auth_security.py
6+
SECRET_KEY=d0d0397de2c85ad26ffd4a0f9643dfe3a0ca3937f99cf3c2e174e11b5ef79880
7+
8+
# SEE services/api-gateway/src/simcore_service_api_gateway/settings.py
9+
LOGLEVEL=DEBUG
10+
11+
POSTGRES_USER=test
12+
POSTGRES_PASSWORD=test
13+
POSTGRES_DB=test

services/api-gateway/Makefile

+12-8
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ export APP_VERSION = $(shell cat VERSION)
1313
reqs: ## compiles pip requirements (.in -> .txt)
1414
@$(MAKE) --directory requirements reqs
1515

16+
1617
.PHONY: install-dev install-prod install-ci
17-
install-dev install-prod install-ci: _check-venv-active ## install app in development/production or CI mode
18+
install-dev install-prod install-ci: _check_venv_active ## install app in development/production or CI mode
1819
# installing in $(subst install-,,$@) mode
1920
pip-sync requirements/$(subst install-,,$@).txt
2021

@@ -34,23 +35,26 @@ tests-integration: ## runs integration tests against local+production images
3435

3536

3637
.PHONY: run-devel down
37-
run-devel: down ## runs app with pg fixture for development
38-
# starting pg database ...
39-
docker-compose -f $(CURDIR)/tests/unit/with_dbs/docker-compose.yml up --detach
40-
# starting service
41-
export SECRET_KEY="d0d0397de2c85ad26ffd4a0f9643dfe3a0ca3937f99cf3c2e174e11b5ef79880"; \
38+
run-devel: .env-devel down ## runs app on host with pg fixture for development
39+
# running current app
40+
export $(shell grep -v '^#' $< | xargs -d '\n'); \
41+
docker-compose -f $(CURDIR)/tests/utils/docker-compose.yml up --detach; \
4242
uvicorn simcore_service_api_gateway.main:the_app --reload --port=8001 --host=0.0.0.0
43-
# stop
4443

4544
down: ## stops pg fixture
46-
docker-compose -f $(CURDIR)/tests/unit/with_dbs/docker-compose.yml down
45+
# stopping extra services
46+
-@docker-compose -f $(CURDIR)/tests/utils/docker-compose.yml down
47+
# killing any process using port 8001
48+
-@fuser --kill --verbose --namespace tcp 8001
49+
4750

4851
.PHONY: build
4952
build: ## builds docker image (using main services/docker-compose-build.yml)
5053
@$(MAKE) --directory ${REPO_BASE_DIR} target=${APP_NAME} $@
5154

5255

5356
.PHONY: replay
57+
# TODO: replay shall point to online cookiecutter
5458
replay: .cookiecutterrc ## re-applies cookiecutter
5559
# Replaying /home/crespo/devp/osparc-simcore/services/api-gateway/../../../cookiecutter-simcore-py-fastapi ...
5660
@cookiecutter --no-input --overwrite-if-exists \

services/api-gateway/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,10 @@ Platform's API Gateway for external clients
1414
[image-version]https://images.microbadger.com/badges/version/itisfoundation/api-gateway.svg
1515
[image-commit]:https://images.microbadger.com/badges/commit/itisfoundation/api-gateway.svg
1616
<!------------------------->
17+
18+
19+
20+
## References
21+
22+
- [Design patterns for modern web APIs](https://blog.feathersjs.com/design-patterns-for-modern-web-apis-1f046635215) by D. Luecke
23+
- [API Design Guide](https://cloud.google.com/apis/design/) by Google Cloud

services/api-gateway/VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.0
1+
0.1.1

services/api-gateway/requirements/dev.txt

+3
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@
1111

1212
# installs current package
1313
-e .
14+
15+
# common dev-tools as well
16+
-r ../../../requirements.txt

services/api-gateway/setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 0.1.0
2+
current_version = 0.1.1
33
commit = True
44
tag = True
55

services/api-gateway/src/simcore_service_api_gateway/__version__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
major, minor, patch = __version__.split(".")
88

99
api_version = __version__
10-
api_version_prefix: str = f"v{major}"
10+
api_vtag: str = f"v{major}"

0 commit comments

Comments
 (0)