[dd-sso] Add API documentation
The API spec file can be generated with: python -m admin.views.test.test_ApiViews --generate-spec From the admin development environment. A simple testing ground that serves the Swagger UI can also be started with: python -m admin.views.test.test_ApiViewsGON-3874-DD-moodle
parent
10e6afe351
commit
d37b4dfa6a
|
@ -21,6 +21,7 @@ requests = "*"
|
|||
python-keycloak = "*"
|
||||
attrs = "*"
|
||||
cryptography = "*"
|
||||
flasgger = "*"
|
||||
|
||||
[dev-packages]
|
||||
mypy = "*"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "cd4a56afb09ac033e44f2b8c075a8103f5ee27b31b766508441e34539f654ea1"
|
||||
"sha256": "7ce3de9caf3a9fcc47859dc03ad9e09db96185bd6be89480c7264ce71f6e80ca"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
|
@ -185,7 +185,7 @@
|
|||
"sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e",
|
||||
"sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f"
|
||||
],
|
||||
"markers": "python_version >= '3.6' and python_version < '4'",
|
||||
"markers": "python_version >= '3.6' and python_version < '4.0'",
|
||||
"version": "==2.2.1"
|
||||
},
|
||||
"ecdsa": {
|
||||
|
@ -204,6 +204,14 @@
|
|||
"index": "pypi",
|
||||
"version": "==0.33.2"
|
||||
},
|
||||
"flasgger": {
|
||||
"hashes": [
|
||||
"sha256:0603941cf4003626b4ee551ca87331f1d17b8eecce500ccf1a1f1d3a332fc94a",
|
||||
"sha256:6ebea406b5beecd77e8da42550f380d4d05a6107bc90b69ce9e77aee7612e2d0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.9.5"
|
||||
},
|
||||
"flask": {
|
||||
"hashes": [
|
||||
"sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b",
|
||||
|
@ -310,6 +318,14 @@
|
|||
"markers": "python_version < '3.10'",
|
||||
"version": "==5.1.0"
|
||||
},
|
||||
"importlib-resources": {
|
||||
"hashes": [
|
||||
"sha256:32bb095bda29741f6ef0e5278c42df98d135391bee5f932841efc0041f748dc3",
|
||||
"sha256:c09b067d82e72c66f4f8eb12332f5efbebc9b007c0b6c40818108c9870adc363"
|
||||
],
|
||||
"markers": "python_version < '3.9'",
|
||||
"version": "==5.10.1"
|
||||
},
|
||||
"itsdangerous": {
|
||||
"hashes": [
|
||||
"sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44",
|
||||
|
@ -326,6 +342,14 @@
|
|||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.1.2"
|
||||
},
|
||||
"jsonschema": {
|
||||
"hashes": [
|
||||
"sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d",
|
||||
"sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==4.17.3"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
|
||||
|
@ -380,6 +404,13 @@
|
|||
"index": "pypi",
|
||||
"version": "==7.1.12"
|
||||
},
|
||||
"mistune": {
|
||||
"hashes": [
|
||||
"sha256:182cc5ee6f8ed1b807de6b7bb50155df7b66495412836b9a74c8fbdfc75fe36d",
|
||||
"sha256:9ee0a66053e2267aba772c71e06891fa8f1af6d4b01d5e84e267b4570d4d9808"
|
||||
],
|
||||
"version": "==2.0.4"
|
||||
},
|
||||
"mysql-connector-python": {
|
||||
"hashes": [
|
||||
"sha256:02526f16eacc3961ff681c5c8455d2306a9b45124f2f012ca75a1eac9ceb5165",
|
||||
|
@ -479,6 +510,14 @@
|
|||
"index": "pypi",
|
||||
"version": "==9.3.0"
|
||||
},
|
||||
"pkgutil-resolve-name": {
|
||||
"hashes": [
|
||||
"sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174",
|
||||
"sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"
|
||||
],
|
||||
"markers": "python_version < '3.9'",
|
||||
"version": "==1.3.10"
|
||||
},
|
||||
"protobuf": {
|
||||
"hashes": [
|
||||
"sha256:06059eb6953ff01e56a25cd02cca1a9649a75a7e65397b5b9b4e929ed71d10cf",
|
||||
|
@ -553,6 +592,34 @@
|
|||
],
|
||||
"version": "==2.21"
|
||||
},
|
||||
"pyrsistent": {
|
||||
"hashes": [
|
||||
"sha256:055ab45d5911d7cae397dc418808d8802fb95262751872c841c170b0dbf51eed",
|
||||
"sha256:111156137b2e71f3a9936baf27cb322e8024dac3dc54ec7fb9f0bcf3249e68bb",
|
||||
"sha256:187d5730b0507d9285a96fca9716310d572e5464cadd19f22b63a6976254d77a",
|
||||
"sha256:21455e2b16000440e896ab99e8304617151981ed40c29e9507ef1c2e4314ee95",
|
||||
"sha256:2aede922a488861de0ad00c7630a6e2d57e8023e4be72d9d7147a9fcd2d30712",
|
||||
"sha256:3ba4134a3ff0fc7ad225b6b457d1309f4698108fb6b35532d015dca8f5abed73",
|
||||
"sha256:456cb30ca8bff00596519f2c53e42c245c09e1a4543945703acd4312949bfd41",
|
||||
"sha256:71d332b0320642b3261e9fee47ab9e65872c2bd90260e5d225dabeed93cbd42b",
|
||||
"sha256:879b4c2f4d41585c42df4d7654ddffff1239dc4065bc88b745f0341828b83e78",
|
||||
"sha256:9cd3e9978d12b5d99cbdc727a3022da0430ad007dacf33d0bf554b96427f33ab",
|
||||
"sha256:a178209e2df710e3f142cbd05313ba0c5ebed0a55d78d9945ac7a4e09d923308",
|
||||
"sha256:b39725209e06759217d1ac5fcdb510e98670af9e37223985f330b611f62e7425",
|
||||
"sha256:bfa0351be89c9fcbcb8c9879b826f4353be10f58f8a677efab0c017bf7137ec2",
|
||||
"sha256:bfd880614c6237243ff53a0539f1cb26987a6dc8ac6e66e0c5a40617296a045e",
|
||||
"sha256:c43bec251bbd10e3cb58ced80609c5c1eb238da9ca78b964aea410fb820d00d6",
|
||||
"sha256:d690b18ac4b3e3cab73b0b7aa7dbe65978a172ff94970ff98d82f2031f8971c2",
|
||||
"sha256:d6982b5a0237e1b7d876b60265564648a69b14017f3b5f908c5be2de3f9abb7a",
|
||||
"sha256:dec3eac7549869365fe263831f576c8457f6c833937c68542d08fde73457d291",
|
||||
"sha256:e371b844cec09d8dc424d940e54bba8f67a03ebea20ff7b7b0d56f526c71d584",
|
||||
"sha256:e5d8f84d81e3729c3b506657dddfe46e8ba9c330bf1858ee33108f8bb2adb38a",
|
||||
"sha256:ea6b79a02a28550c98b6ca9c35b9f492beaa54d7c5c9e9949555893c8a9234d0",
|
||||
"sha256:f1258f4e6c42ad0b20f9cfcc3ada5bd6b83374516cd01c0960e3cb75fdca6770"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.19.2"
|
||||
},
|
||||
"python-engineio": {
|
||||
"hashes": [
|
||||
"sha256:7454314a529bba20e745928601ffeaf101c1b5aca9a6c4e48ad397803d10ea0c",
|
||||
|
@ -651,7 +718,7 @@
|
|||
"sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7",
|
||||
"sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"
|
||||
],
|
||||
"markers": "python_version >= '3.6' and python_version < '4'",
|
||||
"markers": "python_version >= '3.6' and python_version < '4.0'",
|
||||
"version": "==4.9"
|
||||
},
|
||||
"schema": {
|
||||
|
|
|
@ -23,6 +23,7 @@ Flask==2.1.3
|
|||
Flask-Login==0.6.2
|
||||
eventlet==0.33.1
|
||||
Flask-SocketIO==5.2.0
|
||||
flasgger==0.9.5
|
||||
bcrypt==3.2.2
|
||||
# diceware can't be upgraded without issues
|
||||
diceware==0.9.6
|
||||
|
|
|
@ -21,10 +21,13 @@
|
|||
import copy
|
||||
import json
|
||||
import logging as log
|
||||
import os
|
||||
import traceback
|
||||
from operator import itemgetter
|
||||
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional
|
||||
|
||||
from flasgger import Swagger
|
||||
from flasgger.utils import swag_from
|
||||
from flask import request
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -66,8 +69,34 @@ ERR_412 = (
|
|||
|
||||
|
||||
def setup_api_views(app: "AdminFlaskApp") -> None:
|
||||
swagger = Swagger(
|
||||
app,
|
||||
template={
|
||||
"info": {
|
||||
"title": "DD API",
|
||||
"description": "DD API for external integrations",
|
||||
"version": "2022.11.0",
|
||||
"termsOfService": "",
|
||||
},
|
||||
"externalDocs": {
|
||||
"description": "Online Documentation",
|
||||
"url": "https://dd.digitalitzacio-democratica.xnet-x.net/docs/",
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"dd_jwt": {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "Authorization",
|
||||
"description": "JWS token using API_SECRET (e.g. 'bearer X.Y')",
|
||||
}
|
||||
},
|
||||
"security": {"dd_jwt": {"$ref": "#/securityDefinitions/dd_jwt"}},
|
||||
"swagger_ui": bool(os.environ.get("SWAGGER_UI", "")),
|
||||
},
|
||||
)
|
||||
# LISTS
|
||||
@app.json_route("/ddapi/users", methods=["GET"])
|
||||
@app.json_route("/ddapi/users", methods=["GET"], endpoint="api_users")
|
||||
@swag_from("api_docs/users.yml", endpoint="api_users")
|
||||
@has_token
|
||||
def ddapi_users() -> OptionalJsonResponse:
|
||||
try:
|
||||
|
@ -84,7 +113,10 @@ def setup_api_views(app: "AdminFlaskApp") -> None:
|
|||
return ERR_500
|
||||
return None
|
||||
|
||||
@app.json_route("/ddapi/users/filter", methods=["POST"])
|
||||
@app.json_route(
|
||||
"/ddapi/users/filter", methods=["POST"], endpoint="api_users_filter"
|
||||
)
|
||||
@swag_from("api_docs/users_filter.yml", endpoint="api_users_filter")
|
||||
@has_token
|
||||
def ddapi_users_search() -> OptionalJsonResponse:
|
||||
try:
|
||||
|
@ -110,7 +142,8 @@ def setup_api_views(app: "AdminFlaskApp") -> None:
|
|||
return ERR_500
|
||||
return None
|
||||
|
||||
@app.json_route("/ddapi/groups", methods=["GET"])
|
||||
@app.json_route("/ddapi/groups", methods=["GET"], endpoint="api_groups")
|
||||
@swag_from("api_docs/groups.yml", endpoint="api_groups")
|
||||
@has_token
|
||||
def ddapi_groups() -> OptionalJsonResponse:
|
||||
try:
|
||||
|
@ -127,7 +160,8 @@ def setup_api_views(app: "AdminFlaskApp") -> None:
|
|||
return ERR_500
|
||||
return None
|
||||
|
||||
@app.json_route("/ddapi/roles", methods=["GET"])
|
||||
@app.json_route("/ddapi/roles", methods=["GET"], endpoint="api_roles")
|
||||
@swag_from("api_docs/roles.yml", endpoint="api_roles")
|
||||
@has_token
|
||||
def ddapi_roles() -> OptionalJsonResponse:
|
||||
try:
|
||||
|
@ -141,7 +175,8 @@ def setup_api_views(app: "AdminFlaskApp") -> None:
|
|||
return ERR_500
|
||||
return None
|
||||
|
||||
@app.json_route("/ddapi/role/users", methods=["POST"])
|
||||
@app.json_route("/ddapi/role/users", methods=["POST"], endpoint="api_role_users")
|
||||
@swag_from("api_docs/role_users.yml", endpoint="api_role_users")
|
||||
@has_token
|
||||
def ddapi_role_users() -> OptionalJsonResponse:
|
||||
try:
|
||||
|
@ -174,8 +209,16 @@ def setup_api_views(app: "AdminFlaskApp") -> None:
|
|||
return None
|
||||
|
||||
# INDIVIDUAL ACTIONS
|
||||
@app.json_route("/ddapi/user", methods=["POST"])
|
||||
@app.json_route("/ddapi/user/<user_ddid>", methods=["PUT", "GET", "DELETE"])
|
||||
@app.json_route("/ddapi/user", methods=["POST"], endpoint="api_user_new")
|
||||
@app.json_route(
|
||||
"/ddapi/user/<user_ddid>",
|
||||
methods=["PUT", "GET", "DELETE"],
|
||||
endpoint="api_user_ddid",
|
||||
)
|
||||
@swag_from("api_docs/user_new.yml", endpoint="api_user_new")
|
||||
@swag_from("api_docs/user_get.yml", endpoint="api_user_ddid", methods=["GET"])
|
||||
@swag_from("api_docs/user_put.yml", endpoint="api_user_ddid", methods=["PUT"])
|
||||
@swag_from("api_docs/user_delete.yml", endpoint="api_user_ddid", methods=["DELETE"])
|
||||
@has_token
|
||||
def ddapi_user(user_ddid: Optional[str] = None) -> OptionalJsonResponse:
|
||||
try:
|
||||
|
@ -284,8 +327,18 @@ def setup_api_views(app: "AdminFlaskApp") -> None:
|
|||
log.error(traceback.format_exc())
|
||||
return ERR_500
|
||||
|
||||
@app.json_route("/ddapi/group", methods=["POST"])
|
||||
@app.json_route("/ddapi/group/<group_id>", methods=["GET", "POST", "DELETE"])
|
||||
@app.json_route("/ddapi/group", methods=["POST"], endpoint="api_group_new")
|
||||
@app.json_route(
|
||||
"/ddapi/group/<group_id>",
|
||||
methods=["GET", "POST", "DELETE"],
|
||||
endpoint="api_group_group_id",
|
||||
)
|
||||
@swag_from("api_docs/group_new.yml", endpoint="api_group_new")
|
||||
@swag_from("api_docs/group_get.yml", endpoint="api_group_group_id", methods=["GET"])
|
||||
# @swag_from('api_docs/group_put.yml', endpoint='api_group_group_id', methods=["PUT"])
|
||||
@swag_from(
|
||||
"api_docs/group_delete.yml", endpoint="api_group_group_id", methods=["DELETE"]
|
||||
)
|
||||
# @app.json_route("/api/group/<group_id>", methods=["PUT", "GET", "DELETE"])
|
||||
@has_token
|
||||
def ddapi_group(group_id: Optional[str] = None) -> OptionalJsonResponse:
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
Delete a registered group in DD
|
||||
---
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- in: url
|
||||
name: group_id
|
||||
description: |
|
||||
The group to delete from DD
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
schema:
|
||||
type: object
|
||||
404:
|
||||
description: |
|
||||
The group does not exist
|
|
@ -0,0 +1,20 @@
|
|||
Get a registered group in DD
|
||||
---
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- in: url
|
||||
name: group_id
|
||||
description: |
|
||||
The group to retrieve from DD
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: |
|
||||
The group as it exists on DD
|
||||
schema:
|
||||
$ref: '#/definitions/Group'
|
||||
404:
|
||||
description: |
|
||||
The group does not exist
|
|
@ -0,0 +1,30 @@
|
|||
Register a new group in DD
|
||||
---
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- in: body
|
||||
name: group
|
||||
description: |
|
||||
The group to be registered on DD.
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
required: True
|
||||
type: string
|
||||
description:
|
||||
required: False
|
||||
type: string
|
||||
parent:
|
||||
required: False
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: |
|
||||
The keycloak_id of the newly registered group
|
||||
schema:
|
||||
$ref: '#/definitions/KeycloakId'
|
||||
409:
|
||||
description: |
|
||||
The group already exists
|
|
@ -0,0 +1,34 @@
|
|||
List all registered groups on DD.
|
||||
---
|
||||
definitions:
|
||||
Group:
|
||||
type: object
|
||||
properties:
|
||||
keycloak_id:
|
||||
type: string
|
||||
format: uuid
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
Groups:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Group'
|
||||
responses:
|
||||
200:
|
||||
description: The list of groups registered on DD
|
||||
schema:
|
||||
$ref: '#/definitions/Groups'
|
||||
examples: |
|
||||
[{
|
||||
"keycloak_id": "f6ec2bda-bec9-415f-bcb7-f5ae644bfec5",
|
||||
"id": "ID",
|
||||
"name": "NAME",
|
||||
"path": "PATH",
|
||||
"description": "DESCRIPITON",
|
||||
}]
|
|
@ -0,0 +1,31 @@
|
|||
List registered users on DD with a the given role.
|
||||
---
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- in: body
|
||||
name: role
|
||||
description: |
|
||||
The role to search users registered on DD.
|
||||
One of 'id', 'name' and 'keycloak_id' must be provided.
|
||||
This is also the order in which the parameters are checked,
|
||||
in case multiple are provided.
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
required: False
|
||||
name:
|
||||
type: string
|
||||
required: False
|
||||
keycloak_id:
|
||||
type: string
|
||||
format: uuid
|
||||
required: False
|
||||
responses:
|
||||
200:
|
||||
description: |
|
||||
The list of users registered on DD with the filter applied.
|
||||
schema:
|
||||
$ref: '#/definitions/Users'
|
|
@ -0,0 +1,31 @@
|
|||
List all roles configured on DD.
|
||||
---
|
||||
definitions:
|
||||
Role:
|
||||
type: object
|
||||
properties:
|
||||
keycloak_id:
|
||||
type: string
|
||||
format: uuid
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
Roles:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Role'
|
||||
responses:
|
||||
200:
|
||||
description: The list of roles configured on DD
|
||||
schema:
|
||||
$ref: '#/definitions/Roles'
|
||||
examples: |
|
||||
[{
|
||||
"keycloak_id": "f6ec2bda-bec9-415f-bcb7-f5ae644bfec5",
|
||||
"id": "ID",
|
||||
"name": "NAME",
|
||||
"description": "DESCRIPITON",
|
||||
}]
|
|
@ -0,0 +1,18 @@
|
|||
Delete a registered user in DD
|
||||
---
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- in: url
|
||||
name: user_ddid
|
||||
description: |
|
||||
The user to delete from DD
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
schema:
|
||||
type: object
|
||||
404:
|
||||
description: |
|
||||
The user does not exist
|
|
@ -0,0 +1,20 @@
|
|||
Get a registered user in DD
|
||||
---
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- in: url
|
||||
name: user_ddid
|
||||
description: |
|
||||
The user to retrieve from DD
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: |
|
||||
The user as it exists on DD
|
||||
schema:
|
||||
$ref: '#/definitions/User'
|
||||
404:
|
||||
description: |
|
||||
The user does not exist
|
|
@ -0,0 +1,63 @@
|
|||
Register a new user in DD
|
||||
---
|
||||
definitions:
|
||||
KeycloakId:
|
||||
type: object
|
||||
properties:
|
||||
keycloak_id:
|
||||
required: True
|
||||
type: string
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- in: body
|
||||
name: user
|
||||
description: |
|
||||
The user to be registered on DD.
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
username:
|
||||
required: True
|
||||
type: string
|
||||
first:
|
||||
required: True
|
||||
type: string
|
||||
last:
|
||||
required: True
|
||||
type: string
|
||||
email:
|
||||
required: True
|
||||
type: string
|
||||
format: email
|
||||
password:
|
||||
required: True
|
||||
type: string
|
||||
format: email
|
||||
password_temporary:
|
||||
required: False
|
||||
type: bool
|
||||
quota:
|
||||
required: True
|
||||
type: string
|
||||
enabled:
|
||||
required: True
|
||||
type: bool
|
||||
role:
|
||||
required: True
|
||||
groups:
|
||||
required: True
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: |
|
||||
The keycloak_id of the newly registered user
|
||||
schema:
|
||||
$ref: '#/definitions/KeycloakId'
|
||||
examples: |
|
||||
{ "keycloak_id": "f6ec2bda-bec9-415f-bcb7-f5ae644bfec5" }
|
||||
409:
|
||||
description: |
|
||||
The user already exists
|
|
@ -0,0 +1,49 @@
|
|||
Modify a user in DD
|
||||
---
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- in: body
|
||||
name: user
|
||||
description: |
|
||||
The user to be modified on DD.
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
first:
|
||||
required: False
|
||||
type: string
|
||||
last:
|
||||
required: False
|
||||
type: string
|
||||
email:
|
||||
required: False
|
||||
type: string
|
||||
format: email
|
||||
password:
|
||||
required: False
|
||||
type: string
|
||||
format: email
|
||||
password_temporary:
|
||||
required: False
|
||||
type: bool
|
||||
quota:
|
||||
required: False
|
||||
type: string
|
||||
enabled:
|
||||
required: False
|
||||
type: bool
|
||||
role:
|
||||
required: False
|
||||
groups:
|
||||
required: False
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
schema:
|
||||
type: object
|
||||
404:
|
||||
description: |
|
||||
The user does not exist
|
|
@ -0,0 +1,58 @@
|
|||
List all registered users on DD.
|
||||
---
|
||||
definitions:
|
||||
User:
|
||||
type: object
|
||||
properties:
|
||||
keycloak_id:
|
||||
type: string
|
||||
format: uuid
|
||||
id:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
enabled:
|
||||
type: boolean
|
||||
first:
|
||||
type: string
|
||||
last:
|
||||
type: string
|
||||
role:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
fomat: email
|
||||
groups:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
quota:
|
||||
type: string
|
||||
quota_used_bytes:
|
||||
type: string
|
||||
Users:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/User'
|
||||
responses:
|
||||
200:
|
||||
description: The list of users registered on DD
|
||||
schema:
|
||||
$ref: '#/definitions/Users'
|
||||
examples: |
|
||||
[{
|
||||
"keycloak_id": "a773d249-a113-4542-8101-3a50f4cd28c2",
|
||||
"id": "ID",
|
||||
"username": "ID",
|
||||
"enabled": true,
|
||||
"first": "NAME",
|
||||
"last": "LASTNAME",
|
||||
"role": "student",
|
||||
"email": "ID@DOMAIN",
|
||||
"groups": [
|
||||
"GRUP",
|
||||
"student"
|
||||
],
|
||||
"quota": "500 MB",
|
||||
"quota_used_bytes": "0 MB"
|
||||
}]
|
|
@ -0,0 +1,21 @@
|
|||
List registered users on DD with a filter applied.
|
||||
---
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- in: body
|
||||
name: filter
|
||||
description: The filter to apply to users registered on DD
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- filter
|
||||
properties:
|
||||
text:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: |
|
||||
The list of users registered on DD with the filter applied.
|
||||
schema:
|
||||
$ref: '#/definitions/Users'
|
|
@ -553,3 +553,27 @@ class ApiViewsTests(flask_unittest.ClientTestCase):
|
|||
# rv = self._r(client, "/ddapi/role/users", method="POST")
|
||||
# print(rv)
|
||||
# print(rv.json)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
if "DOMAIN" not in os.environ:
|
||||
os.environ["DOMAIN"] = "localhost"
|
||||
os.environ["SWAGGER_UI"] = "TRUE"
|
||||
app = _testApp()
|
||||
if "--generate-spec" in sys.argv:
|
||||
with app.app_context():
|
||||
ep = app.swag.config["specs"][0]["endpoint"]
|
||||
spec = app.swag.get_apispecs(ep)
|
||||
import json
|
||||
|
||||
print(json.dumps(spec, indent=4))
|
||||
else:
|
||||
# Start a simple testing server
|
||||
app.socketio.run(
|
||||
app,
|
||||
host=os.environ.get("ADMIN_LISTEN_ADDESS", "::"),
|
||||
port=int(os.environ.get("ADMIN_LISTEN_PORT", "9000")),
|
||||
debug=True,
|
||||
)
|
||||
|
|
|
@ -77,3 +77,8 @@ Un cop fet això, a la interfície d'administració de Keycloak haurem de triar
|
|||
> **Nota:** el directori dd-custom no s'actualitzarà mai, és responsabilitat
|
||||
> vostra revisar els canvis al tema `dd` i al directori `dd-custom.sample`
|
||||
> per tal de mantenir la compatibilitat amb els vostres canvis.
|
||||
|
||||
## Integració amb altres eines
|
||||
|
||||
És possible integrar el DD amb altres eines, vegeu la secció
|
||||
d'[integracions](integrations.ca.md).
|
||||
|
|
|
@ -0,0 +1,554 @@
|
|||
{
|
||||
"info": {
|
||||
"title": "DD API",
|
||||
"description": "DD API for external integrations",
|
||||
"version": "2022.11.0",
|
||||
"termsOfService": ""
|
||||
},
|
||||
"paths": {
|
||||
"/ddapi/users": {
|
||||
"get": {
|
||||
"summary": "List all registered users on DD.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The list of users registered on DD",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Users"
|
||||
},
|
||||
"examples": "[{\n \"keycloak_id\": \"a773d249-a113-4542-8101-3a50f4cd28c2\",\n \"id\": \"ID\",\n \"username\": \"ID\",\n \"enabled\": true,\n \"first\": \"NAME\",\n \"last\": \"LASTNAME\",\n \"role\": \"student\",\n \"email\": \"ID@DOMAIN\",\n \"groups\": [\n \"GRUP\",\n \"student\"\n ],\n \"quota\": \"500 MB\",\n \"quota_used_bytes\": \"0 MB\"\n}]\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ddapi/users/filter": {
|
||||
"post": {
|
||||
"summary": "List registered users on DD with a filter applied.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The list of users registered on DD with the filter applied.\n",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Users"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "filter",
|
||||
"description": "The filter to apply to users registered on DD",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"filter"
|
||||
],
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ddapi/groups": {
|
||||
"get": {
|
||||
"summary": "List all registered groups on DD.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The list of groups registered on DD",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Groups"
|
||||
},
|
||||
"examples": "[{\n \"keycloak_id\": \"f6ec2bda-bec9-415f-bcb7-f5ae644bfec5\",\n \"id\": \"ID\",\n \"name\": \"NAME\",\n \"path\": \"PATH\",\n \"description\": \"DESCRIPITON\",\n}]\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ddapi/roles": {
|
||||
"get": {
|
||||
"summary": "List all roles configured on DD.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The list of roles configured on DD",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Roles"
|
||||
},
|
||||
"examples": "[{\n \"keycloak_id\": \"f6ec2bda-bec9-415f-bcb7-f5ae644bfec5\",\n \"id\": \"ID\",\n \"name\": \"NAME\",\n \"description\": \"DESCRIPITON\",\n}]\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ddapi/role/users": {
|
||||
"post": {
|
||||
"summary": "List registered users on DD with a the given role.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The list of users registered on DD with the filter applied.\n",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Users"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "role",
|
||||
"description": "The role to search users registered on DD.\nOne of 'id', 'name' and 'keycloak_id' must be provided.\nThis is also the order in which the parameters are checked,\nin case multiple are provided.\n",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"required": false
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": false
|
||||
},
|
||||
"keycloak_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"required": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ddapi/user/{user_ddid}": {
|
||||
"get": {
|
||||
"summary": "Get a registered user in DD",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The user as it exists on DD\n",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/User"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "The user does not exist\n"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"in": "url",
|
||||
"name": "user_ddid",
|
||||
"description": "The user to retrieve from DD\n",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"summary": "Delete a registered user in DD",
|
||||
"responses": {
|
||||
"200": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "The user does not exist\n"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"in": "url",
|
||||
"name": "user_ddid",
|
||||
"description": "The user to delete from DD\n",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"summary": "Modify a user in DD",
|
||||
"responses": {
|
||||
"200": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "The user does not exist\n"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "user",
|
||||
"description": "The user to be modified on DD.\n",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"first": {
|
||||
"required": false,
|
||||
"type": "string"
|
||||
},
|
||||
"last": {
|
||||
"required": false,
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
},
|
||||
"password": {
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
},
|
||||
"password_temporary": {
|
||||
"required": false,
|
||||
"type": "bool"
|
||||
},
|
||||
"quota": {
|
||||
"required": false,
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"required": false,
|
||||
"type": "bool"
|
||||
},
|
||||
"role": {
|
||||
"required": false
|
||||
},
|
||||
"groups": {
|
||||
"required": false,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ddapi/user": {
|
||||
"post": {
|
||||
"summary": "Register a new user in DD",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The keycloak_id of the newly registered user\n",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/KeycloakId"
|
||||
},
|
||||
"examples": "{ \"keycloak_id\": \"f6ec2bda-bec9-415f-bcb7-f5ae644bfec5\" }\n"
|
||||
},
|
||||
"409": {
|
||||
"description": "The user already exists\n"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "user",
|
||||
"description": "The user to be registered on DD.\n",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"username": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"first": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"last": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
},
|
||||
"password": {
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
},
|
||||
"password_temporary": {
|
||||
"required": false,
|
||||
"type": "bool"
|
||||
},
|
||||
"quota": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"required": true,
|
||||
"type": "bool"
|
||||
},
|
||||
"role": {
|
||||
"required": true
|
||||
},
|
||||
"groups": {
|
||||
"required": true,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ddapi/group/{group_id}": {
|
||||
"get": {
|
||||
"summary": "Get a registered group in DD",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The group as it exists on DD\n",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Group"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "The group does not exist\n"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"in": "url",
|
||||
"name": "group_id",
|
||||
"description": "The group to retrieve from DD\n",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"summary": "Delete a registered group in DD",
|
||||
"responses": {
|
||||
"200": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "The group does not exist\n"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"in": "url",
|
||||
"name": "group_id",
|
||||
"description": "The group to delete from DD\n",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ddapi/group": {
|
||||
"post": {
|
||||
"summary": "Register a new group in DD",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The keycloak_id of the newly registered group\n",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/KeycloakId"
|
||||
}
|
||||
},
|
||||
"409": {
|
||||
"description": "The group already exists\n"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "group",
|
||||
"description": "The group to be registered on DD.\n",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"required": false,
|
||||
"type": "string"
|
||||
},
|
||||
"parent": {
|
||||
"required": false,
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"User": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"keycloak_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"first": {
|
||||
"type": "string"
|
||||
},
|
||||
"last": {
|
||||
"type": "string"
|
||||
},
|
||||
"role": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"fomat": "email"
|
||||
},
|
||||
"groups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"quota": {
|
||||
"type": "string"
|
||||
},
|
||||
"quota_used_bytes": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Users": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/User"
|
||||
}
|
||||
},
|
||||
"Group": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"keycloak_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Groups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Group"
|
||||
}
|
||||
},
|
||||
"Role": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"keycloak_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Roles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Role"
|
||||
}
|
||||
},
|
||||
"KeycloakId": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"keycloak_id": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"swagger": "2.0",
|
||||
"externalDocs": {
|
||||
"description": "Online Documentation",
|
||||
"url": "https://dd.digitalitzacio-democratica.xnet-x.net/docs/"
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"dd_jwt": {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "Authorization",
|
||||
"description": "JWS token using API_SECRET (e.g. 'bearer X.Y')"
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"dd_jwt": {
|
||||
"$ref": "#/securityDefinitions/dd_jwt"
|
||||
}
|
||||
},
|
||||
"swagger_ui": true
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
# Integracions
|
||||
|
||||
El DD es pot integrar amb altres sistemes a través de les seves APIs.
|
||||
|
||||
## Autenticació
|
||||
|
||||
Totes les peticions han d'estar autenticades amb un [Json Web Token (JWT)][jwt],
|
||||
que estigui signat per l'`API_SECRET` (present al fitxer `dd.conf`).
|
||||
|
||||
Aquesta autenticació es fa mitjançant la capcelera HTTP `Authentication`.
|
||||
|
||||
<details>
|
||||
<summary>Vegeu-ne els detalls</summary>
|
||||
```sh
|
||||
> curl -H "Authorization: bearer ${jwt}" https://admin.DOMAIN/ddapi/roles
|
||||
[
|
||||
{
|
||||
"keycloak_id": "9325ad99-7e04-4c31-9768-5512e1564160",
|
||||
"id": "admin",
|
||||
"name": "admin",
|
||||
"description": "${role_admin}"
|
||||
},
|
||||
{
|
||||
"keycloak_id": "c6c8a73e-51fc-4716-831d-1dfc0e0b62b0",
|
||||
"id": "manager",
|
||||
"name": "manager",
|
||||
"description": "Realm managers"
|
||||
},
|
||||
{
|
||||
"keycloak_id": "24d7977e-da83-4591-8e13-0fac3126afa1",
|
||||
"id": "student",
|
||||
"name": "student",
|
||||
"description": "Realm students"
|
||||
},
|
||||
{
|
||||
"keycloak_id": "d6699c41-13d5-4623-bdca-e5f2775474ed",
|
||||
"id": "teacher",
|
||||
"name": "teacher",
|
||||
"description": "Realm teachers"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
On el <code>JWT</code> es pot generar, per exemple fent servir <code>python-jose</code>, de la
|
||||
següent manera:
|
||||
|
||||
```python
|
||||
import os
|
||||
from jose import jws
|
||||
t = jws.sign({}, os.environ["API_SECRET"], algorithm="HS256")
|
||||
print(t)
|
||||
```
|
||||
|
||||
Altres llenguatges de programació i llibreries tindran una manera anàloga de
|
||||
generar aquests tokens.
|
||||
</details>
|
||||
|
||||
[jwt]: https://jwt.io/
|
||||
[jose]: https://python-jose.readthedocs.io/
|
||||
|
||||
## API
|
||||
|
||||
!!swagger ddapi.json!!
|
|
@ -42,6 +42,7 @@ markdown_extensions:
|
|||
plugins:
|
||||
- search
|
||||
#- enumerate-headings
|
||||
- render_swagger
|
||||
- i18n:
|
||||
languages:
|
||||
ca: "Català"
|
||||
|
|
Loading…
Reference in New Issue