Compare commits
10 Commits
f9ec594a0e
...
2283f20ab2
Author | SHA1 | Date |
---|---|---|
Cayo Puigdefabregas | 2283f20ab2 | |
Cayo Puigdefabregas | a4c7b2a744 | |
Cayo Puigdefabregas | da8d43f9f6 | |
Cayo Puigdefabregas | f0710e88ec | |
Cayo Puigdefabregas | 55839a26ea | |
Cayo Puigdefabregas | 39f0300a28 | |
Cayo Puigdefabregas | e5dbb09025 | |
Cayo Puigdefabregas | c948b0bca5 | |
Cayo Puigdefabregas | cd440b9931 | |
Cayo Puigdefabregas | 44b1a245b6 |
15
README.md
15
README.md
|
@ -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:
|
||||
```
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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', [])
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Reference in New Issue