diff --git a/app/config.ini b/app/config.ini index 5a97d75..8b14dfc 100644 --- a/app/config.ini +++ b/app/config.ini @@ -14,6 +14,9 @@ keypair = /app/app/ci_keypair.keys contracts_path = /app/app/contracts/src +secret = 615872afdb35a291f56166d63399d0fa1f2a37415b24206fb051dbce294afc5c + + # OAuth2 ALGORITHM = HS256 TOKEN_SUBJECT = access diff --git a/app/models.py b/app/models.py index c6b6b67..0377a5e 100644 --- a/app/models.py +++ b/app/models.py @@ -97,6 +97,10 @@ def exists(cls, aaid, value): def total(cls): return DBSession.query(cls).count() + @classmethod + def total_by_aaid(cls, aaid): + return DBSession.query(cls).filter_by(aaid=aaid).count() + class Statistics(Base): uid = Column(Integer, primary_key=True, index=True) @@ -128,6 +132,26 @@ def aggregate(cls): return result + @classmethod + def by_aa(cls, aa): + result = {} + values = ( + DBSession.query(cls.name, cls.value, func.count(cls.value), cls.aaid) + .filter_by(aaid=aa.authorizable_attribute_id) + .group_by(cls.name, cls.value) + .all() + ) + for v in values: + existent_list = result.get(v[0], []) + existent_list.append({v[1]: v[2]}) + if v[2] >= aa.optional_k(v[0]): + result[v[0]] = existent_list + result["total"] = ValidatedCredentials.total_by_aaid( + aa.authorizable_attribute_id + ) + + return result + Base.metadata.create_all(bind=engine) diff --git a/app/routers/authorizable_attribute.py b/app/routers/authorizable_attribute.py index a674eb4..435b294 100644 --- a/app/routers/authorizable_attribute.py +++ b/app/routers/authorizable_attribute.py @@ -67,9 +67,11 @@ def authorizable_attribute( ): info = [_.json() for _ in item.authorizable_attribute_info] optional = [_.json() for _ in item.authorizable_attribute_info_optional] - keypair = ZenContract( + + keypair_contract = ZenContract( CONTRACTS.GENERATE_KEYPAIR, {"issuer_identifier": config.get("uid")} - ).execute() + ) + keypair = keypair_contract.execute() contract = ZenContract( CONTRACTS.PUBLIC_VERIFY, {"issuer_identifier": config.get("uid")} ) diff --git a/app/routers/security.py b/app/routers/security.py index 5543017..863bc8d 100644 --- a/app/routers/security.py +++ b/app/routers/security.py @@ -1,6 +1,4 @@ -import json from datetime import timedelta, datetime -from pathlib import Path import jwt from fastapi import Depends, HTTPException, APIRouter @@ -8,32 +6,12 @@ from app.config.config import BaseConfig from app.schema import TokenOutput -from app.zencontract import ZenContract, CONTRACTS router = APIRouter() config = BaseConfig() log = config.logger -def load_keypair(): - keypair = Path(config.get("keypair")) - if not keypair.is_file(): - log.info("CREATING KEYPAIR IN %s" % keypair.as_posix()) - keypair.touch() - keypair.write_text( - ZenContract( - CONTRACTS.GENERATE_KEYPAIR, {"issuer_identifier": config.get("uid")} - ).execute() - ) - - if config.getboolean("debug"): # pragma: no cover - log.debug("+" * 50) - log.debug("KEYPAIR IS: \n%s" % keypair.read_text()) - log.debug("+" * 50) - - return keypair.read_text() - - def create_access_token(*, data: dict, expires_delta: timedelta = None): to_encode = data.copy() if expires_delta: @@ -43,8 +21,7 @@ def create_access_token(*, data: dict, expires_delta: timedelta = None): minutes=config.getint("ACCESS_TOKEN_EXPIRE_MINUTES") ) to_encode.update({"exp": expire, "sub": config.get("TOKEN_SUBJECT")}) - keypair = json.loads(load_keypair()) - secret = keypair[config.get("uid")]["sign"]["x"] + secret = config.get("secret") encoded_jwt = jwt.encode(to_encode, secret, algorithm=config.get("ALGORITHM")) return encoded_jwt diff --git a/app/routers/stats.py b/app/routers/stats.py index 9666e81..3a6c18e 100644 --- a/app/routers/stats.py +++ b/app/routers/stats.py @@ -1,6 +1,7 @@ -from fastapi import APIRouter +from fastapi import APIRouter, HTTPException +from starlette.status import HTTP_204_NO_CONTENT -from app.models import Statistics +from app.models import Statistics, AuthorizableAttribute router = APIRouter() @@ -12,3 +13,18 @@ ) def index(): return Statistics.aggregate() + + +@router.get( + "/{authorizable_attribute_id}", + summary="Get an aggregated view of optional info values, for an Authorizable Attribute", + tags=["Statistics"], +) +def get_authorizable_attribute_stats(authorizable_attribute_id): + aa = AuthorizableAttribute.by_aa_id(authorizable_attribute_id) + if not aa: + raise HTTPException( + status_code=HTTP_204_NO_CONTENT, detail="Authorizable Attribute Not Found" + ) + + return Statistics.by_aa(aa) diff --git a/app/test.ini b/app/test.ini index a295be1..75cce02 100644 --- a/app/test.ini +++ b/app/test.ini @@ -1,8 +1,9 @@ [DEFAULT] debug = true -uid = Credential Issuer 01 +uid = issuer_identifier keypair = /home/travis/build/DECODEproject/dddc-credential-issuer/tests/ci_keypair.keys contracts_path = /home/travis/build/DECODEproject/dddc-credential-issuer/app/contracts/src +secret = 615872afdb35a291f56166d63399d0fa1f2a37415b24206fb051dbce294afc5c ALGORITHM = HS256 TOKEN_SUBJECT = access diff --git a/app/zencontract.py b/app/zencontract.py index 594aa69..ec43e92 100644 --- a/app/zencontract.py +++ b/app/zencontract.py @@ -40,7 +40,7 @@ def get_contract(self): contract = contracts_dir.joinpath(self.name).read_text() for k, v in self.placeholder.items(): contract = contract.replace(f"'{k}'", f"'{v}'") - return contract + return str(contract) def execute(self): if config.getboolean("debug"): # pragma: no cover @@ -50,11 +50,13 @@ def execute(self): log.debug("DATA: %s" % self._data) log.debug("KEYS: %s" % self._keys) log.debug("CODE: \n%s" % self.zencode) - - result, errors = zenroom.zencode_exec( - script=self.zencode, keys=self._keys, data=self._data - ) - self._error = errors + try: + result, errors = zenroom.zencode_exec( + script=self.zencode, keys=self._keys, data=self._data + ) + self._error = str(errors) + except Exception: + log.exception("Zenroom contract exception", exc_info=True) return result def keys(self, keys=None): diff --git a/setup.cfg b/setup.cfg index 9dc95b4..2ab11d9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,7 +7,6 @@ env = DDDC_CREDENTIAL_ISSUER_CONFIGFILE=/home/puria/src/dddc-credential-issuer/app/test.ini - [flake8] ignore = E501 max-line-length = 99 diff --git a/setup.py b/setup.py index 1392c44..72c4ad1 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ "bunch==1.0.1", "fastapi==0.33.0", "pytest_runner==4.4", - "zenroom==1.0.5", + "zenroom==1.0.6", "pre-commit==1.17.0", "python-multipart==0.0.5", "pyjwt==1.7.1", diff --git a/tests/test_auth_attributes.py b/tests/test_auth_attributes.py index 1a3b5f6..67e434e 100644 --- a/tests/test_auth_attributes.py +++ b/tests/test_auth_attributes.py @@ -37,7 +37,7 @@ def test_authorizable_attribute(client): attrib = AuthorizableAttribute.by_aa_id(aaid) assert attrib is not None assert attrib.authorizable_attribute_id == aaid - assert r.json()["credential_issuer_id"] == "Credential Issuer 01" + assert r.json()["credential_issuer_id"] == "issuer_identifier" assert "super_long_key" in attrib.authorizable_attribute_info diff --git a/tests/test_config.py b/tests/test_config.py index 53e9a6c..0c5cb30 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -13,7 +13,7 @@ def test_config_base_logger(): def test_config_get(): config = BaseConfig() - assert config.get("uid") == "Credential Issuer 01" + assert config.get("uid") == "issuer_identifier" def test_config_getint(): diff --git a/tests/test_main.py b/tests/test_main.py index ea9fe89..2a15a2a 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -7,7 +7,6 @@ from app.config.config import BaseConfig from app.main import api -from app.routers.security import load_keypair from app.zencontract import ZenContract, CONTRACTS @@ -52,13 +51,6 @@ def remove_secret(): secret.unlink() -def test_secret_key_creation(remove_secret): - c = BaseConfig() - assert not Path(c.get("keypair")).is_file() - load_keypair() - assert Path(c.get("keypair")).is_file() - - def test_uid(client): config = BaseConfig() r = client.get("/uid")