Compare commits

...

10 Commits

Author SHA1 Message Date
Cayo Puigdefabregas 2283f20ab2 fix access to table with schema and without 2024-03-27 17:32:57 +01:00
Cayo Puigdefabregas a4c7b2a744 add new flow for get credentials from wallet 2024-03-26 18:51:46 +01:00
Cayo Puigdefabregas da8d43f9f6 fix new dlt_keys structure 2024-03-26 18:25:06 +01:00
Cayo Puigdefabregas f0710e88ec first step 2024-03-26 17:52:58 +01:00
Cayo Puigdefabregas 55839a26ea fix did 2024-02-22 17:49:44 +01:00
Cayo Puigdefabregas 39f0300a28 fix register_user_dlt when there are more than one user 2024-02-22 11:57:05 +01:00
Cayo Puigdefabregas e5dbb09025 fix roles 2024-02-21 16:19:39 +01:00
Cayo Puigdefabregas c948b0bca5 update readme 2024-02-20 15:59:20 +01:00
Cayo Puigdefabregas cd440b9931 fix get role in register_user_dlt 2024-02-16 20:59:21 +01:00
Cayo Puigdefabregas 44b1a245b6 fix get role in register_user_dlt 2024-02-16 20:34:36 +01:00
8 changed files with 89 additions and 95 deletions

View File

@ -12,7 +12,7 @@ Devicehub relies on the existence of an [API_DLT connector](https://gitlab.com/d
Please visit the [Manual Installation](README_MANUAL_INSTALLATION.md) instructions to understand the detailed steps to install it locally or deploy it on a server. However, we recommend the following Docker deployment process. Please visit the [Manual Installation](README_MANUAL_INSTALLATION.md) instructions to understand the detailed steps to install it locally or deploy it on a server. However, we recommend the following Docker deployment process.
# Docker # Docker
There is a Docker compose file for an automated deployment. The following steps describe how to run and use it. There is a Docker compose file for an automated deployment. Two instances of DeviceHub will be deployed. The following steps describe how to run and use it.
1. Download the sources: 1. Download the sources:
``` ```
@ -20,7 +20,7 @@ There is a Docker compose file for an automated deployment. The following steps
cd devicehub-teal cd devicehub-teal
``` ```
2. If you want to initialise your DeviceHub instance with sample device snapshots, copy it/them into that directory. e.g. 2. If you want to initialise one of DeviceHub instances (running on port 5000) with sample device snapshots, copy it/them into that directory. e.g.
``` ```
cp snapshot01.json examples/snapshots/ cp snapshot01.json examples/snapshots/
``` ```
@ -30,7 +30,7 @@ There is a Docker compose file for an automated deployment. The following steps
IMPORT_SNAPSHOTS='n' IMPORT_SNAPSHOTS='n'
``` ```
To register new devices, the [workbench software](https://github.com/eReuse/workbench) can be run on a device to generate its hardware snapshot that can be uploaded to your DeviceHub instance. To register new devices, the [workbench software](https://github.com/eReuse/workbench) can be run on a device to generate its hardware snapshot that can be uploaded to one of the two DeviceHub instance.
3. Setup the environment variables in the .env file. You can find one example in examples/env.example. 3. Setup the environment variables in the .env file. You can find one example in examples/env.example.
If you don't have any, you can copy that example and modify the basic vars If you don't have any, you can copy that example and modify the basic vars
@ -45,15 +45,12 @@ You can use these parameters as default for a local test, but default values may
ABAC_TOKEN ABAC_TOKEN
ABAC_USER ABAC_USER
ABAC_URL ABAC_URL
```
These values should come from an already operational [API_DLT connector](https://gitlab.com/dsg-upc/ereuse-dpp) service instance.
If you want to use OIDC4VP, you need to set the vars:
```
SERVER_ID_FEDERATED SERVER_ID_FEDERATED
CLIENT_ID_FEDERATED CLIENT_ID_FEDERATED
``` ```
You can see the [manual install step 9]('https://github.com/eReuse/devicehub-teal/blob/oidc4vp/README_MANUAL_INSTALLATION.md#installing') for more details. The first six values should come from an already operational [API_DLT connector](https://gitlab.com/dsg-upc/ereuse-dpp) service instance.
For the last two values check [manual install step 9]('https://github.com/eReuse/devicehub-teal/blob/oidc4vp/README_MANUAL_INSTALLATION.md#installing') for more details.
4. Build and run the docker containers: 4. Build and run the docker containers:
``` ```

View File

@ -110,6 +110,7 @@ class DevicehubConfig(Config):
ABAC_TOKEN = config('ABAC_TOKEN', None) ABAC_TOKEN = config('ABAC_TOKEN', None)
ABAC_COOKIE = config('ABAC_COOKIE', None) ABAC_COOKIE = config('ABAC_COOKIE', None)
ABAC_URL = config('ABAC_URL', None) ABAC_URL = config('ABAC_URL', None)
VERIFY_URL = config('VERIFY_URL', None)
"""Definition of oauth jwt details.""" """Definition of oauth jwt details."""
OAUTH2_JWT_ENABLED = config('OAUTH2_JWT_ENABLED', False) OAUTH2_JWT_ENABLED = config('OAUTH2_JWT_ENABLED', False)

View File

@ -70,7 +70,10 @@ class LoginForm(FlaskForm):
self.form_errors.append(self.error_messages['inactive']) self.form_errors.append(self.error_messages['inactive'])
if 'dpp' in app.blueprints.keys(): if 'dpp' in app.blueprints.keys():
dlt_keys = user.get_dlt_keys(self.password.data) dlt_keys = user.get_dlt_keys(
self.password.data
).get('data', {})
token_dlt = dlt_keys.get('api_token') token_dlt = dlt_keys.get('api_token')
eth_pub_key = dlt_keys.get('eth_pub_key') eth_pub_key = dlt_keys.get('eth_pub_key')
session['token_dlt'] = token_dlt session['token_dlt'] = token_dlt

View File

@ -62,7 +62,9 @@ class DidView(View):
"isOperator": "operator.html", "isOperator": "operator.html",
"isVerifier": "verifier.html", "isVerifier": "verifier.html",
"operator": "operator.html", "operator": "operator.html",
"Operator": "operator.html",
"verifier": "verifier.html", "verifier": "verifier.html",
"Verifier": "verifier.html",
} }
self.template_name = tlmp.get(rol, self.template_name) self.template_name = tlmp.get(rol, self.template_name)
@ -87,7 +89,7 @@ class DidView(View):
if not g.user.is_authenticated and not rols: if not g.user.is_authenticated and not rols:
return [] return []
if rols: if rols and rols != [('', '')]:
self.context['rols'] = rols self.context['rols'] = rols
if 'dpp' not in app.blueprints.keys(): if 'dpp' not in app.blueprints.keys():
@ -96,10 +98,13 @@ class DidView(View):
if not session.get('token_dlt'): if not session.get('token_dlt'):
return [] return []
_role = g.user.get_rols_dlt()
role = session.get('iota_abac_attributes', {}).get('role', '') role = session.get('iota_abac_attributes', {}).get('role', '')
if not role:
if not _role:
return [] return []
self.context['rols'] = [(x.strip(), x.strip()) for x in role.split(",")] self.context['rols'] = _role
return _role
def get_rol(self): def get_rol(self):
rols = self.context.get('rols', []) rols = self.context.get('rols', [])

View File

@ -1,7 +1,9 @@
import json import json
import requests
import click import click
from ereuseapi.methods import API
from flask import g, current_app as app from flask import g, current_app as app
from ereuseapi.methods import register_user from ereuseapi.methods import register_user
from ereuse_devicehub.db import db from ereuse_devicehub.db import db
@ -11,7 +13,7 @@ from ereuse_devicehub.modules.dpp.utils import encrypt
class RegisterUserDlt: class RegisterUserDlt:
# "Operator", "Verifier" or "Witness" # "operator", "verifier" or "witness"
def __init__(self, app) -> None: def __init__(self, app) -> None:
super().__init__() super().__init__()
@ -27,15 +29,13 @@ class RegisterUserDlt:
for d in dataset: for d in dataset:
self.add_user(d) self.add_user(d)
db.session.commit() db.session.commit()
def add_user(self, data): def add_user(self, data):
email = data.get("email") email = data.get("email")
name = email.split('@')[0] name = email.split('@')[0]
password = data.get("password") password = data.get("password")
api_dlt = app.config.get('API_DLT') ethereum = {"data": data.get("data")}
eth_priv_key = data.get("eth_priv_key")
eth_pub_key = data.get("eth_pub_key")
user = User.query.filter_by(email=email).first() user = User.query.filter_by(email=email).first()
@ -43,31 +43,20 @@ class RegisterUserDlt:
user = User(email=email, password=password) user = User(email=email, password=password)
user.individuals.add(Person(name=name)) user.individuals.add(Person(name=name))
try:
response = register_user(api_dlt, privateKey=eth_priv_key[2:])
api_token = response.get('data', {}).get('api_token')
except Exception:
api_token = ""
ethereum = {
"eth_pub_key": eth_pub_key,
"eth_priv_key": eth_priv_key,
"api_token": api_token
}
data_eth = json.dumps(ethereum) data_eth = json.dumps(ethereum)
user.api_keys_dlt = encrypt(password, data_eth) user.api_keys_dlt = encrypt(password, data_eth)
try: roles = []
# TODO Not works token_dlt = ethereum["data"]["api_token"]
with app.app_context(): api_dlt = app.config.get('API_DLT')
ses = g.get('session', None) api = API(api_dlt, token_dlt, "ethereum")
ses["eth_pub_key"] = eth_pub_key result = api.check_user_roles()
attributes = user.get_abac_attributes()
roles = attributes.get("role", ["Operator"]) if result.get('Status') == 200:
except Exception: if 'Success' in result.get('Data', {}).get('status'):
roles = ["Operator"] rols = result.get('Data', {}).get('data', {})
roles = [(k, k) for k, v in rols.items() if v]
user.rols_dlt = json.dumps(roles) user.rols_dlt = json.dumps(roles)
# if not user.id:
db.session.add(user) db.session.add(user)

View File

@ -49,6 +49,30 @@ def upgrade():
op.execute(f"CREATE SEQUENCE {get_inv()}.code_roles_seq;") op.execute(f"CREATE SEQUENCE {get_inv()}.code_roles_seq;")
op.create_table(
'code_roles',
sa.Column('id', sa.BigInteger(), nullable=False),
sa.Column(
'updated',
sa.TIMESTAMP(timezone=True),
server_default=sa.text('CURRENT_TIMESTAMP'),
nullable=False,
),
sa.Column(
'created',
sa.TIMESTAMP(timezone=True),
server_default=sa.text('CURRENT_TIMESTAMP'),
nullable=False,
),
sa.Column('code', citext.CIText(), nullable=False),
sa.Column('roles', citext.CIText(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.execute(f"CREATE SEQUENCE code_roles_seq;")
def downgrade(): def downgrade():
op.drop_table('code_roles', schema=f'{get_inv()}') op.drop_table('code_roles', schema=f'{get_inv()}')
op.execute(f"DROP SEQUENCE {get_inv()}.code_roles_seq;") op.execute(f"DROP SEQUENCE {get_inv()}.code_roles_seq;")
op.drop_table('code_roles')
op.execute(f"DROP SEQUENCE code_roles_seq;")

View File

@ -6,6 +6,7 @@ from authlib.integrations.sqla_oauth2 import (
from flask import g from flask import g
from werkzeug.security import gen_salt from werkzeug.security import gen_salt
from flask import current_app
from ereuse_devicehub.db import db from ereuse_devicehub.db import db
from ereuse_devicehub.resources.models import Thing from ereuse_devicehub.resources.models import Thing
from ereuse_devicehub.resources.user.models import User from ereuse_devicehub.resources.user.models import User
@ -81,8 +82,8 @@ class OAuth2Token(Thing, OAuth2TokenMixin):
member = db.relationship('MemberFederated') member = db.relationship('MemberFederated')
class Code2Roles(Thing): class CodeRoles(Thing):
__tablename__ = 'code_roles' # __tablename__ = 'code_roles'
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(40), default=gen_code, nullable=False) code = db.Column(db.String(40), default=gen_code, nullable=False)

View File

@ -29,7 +29,7 @@ from ereuse_devicehub.modules.oidc.forms import (
from ereuse_devicehub.modules.oidc.models import ( from ereuse_devicehub.modules.oidc.models import (
MemberFederated, MemberFederated,
OAuth2Client, OAuth2Client,
Code2Roles CodeRoles
) )
from ereuse_devicehub.modules.oidc.oauth2 import ( from ereuse_devicehub.modules.oidc.oauth2 import (
authorization, authorization,
@ -132,16 +132,18 @@ class SelectInventoryView(GenericMixin):
def dispatch_request(self): def dispatch_request(self):
host = app.config.get('HOST', '').strip("/") host = app.config.get('HOST', '').strip("/")
url = "https://ebsi-pcp-wallet-ui.vercel.app/oid4vp?"
url += f"client_id=https://{host}&"
url += "presentation_definition_uri=https://iotaledger.github.io"
# url += "/ebsi-stardust-components/public/presentation-definition-ex1.json"
url += "/ebsi-stardust-components/public//presentation-definition-ereuse.json&"
url += f"response_uri=https://{host}/allow_code_oidc4vp"
url += "&state=1700822573400&response_type=vp_token&response_mode=direct_post"
url += "&nonce=DybC3A=="
next = request.args.get('next', '#') next = request.args.get('next', '#')
# url = "https://ebsi-pcp-wallet-ui.vercel.app/oid4vp?"
# url += f"client_id=https://{host}&"
# url += "presentation_definition_uri=https://iotaledger.github.io"
# url += "/ebsi-stardust-components/public/presentation-definition-ex1.json"
# url += "/ebsi-stardust-components/public//presentation-definition-ereuse.json&"
# url += f"response_uri=https://{host}/allow_code_oidc4vp"
# url += "&state=1700822573400&response_type=vp_token&response_mode=direct_post"
url = app.config.get('VERIFY_URL')
url += f"?response_uri=http://{host}:5000/allow_code_oidc4vp"
url += '&presentation_definition=["EOperatorClaim"]'
session['next_url'] = next session['next_url'] = next
return redirect(url, code=302) return redirect(url, code=302)
@ -233,7 +235,7 @@ class AllowCodeOidc4vpView(GenericMixin):
if not vcredential: if not vcredential:
return jsonify({"error": "No there are credentials"}) return jsonify({"error": "No there are credentials"})
roles = self.verify(vcredential) roles = self.get_roles(vcredential)
if not roles: if not roles:
return jsonify({"error": "No there are roles"}) return jsonify({"error": "No there are roles"})
@ -242,55 +244,27 @@ class AllowCodeOidc4vpView(GenericMixin):
return jsonify({"redirect_uri": uri}) return jsonify({"redirect_uri": uri})
def get_credential(self): def get_credential(self):
self.vp_token = request.values.get("vp_token") pv = request.values.get("vp_token")
pv = self.vp_token.split(".") self.code = request.values.get("code")
token = json.loads(base64.b64decode(pv[1]).decode()) token = json.loads(base64.b64decode(pv).decode())
return token.get('vp', {}).get("verifiableCredential") return token.get("verifiableCredential")
def verify(self, vcredential):
WALLET_INX_EBSI_PLUGIN_TOKEN = app.config.get(
'WALLET_INX_EBSI_PLUGIN_TOKEN'
)
WALLET_INX_EBSI_PLUGIN_URL = app.config.get(
'WALLET_INX_EBSI_PLUGIN_URL'
)
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {WALLET_INX_EBSI_PLUGIN_TOKEN}'
}
for v in vcredential:
data = json.dumps({
"type": "VerificationRequest",
"jwtCredential": v
})
result = requests.post(
WALLET_INX_EBSI_PLUGIN_URL,
headers=headers,
data=data
)
if result.status_code != 200:
return
vps = json.loads(result.text)
try:
roles = vps['credential']['credentialSubject'].get('role')
except Exception:
roles = None
if roles:
break
if not vps.get('verified'):
return
def get_roles(self, vps):
try:
for vp in vps:
roles = vp.get('credentialSubject', {}).get('role')
if roles:
return roles
except Exception:
roles = None
return roles return roles
def get_response_uri(selfi, roles): def get_response_uri(selfi, roles):
code = Code2Roles(roles=roles) code = CodeRoles(roles=roles)
db.session.add(code) db.session.add(code)
db.session.commit() db.session.commit()
url = "https://{host}/allow_code_oidc4vp2?code={code}".format( url = "http://{host}:5000/allow_code_oidc4vp2?code={code}".format(
host=app.config.get('HOST'), host=app.config.get('HOST'),
code=code.code code=code.code
) )
@ -314,7 +288,7 @@ class AllowCodeOidc4vp2View(View):
return redirect(url) return redirect(url)
def get_user_info(self): def get_user_info(self):
code = Code2Roles.query.filter_by(code=self.code).first() code = CodeRoles.query.filter_by(code=self.code).first()
if not code: if not code:
return return