diff --git a/.gitea/workflows/ci-pipeline.yaml b/.gitea/workflows/ci-pipeline.yaml index 086e4a3..5e89828 100644 --- a/.gitea/workflows/ci-pipeline.yaml +++ b/.gitea/workflows/ci-pipeline.yaml @@ -65,9 +65,14 @@ jobs: pip install -r requirements.txt if: steps.didkit.outcome == 'success' + - name: Check correct env vars + run: | + echo $DOMAIN + - name: Run tests run: | source venv/bin/activate + echo $DOMAIN coverage run manage.py test coverage report # python manage.py test diff --git a/examples/organizations.csv b/examples/organizations.csv index 2f447ef..8396d42 100644 --- a/examples/organizations.csv +++ b/examples/organizations.csv @@ -1,5 +1,5 @@ -"pangea.org";"https://idhub1.demo.pangea.org/oidc4vp/" -"somconnexio.coop";"https://idhub2.demo.pangea.org/oidc4vp/" -"exo.cat";"https://verify.exo.cat" -"local 9000";"http://localhost:9000/oidc4vp/" -"local 8000";"http://localhost:8000/oidc4vp/" +"pangea.org";"https://idhub1.demo.pangea.org/oidc4vp/";"idhub1.demo.pangea.org" +"somconnexio.coop";"https://idhub2.demo.pangea.org/oidc4vp/";"idhub2.demo.pangea.org" +"exo.cat";"https://verify.exo.cat";"verify.exo.cat" +"local 8000";"http://localhost/oidc4vp/";"localhost" +"local 9000";"http://localhost1/oidc4vp/";"localhost1" diff --git a/idhub/management/commands/initial_datas.py b/idhub/management/commands/initial_datas.py index da3539e..dfc0be8 100644 --- a/idhub/management/commands/initial_datas.py +++ b/idhub/management/commands/initial_datas.py @@ -17,6 +17,8 @@ User = get_user_model() class Command(BaseCommand): help = "Insert minimum datas for the project" + DOMAIN = settings.DOMAIN + OIDC_ORGS = settings.OIDC_ORGS def handle(self, *args, **kwargs): ADMIN_EMAIL = config('ADMIN_EMAIL', 'admin@example.org') @@ -28,16 +30,15 @@ class Command(BaseCommand): user = 'user{}@example.org'.format(u) self.create_users(user, '1234') - BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent - ORGANIZATION = os.path.join(BASE_DIR, settings.ORG_FILE) - with open(ORGANIZATION, newline='\n') as csvfile: - f = csv.reader(csvfile, delimiter=';', quotechar='"') - for r in f: - self.create_organizations(r[0].strip(), r[1].strip()) + self.org = Organization.objects.create( + name=self.DOMAIN, + domain=self.DOMAIN, + main=True + ) + + if self.OIDC_ORGS: + self.create_organizations() - if settings.SYNC_ORG_DEV == 'y': - self.sync_credentials_organizations("pangea.org", "somconnexio.coop") - self.sync_credentials_organizations("local 8000", "local 9000") self.create_schemas() def create_admin_users(self, email, password): @@ -50,12 +51,32 @@ class Command(BaseCommand): u.set_password(password) u.save() + def create_organizations(self): + BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent + ORGANIZATION = os.path.join(BASE_DIR, self.OIDC_ORGS) + DOMAIN = self.DOMAIN - def create_organizations(self, name, url): - if url == settings.RESPONSE_URI: - Organization.objects.create(name=name, response_uri=url, main=True) + with open(ORGANIZATION, newline='\n') as csvfile: + f = csv.reader(csvfile, delimiter=';', quotechar='"') + exist_main_domain = False + for r in f: + if DOMAIN == r[2].strip(): + exist_main_domain = True + self.create_one_organization(r[0].strip(), r[1].strip(), r[2].strip()) + + assert exist_main_domain, f"{DOMAIN} is not in {ORGANIZATION}" + + if settings.SYNC_ORG_DEV == 'y': + self.sync_credentials_organizations("pangea.org", "somconnexio.coop") + self.sync_credentials_organizations("local 8000", "local 9000") + + def create_one_organization(self, name, url, domain): + if self.DOMAIN == domain: + self.org.name = name + self.org.response_uri = url + self.org.save() else: - Organization.objects.create(name=name, response_uri=url) + Organization.objects.create(name=name, response_uri=url, domain=domain) def sync_credentials_organizations(self, test1, test2): org1 = Organization.objects.get(name=test1) diff --git a/idhub/management/commands/send_mail_admins.py b/idhub/management/commands/send_mail_admins.py index 0ef24f8..a878456 100644 --- a/idhub/management/commands/send_mail_admins.py +++ b/idhub/management/commands/send_mail_admins.py @@ -27,10 +27,9 @@ class Command(BaseCommand): """ Send a email when a user is activated. """ - parsed_url = urlparse(settings.RESPONSE_URI) - domain = f"{parsed_url.scheme}://{parsed_url.netloc}/" + url_domain = "https://{}/".format(settings.DOMAIN) context = { - "domain": domain, + "domain": url_domain, } subject = loader.render_to_string(self.subject_template_name, context) # Email subject *must not* contain newlines diff --git a/idhub/models.py b/idhub/models.py index 1072e26..2eae07f 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -670,7 +670,7 @@ class VerificableCredential(models.Model): credential_subject = ujson.loads(data).get("credentialSubject", {}) return credential_subject.items() - def issue(self, did, domain=settings.DOMAIN.strip("/")): + def issue(self, did, domain): if self.status == self.Status.ISSUED: return @@ -704,7 +704,7 @@ class VerificableCredential(models.Model): cred_path = 'public/credentials' sid = self.hash - url_id = "{}/{}/{}".format( + url_id = "https://{}/{}/{}".format( domain, cred_path, sid diff --git a/idhub/tests/test_views.py b/idhub/tests/test_views.py index 10e5b24..37a6df1 100644 --- a/idhub/tests/test_views.py +++ b/idhub/tests/test_views.py @@ -1,9 +1,11 @@ from django.test import TestCase, RequestFactory from django.core.cache import cache from django.urls import reverse +from django.conf import settings from idhub_auth.models import User from idhub.models import Event +from oidc4vp.models import Organization from idhub.admin.views import PeopleListView @@ -23,6 +25,10 @@ class AdminDashboardViewTest(TestCase): password='adminpass12') self.admin_user.accept_gdpr=True self.admin_user.save() + self.org = Organization.objects.create(name="testserver", main=True) + + settings.DOMAIN = self.org.name + settings.ENABLE_EMAIL = False def test_view_url_exists_at_desired_location(self): response = self.client.get('/admin/dashboard/', follow=True) diff --git a/idhub/user/forms.py b/idhub/user/forms.py index 81c36a7..654a936 100644 --- a/idhub/user/forms.py +++ b/idhub/user/forms.py @@ -132,8 +132,9 @@ class DemandAuthorizationForm(forms.Form): self.user = kwargs.pop('user', None) super().__init__(*args, **kwargs) self.fields['organization'].choices = [ - (x.id, x.name) for x in Organization.objects.filter() - if x.response_uri != settings.RESPONSE_URI + (x.id, x.name) for x in Organization.objects.exclude( + domain=settings.DOMAIN + ) ] def save(self, commit=True): diff --git a/oidc4vp/migrations/0004_organization_domain.py b/oidc4vp/migrations/0004_organization_domain.py new file mode 100644 index 0000000..fda0103 --- /dev/null +++ b/oidc4vp/migrations/0004_organization_domain.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.5 on 2024-03-01 14:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('oidc4vp', '0003_organization_main'), + ] + + operations = [ + migrations.AddField( + model_name='organization', + name='domain', + field=models.CharField(default=None, max_length=250, null=True), + ), + ] diff --git a/oidc4vp/models.py b/oidc4vp/models.py index 8cfdfaf..916e1d6 100644 --- a/oidc4vp/models.py +++ b/oidc4vp/models.py @@ -51,6 +51,7 @@ class Organization(models.Model): main is a field which indicates the organization of this idhub """ name = models.CharField(max_length=250) + domain = models.CharField(max_length=250, null=True, default=None) main = models.BooleanField(default=False) client_id = models.CharField( max_length=24, @@ -94,7 +95,7 @@ class Organization(models.Model): """ url = "{url}/verify?demand_uri={redirect_uri}".format( url=self.response_uri.strip("/"), - redirect_uri=settings.RESPONSE_URI + redirect_uri=self.response_uri ) auth = (self.my_client_id, self.my_client_secret) return requests.get(url, auth=auth) @@ -284,7 +285,7 @@ class OAuth2VPToken(models.Model): return response def get_redirect_url(self): - if not settings.ALLOW_CODE_URI: + if not settings.OIDC_REDIRECT: return data = { diff --git a/promotion/forms.py b/promotion/forms.py index 30dce87..b9f9e1a 100644 --- a/promotion/forms.py +++ b/promotion/forms.py @@ -23,8 +23,9 @@ class WalletForm(forms.Form): self.presentation_definition = kwargs.pop('presentation_definition', []) super().__init__(*args, **kwargs) self.fields['organization'].choices = [ - (x.id, x.name) for x in Organization.objects.filter() - if x.response_uri != settings.RESPONSE_URI + (x.id, x.name) for x in Organization.objects.exclude( + domain=settings.DOMAIN + ) ] def save(self, commit=True): diff --git a/schemas/course-credential.json b/schemas/course-credential.json index f5fed93..b5dc490 100644 --- a/schemas/course-credential.json +++ b/schemas/course-credential.json @@ -1,5 +1,5 @@ { - "$id": "https://idhub.pangea.org/vc_schemas/course-credential", + "$id": "https://idhub.pangea.org/vc_schemas/course-credential.json", "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "NGO Course Credential Schema", "description": "A NGO Course Credential Schema awarded by a NGO federation and their NGO members, as proposed by Lafede.cat", diff --git a/trustchain_idhub/settings.py b/trustchain_idhub/settings.py index 090be76..ed19a4c 100644 --- a/trustchain_idhub/settings.py +++ b/trustchain_idhub/settings.py @@ -32,12 +32,14 @@ SECRET_KEY = config('SECRET_KEY') # SECURITY WARNING: don't run with debug turned on in production! DEBUG = config('DEBUG', default=False, cast=bool) -ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='', cast=Csv()) -CSRF_TRUSTED_ORIGINS = config('CSRF_TRUSTED_ORIGINS', default='', cast=Csv()) - DOMAIN = config("DOMAIN") assert DOMAIN not in [None, ''], "DOMAIN var is MANDATORY" +ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=DOMAIN, cast=Csv()) +assert DOMAIN in ALLOWED_HOSTS, "DOMAIN is not ALLOWED_HOST" + +CSRF_TRUSTED_ORIGINS = config('CSRF_TRUSTED_ORIGINS', default=f'https://{DOMAIN}', cast=Csv()) + DEFAULT_FROM_EMAIL = config( 'DEFAULT_FROM_EMAIL', default='webmaster@localhost') @@ -201,8 +203,13 @@ USE_I18N = True USE_L10N = True AUTH_USER_MODEL = 'idhub_auth.User' -RESPONSE_URI = config('RESPONSE_URI', default="") -ALLOW_CODE_URI= config('ALLOW_CODE_URI', default="") + +OIDC_REDIRECT = config('OIDC_REDIRECT', default=False, cast=bool) +ALLOW_CODE_URI = config( + 'ALLOW_CODE_URI', + default=f"https://{DOMAIN}/allow_code" +) + SUPPORTED_CREDENTIALS = config( 'SUPPORTED_CREDENTIALS', default='[]', @@ -222,7 +229,7 @@ LOGGING = { } SYNC_ORG_DEV = config('SYNC_ORG_DEV', 'y') -ORG_FILE = config('ORG_FILE', 'examples/organizations.csv') +OIDC_ORGS = config('OIDC_ORGS', '') ENABLE_EMAIL = config('ENABLE_EMAIL', default=True, cast=bool) CREATE_TEST_USERS = config('CREATE_TEST_USERS', default=False, cast=bool) ENABLE_2FACTOR_AUTH = config('ENABLE_2FACTOR_AUTH', default=True, cast=bool)