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.
# 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:
```
@ -20,7 +20,7 @@ There is a Docker compose file for an automated deployment. The following steps
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/
```
@ -30,7 +30,7 @@ There is a Docker compose file for an automated deployment. The following steps
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.
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_USER
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
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:
```

View File

@ -110,6 +110,7 @@ class DevicehubConfig(Config):
ABAC_TOKEN = config('ABAC_TOKEN', None)
ABAC_COOKIE = config('ABAC_COOKIE', None)
ABAC_URL = config('ABAC_URL', None)
VERIFY_URL = config('VERIFY_URL', None)
"""Definition of oauth jwt details."""
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'])
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')
eth_pub_key = dlt_keys.get('eth_pub_key')
session['token_dlt'] = token_dlt

View File

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

View File

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

View File

@ -49,6 +49,30 @@ def upgrade():
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():
op.drop_table('code_roles', schema=f'{get_inv()}')
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 werkzeug.security import gen_salt
from flask import current_app
from ereuse_devicehub.db import db
from ereuse_devicehub.resources.models import Thing
from ereuse_devicehub.resources.user.models import User
@ -81,8 +82,8 @@ class OAuth2Token(Thing, OAuth2TokenMixin):
member = db.relationship('MemberFederated')
class Code2Roles(Thing):
__tablename__ = 'code_roles'
class CodeRoles(Thing):
# __tablename__ = 'code_roles'
id = db.Column(db.Integer, primary_key=True)
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 (
MemberFederated,
OAuth2Client,
Code2Roles
CodeRoles
)
from ereuse_devicehub.modules.oidc.oauth2 import (
authorization,
@ -132,16 +132,18 @@ class SelectInventoryView(GenericMixin):
def dispatch_request(self):
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', '#')
# 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
return redirect(url, code=302)
@ -233,7 +235,7 @@ class AllowCodeOidc4vpView(GenericMixin):
if not vcredential:
return jsonify({"error": "No there are credentials"})
roles = self.verify(vcredential)
roles = self.get_roles(vcredential)
if not roles:
return jsonify({"error": "No there are roles"})
@ -242,55 +244,27 @@ class AllowCodeOidc4vpView(GenericMixin):
return jsonify({"redirect_uri": uri})
def get_credential(self):
self.vp_token = request.values.get("vp_token")
pv = self.vp_token.split(".")
token = json.loads(base64.b64decode(pv[1]).decode())
return token.get('vp', {}).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
pv = request.values.get("vp_token")
self.code = request.values.get("code")
token = json.loads(base64.b64decode(pv).decode())
return token.get("verifiableCredential")
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
def get_response_uri(selfi, roles):
code = Code2Roles(roles=roles)
code = CodeRoles(roles=roles)
db.session.add(code)
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'),
code=code.code
)
@ -314,7 +288,7 @@ class AllowCodeOidc4vp2View(View):
return redirect(url)
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:
return