Skip to content

Commit

Permalink
Merge pull request #217 from italia/dev
Browse files Browse the repository at this point in the history
fix: entity trust chain reload admin action MUST have force
feat: automatic update trust marks via entity admin action
  • Loading branch information
Giuseppe De Marco authored Apr 4, 2022
2 parents 49fdc30 + 89e9b7b commit e5f4d40
Show file tree
Hide file tree
Showing 21 changed files with 130 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[run]
branch = true
omit = */tests/*, */migrations/*, */urls.py, */settings/*, */wsgi.py, manage.py, fabfile.py, /usr/local/*, ./setup.py, */manage.py, */examples/*
omit = */tests/*, */migrations/*, */urls.py, */settings/*, */wsgi.py, manage.py, fabfile.py, /usr/local/*, ./setup.py, */manage.py, */examples/*, */relying_party_test/*, spid_cie_oidc/entity/policy.py
source = .

[report]
Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ RUN pip3 install --upgrade pip
RUN pip3 install -e .
RUN pip3 install design-django-theme

# let compose do this
# WORKDIR /django-project/
# RUN ls -al .
# RUN python3 manage.py migrate
Expand Down
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ each of these can be installed separately within a django project. These are the

* [Features](#features)
* [Setup](#setup)
* [Docker compose](#docker-compose)
* [Docker](#docker)
* [Usage](#usage)
* [OpenAPI Schema 3](#openapi-schema)
* [OpenAPI Schema 3](#openapi-schema-3)
* [Tools](#tools)
* [Contribute](#contribute)
* [Contribute as end user](#contribute-as-end-user)
Expand Down Expand Up @@ -67,7 +67,15 @@ __spid_cie_oidc.entity__ and __spid_cie_oidc.provider__ or __.relying_party__ as

Read the [setup documentation](docs/SETUP.md) to get started.

## Docker compose
## Docker

### Docker image

````
docker pull ghcr.io/italia/spid-cie-oidc-django:v0.6.2
````

### Docker compose

Install Docker using the packages distributed from the official website and the following tools.
````
Expand Down
3 changes: 3 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ version: "3"

services:
trust-anchor.org:
image: spid-cie-oidc-django:v0.6.2
build:
context: .
dockerfile: ./Dockerfile
Expand All @@ -18,6 +19,7 @@ services:
python3 manage.py runserver 0.0.0.0:8000"
cie-provider.org:
image: spid-cie-oidc-django:v0.6.2
build:
context: .
dockerfile: ./Dockerfile
Expand All @@ -38,6 +40,7 @@ services:
python3 manage.py runserver 0.0.0.0:8002"
relying-party.org:
image: spid-cie-oidc-django:v0.6.2
build:
context: .
dockerfile: ./Dockerfile
Expand Down
2 changes: 1 addition & 1 deletion spid_cie_oidc/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.6.1"
__version__ = "0.6.2"
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class AdvancedEntityListRequest(BaseModel):
page : Optional[int]

def example():
def example(): # pragma: no cover
return AdvancedEntityListRequest(
page= 1,
)
Expand All @@ -23,7 +23,7 @@ class AdvancedEntityListResponse(BaseModel):
next_page_path: str
prev_page_path: str

def example():
def example(): # pragma: no cover
return AdvancedEntityListResponse(
iss= "https://registry.spid.gov.it/",
iat= 1620050972,
Expand Down
2 changes: 1 addition & 1 deletion spid_cie_oidc/authority/schemas/fetch_endpoint_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class FetchRequest(BaseModel):
iss : Optional[HttpUrl]
aud: Optional[List[HttpUrl]]

def example():
def example(): # pragma: no cover
return FetchRequest(
sub= "http://127.0.0.1:8000/oidc/rp/",
iss= "http://127.0.0.1:8000/",
Expand Down
2 changes: 1 addition & 1 deletion spid_cie_oidc/authority/schemas/list_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class ListRequest(BaseModel):
is_leaf : Optional[bool]
type : Optional[Literal["openid_relying_party", "openid_provider", "oauth_resource", "federation_entity"]]

def example():
def example(): # pragma: no cover
return ListRequest(
is_leaf= True,
type= "openid_provider"
Expand Down
2 changes: 1 addition & 1 deletion spid_cie_oidc/authority/schemas/resolve_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class ResolveRequest(BaseModel):
iss: Optional[HttpUrl]
format :Literal["json"]

def example():
def example(): # pragma: no cover
return ResolveRequest(
sub= "http://127.0.0.1:8000/oidc/rp/",
anchor= "http://127.0.0.1:8000/",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def validate_id(cls, id_value, values):
if (not values.get("trust_mark") and (not values.get("sub") or not id_value)):
raise ValueError("sub an id must be present if not trust_mark")

def example():
def example(): # pragma: no cover
return TrustMarkRequest(
id= "https://www.spid.gov.it/openid-federation/agreement/op-public/",
sub= "http://127.0.0.1:8000/oidc/op",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ def setUp(self):
)

def test_fetch_endpoint(self):
self.rp_profile.__str__()
self.rp_profile.trust_mark_template_as_json
url = reverse("oidcfed_fetch")
c = Client()
res = c.get(url, data={"sub": self.rp.sub})
Expand Down
97 changes: 89 additions & 8 deletions spid_cie_oidc/entity/admin.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import logging

from django.contrib import admin
from django.utils.safestring import mark_safe
from django.contrib import messages

# from prettyjson import PrettyJSONWidget

from django.conf import settings
from .jwtse import unpad_jwt_payload
from .statements import EntityConfiguration, get_entity_configurations, get_entity_statements
from .models import (
FederationEntityConfiguration,
FetchedEntityStatement,
TrustChain
)
from spid_cie_oidc.entity.trust_chain_operations import get_or_create_trust_chain

from . settings import HTTPC_PARAMS

logger = logging.getLogger(__name__)


@admin.register(FederationEntityConfiguration)
class FederationEntityConfigurationAdmin(admin.ModelAdmin):
Expand All @@ -22,6 +30,75 @@ class FederationEntityConfigurationAdmin(admin.ModelAdmin):
# )
# }
# }

@admin.action(description='update trust marks')
def update_trust_marks(modeladmin, request, queryset): # pragma: no cover
"""
fetch trust marks from all the authorities
"""
trust_marks = {}

for obj in queryset:
jwts = get_entity_configurations(obj.authority_hints, HTTPC_PARAMS)
for jwt in jwts:
try:
ec = EntityConfiguration(jwt, httpc_params=HTTPC_PARAMS)
except Exception as e:
_msg = f"Failed getting Entity Configuration for {jwt}: {e}"
logger.warning(_msg)
messages.error(_msg)
continue

try:
# get superior fetch url
fetch_api_url = ec.payload["metadata"]["federation_entity"][
"federation_fetch_endpoint"
]
except KeyError:
_msg = (
"Missing federation_fetch_endpoint in "
f"federation_entity metadata for {obj.sub} by {ec.sub}."
)
logger.warning(_msg)
messages.error(_msg)
continue

_url = f"{fetch_api_url}?sub={obj.sub}"
try:
logger.info(f"Getting entity statements from {_url}")
_jwts = get_entity_statements([_url], HTTPC_PARAMS)
payload = unpad_jwt_payload(_jwts[0])
for i in payload.get("trust_marks", []):
trust_marks[i['id']] = i['trust_mark']
except Exception as e:
_msg = f"Error getting entity statements from {_url}: {e}"
logger.warning(_msg)
messages.error(_msg)
continue

positions = {}
count = 0
for i in obj.trust_marks:
positions[i['id']] = count
count += 1

if positions:
for k,v in trust_marks.items():
if positions.get(k, None):
obj.trust_marks[positions[k]] = {k:v}
else:
obj.trust_marks.append({k:v})
else:
obj.trust_marks = [
{"id":k, "trust_mark":v} for k,v in trust_marks.items()
]

obj.save()
messages.success(
request,
f"Trust mark reloaded succesfully: {', '.join(trust_marks.keys())}"
)

list_display = (
"sub",
"type",
Expand All @@ -39,6 +116,7 @@ class FederationEntityConfigurationAdmin(admin.ModelAdmin):
"kids",
"type",
)
actions = [update_trust_marks]

def pems_as_html(self, obj):
res = ""
Expand All @@ -59,20 +137,23 @@ def pems_as_html(self, obj):
class TrustChainAdmin(admin.ModelAdmin):

@admin.action(description='reload trust chain')
def update_trust_chain(modeladmin, request, queryset):
def update_trust_chain(modeladmin, request, queryset): # pragma: no cover
for tc in queryset:
sub = tc.sub
ta = tc.trust_anchor.sub
try :
get_or_create_trust_chain(
subject=sub,
trust_anchor=ta,
httpc_params=settings.HTTPC_PARAMS,
required_trust_marks=getattr(
settings, "OIDCFED_REQUIRED_TRUST_MARKS", []
),
subject=sub,
trust_anchor=ta,
httpc_params=settings.HTTPC_PARAMS,
required_trust_marks=getattr(
settings, "OIDCFED_REQUIRED_TRUST_MARKS", [],
),
force=True
)
messages.success(
request, f"Trust chain successfully reloaded for {sub}"
)
messages.success(request, f"reload trust chain successfully")
except Exception as e:
messages.error(request, f"Failed to update {sub} due to: {e}")
continue
Expand Down
1 change: 1 addition & 0 deletions spid_cie_oidc/entity/schemas/op_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from .jwks import JwksCie, JwksSpid


class ScopeSupported(str, Enum):
openid = "openid"
offline_access = "offline_access"
Expand Down
2 changes: 1 addition & 1 deletion spid_cie_oidc/entity/schemas/rp_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ class RPMetadataCie(RPMetadata):
def validate_jwks_uri(cls, jwks, values):
jwks_uri = values.get("jwks_uri")
if not jwks_uri and not jwks:
raise ValueError("one of jwks_uri or jwks must be set")
raise ValueError("one of jwks_uri or jwks must be set")
if jwks_uri and jwks:
raise ValueError("jwks MUST NOT indicate")
4 changes: 2 additions & 2 deletions spid_cie_oidc/onboarding/schemas/authn_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ class AuthenticationRequestSpid(AuthenticationRequest):
def get_claims() -> dict:
return CLAIMS_SPID

def example():
def example(): # pragma: no cover
return AuthenticationRequestSpid( # nosec B106
client_id= "https://rp.cie.it/callback1/",
response_type= "code",
Expand Down Expand Up @@ -214,7 +214,7 @@ class AuthenticationRequestDoc(BaseModel):
code_challenge_method: Literal["S256"]
request: constr(regex=r"^[a-zA-Z\_\-0-9]+\.[a-zA-Z\_\-0-9]+\.[a-zA-Z\_\-0-9]+") # noqa: F722

def example():
def example(): # pragma: no cover
return AuthenticationRequestDoc( # nosec B106
client_id= "https://rp.cie.it/callback1/",
response_type= "code",
Expand Down
4 changes: 2 additions & 2 deletions spid_cie_oidc/onboarding/schemas/authn_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class AuthenticationResponse(BaseModel):
code: str
state: constr(min_length=32)

def example():
def example(): # pragma: no cover
return AuthenticationResponse( # nosec B106
code= "usDwMnEzJPpG5oaV8x3j&",
state= "fyZiOL9Lf2CeKuNT2JzxiLRDink0uPcd",
Expand All @@ -18,7 +18,7 @@ def example():
class AuthenticationResponseCie(AuthenticationResponse):
iss: HttpUrl

def example():
def example(): # pragma: no cover
return AuthenticationResponse( # nosec B106
code= "usDwMnEzJPpG5oaV8x3j&",
state= "fyZiOL9Lf2CeKuNT2JzxiLRDink0uPcd",
Expand Down
2 changes: 1 addition & 1 deletion spid_cie_oidc/onboarding/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
from spid_cie_oidc.provider.settings import OIDCFED_PROVIDER_PROFILES


def onboarding_landing(request):
def onboarding_landing(request): # pragma: no cover
return render(request, "onboarding_landing.html")


Expand Down
Loading

0 comments on commit e5f4d40

Please sign in to comment.