Skip to content
Snippets Groups Projects
Commit 78d4fafd authored by Jannis Grundmann's avatar Jannis Grundmann
Browse files

- added client registration via Initial Access Token

- added jwt request via api
parent f6050cdc
Branches
No related tags found
No related merge requests found
# OntoDocker
Runs a Flask application-prototype to access a Blazegraph instance via GUI and API.
<br/>
- API authentication via JWT and OIDC. The user needs the Keycloak role "ontodocker".
<br/>
Allowed Content-Types to upload are "text/turtle" and "application/rdf+xml" as .ttl/.rdf files
Runs a Flask application-prototype to access a Blazegraph and Jena instance via GUI and API.
<br/>
Initial OIDC setup:
- Request an Initial Access Token from am MatDigi-Admin
- save as ia.txt
- copy ia.txt to ontodocker-container via ```docker cp ia.txt ontodocker:/app/app```
```bash
docker-compose exec ontodocker bash
cd /app/app
oidc-register --initial-access-token $(cat ia.txt) https://sso.material-digital.de/auth/realms/material-digital https://ontodocker.material-digital.de
exit
```
Refer to api_usage_examples.py for examples on how to use the API.
- Replace KEYCLOAK_URL, REALM_NAME and APPLICATION_URL in ```provider_info.json``` accordingly
- Request an Initial Access Token (save as ```ia.jwt```)
- Copy both files to ```./flask_app/app/```
- In case You cannot build and deploy on the same machine, see the comments in ```docker-compose.yml```
- Start container with ```docker-compose up --build```
<br/>
Refer to ```api_usage_examples.py``` for examples on how to use the API.
Allowed Content-Types for uploading are "text/turtle" and "application/rdf+xml" as .ttl/.rdf files
<br/>
<br/>
Graph visualization via WebVOWL 1.1.7
......
import json
import requests
if __name__ == "__main__":
jwt = "eyJhbG..." #get API key from the top Button on the landingpage
# request jwt or get the JWT from the Button "API Key" on the landingpage
username = "user"
password = "1234"
login_response = json.loads(requests.post('http://127.0.0.1/api/login', auth=(username, password)).content.decode())
jwt = login_response["access_token"]
print(jwt)
headers = {"Authorization": f"Bearer {jwt}"}
# get all dataset ids
print(requests.get('http://127.0.0.1/api/ds/all', headers=headers).content.decode())
exit()
# create dataset
print(requests.put('http://127.0.0.1/api/jena/test/create', headers=headers).content.decode())
......
version: '3.2'
services:
ontodocker:
build: ./flask_app
restart: always
depends_on:
- blazegraph
- jena
networks:
- default
command: gunicorn -w 1 -b :8000 run:ontoapp
nginx:
build: ./nginx
restart: always
ports:
- "80:80"
depends_on:
- ontodocker
networks:
- default
blazegraph:
image: nawer/blazegraph
restart: always
#ports:
# - '9999:9999'
volumes:
- "./data/blazegraph/:/var/lib/blazegraph"
networks:
- default
jena:
image: zacanbot/fuseki
#ports:
# - '3030:3030'
volumes:
- './data/jena/:/data/fuseki'
networks:
- default
......@@ -9,7 +9,6 @@ services:
# image: <ontodocker_image_id>
# volumes:
# - ./flask_app/app/client_secrets.json:/app/app/client_secrets.json:ro
restart: always
depends_on:
- blazegraph
......
import json
import os
import jpype
......@@ -27,7 +28,23 @@ def create_app():
app = Flask(__name__)
app.config.from_object(config.get("default"))
# register client via Initial Access Token
if not os.path.isfile(app.config['OIDC_CLIENT_SECRETS']):
from flask_oidc.registration import register_client
with open(app.config['OIDC_PROVIDER_INFO']) as pi:
provider_info = json.load(pi)
application_url = provider_info["application_url"]
redirect_uris = [f'{application_url}/oidc_callback']
with open(app.config['OIDC_INIT_ACCESS_TOKEN']) as ia:
initial_access_token = ia.read()
with open(app.config['OIDC_CLIENT_SECRETS'], "w") as file_secret:
secret_info = register_client(provider_info, redirect_uris, initial_access_token)
if not secret_info.get("token_introspection_uri"):
secret_info["web"]["token_introspection_uri"] = secret_info["web"]["token_uri"]+"/introspect"
json.dump(secret_info, file_secret)
oidc.init_app(app)
bootstrap.init_app(app)
login_manager.init_app(app)
......
from flask import Blueprint
from flask import Blueprint, current_app
from . import oidc
import requests
import json
from flask import jsonify, request
from .triplestores.blazegraph import get_blazeconn, BlazeConnection
from .triplestores.jena import get_jenaconn, FusekiConnection
from .models import User
from . import db
api = Blueprint('api', __name__)
@api.route('/api/login', methods=['GET', 'POST'])
def login_user():
"""
The function uses the users's credential passed-in, in addition to the info from
client-secret.json to request keycloak api for a token
:return: if successful, a dictionary of token, refresh_token, and expiry time og the both token and refresh token
"""
auth = request.authorization
with open(current_app.config['OIDC_CLIENT_SECRETS']) as file_secret:
secret_info = json.load(file_secret)
data = {"username": auth['username'], "password": auth['password'],
"client_id": secret_info['web']['client_id'], "grant_type": "password",
"client_secret": secret_info['web']['client_secret']}
response = requests.post(secret_info['web']['token_uri'],
headers={"Content-Type": "application/x-www-form-urlencoded"},
data=data)
if response.status_code != 200:
return jsonify({'login': 'failed'})
else:
token = response.json()['access_token']
expires_in = response.json()['expires_in']
user = User.query.filter_by(email=auth['username']).first()
# register if not existent
if not user:
# create a new user
user = User(email=auth['username'])
# add the new user to the database
db.session.add(user)
db.session.commit()
return jsonify(
{'access_token': token,
'expired in (Seconds)': expires_in,
}
)
##### DatasetResource
@api.route('/api/ds/all', methods=['GET'])
@oidc.accept_token(require_token=True)
......
......@@ -10,8 +10,6 @@ from .models import User
auth = Blueprint('auth', __name__)
def create_api_key():
# thx to https://int-gitlab.int.kit.edu/architektur/docker-compose-registry/-/blob/master/app_registry/app/routes.py#L50
with open(current_app.config['OIDC_CLIENT_SECRETS']) as file_secret:
secret_info = json.load(file_secret)
......@@ -25,6 +23,10 @@ def create_api_key():
if response.status_code == 200:
return response.json()['access_token']
elif response.status_code == 401:
return "Ask Keycloak admin to activate Authorization Enabled"
else:
return response.status_code
@login_manager.user_loader
......
......@@ -10,6 +10,8 @@ class Config:
OIDC_ENABLED = True
OIDC_CLIENT_SECRETS = os.path.join(basedir, 'client_secrets.json')
OIDC_PROVIDER_INFO = os.path.join(basedir, 'provider_info.json')
OIDC_INIT_ACCESS_TOKEN = os.path.join(basedir, 'ia.jwt')
OIDC_USER_INFO_ENABLED = True
OIDC_SCOPES = ['openid', 'email', 'profile']
OIDC_ID_TOKEN_COOKIE_TTL = 10
......
{
"issuer" : "https://KEYCLOAK_URL/auth/realms/REALM_NAME",
"userinfo_endpoint" : "https://KEYCLOAK_URL/auth/realms/REALM_NAME/protocol/openid-connect/userinfo",
"token_endpoint" : "https://KEYCLOAK_URL/auth/realms/REALM_NAME/protocol/openid-connect/token",
"authorization_endpoint" : "https://KEYCLOAK_URL/auth/realms/REALM_NAME/protocol/openid-connect/auth",
"registration_endpoint" : "https://KEYCLOAK_URL/auth/realms/REALM_NAME/clients-registrations/openid-connect",
"application_url" : "APPLICATION_URL"
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment