From 3d57728b7416c32527aa49451ec54e9d746e2d4f Mon Sep 17 00:00:00 2001 From: Isman Firmansyah Date: Mon, 16 Dec 2024 20:36:25 +0700 Subject: [PATCH 01/10] chore(cloud-native): sync fido2 assets and configuration (#10423) * chore(cloud-native): sync fido2 assets and configuration Signed-off-by: iromli * refactor(docker-jans-fido2): remove deprecated function calls Signed-off-by: iromli * chore(charts): add webauthn endpoint Signed-off-by: iromli * chore(cloud-native): update dependencies Signed-off-by: iromli * chore(charts): add missing webauthn virtual service for istio Signed-off-by: iromli * fix(charts): resolve rewrite URI of webauthn in istio virtual service Signed-off-by: iromli --------- Signed-off-by: iromli Signed-off-by: Isman Firmansyah --- charts/janssen-all-in-one/README.md | 5 +- .../templates/nginx-ingress.yaml | 48 ++++++++++++++++ charts/janssen-all-in-one/values.yaml | 6 ++ charts/janssen/README.md | 7 ++- .../templates/fido2-virtual-services.yaml | 14 +++++ .../nginx-ingress/templates/ingress.yaml | 57 +++++++++++++++++++ charts/janssen/values.yaml | 6 ++ docker-jans-all-in-one/Dockerfile | 2 +- docker-jans-fido2/Dockerfile | 9 +-- docker-jans-fido2/scripts/bootstrap.py | 47 ++++++++++++--- docker-jans-fido2/scripts/upgrade.py | 38 +++++++++---- 11 files changed, 211 insertions(+), 28 deletions(-) diff --git a/charts/janssen-all-in-one/README.md b/charts/janssen-all-in-one/README.md index 2542089b2d0..819a215970b 100644 --- a/charts/janssen-all-in-one/README.md +++ b/charts/janssen-all-in-one/README.md @@ -244,13 +244,16 @@ Kubernetes: `>=v1.22.0-0` | fido2.appLoggers.scriptLogTarget | string | `"FILE"` | fido2_script.log target | | fido2.enabled | bool | `true` | Boolean flag to enable/disable the fido2 chart. | | fido2.fido2ServiceName | string | `"fido2"` | Name of the fido2 service. Please keep it as default. | -| fido2.ingress | object | `{"fido2AdditionalAnnotations":{},"fido2ConfigAdditionalAnnotations":{},"fido2ConfigEnabled":false,"fido2ConfigLabels":{},"fido2Enabled":false,"fido2Labels":{}}` | Enable endpoints in either istio or nginx ingress depending on users choice | +| fido2.ingress | object | `{"fido2AdditionalAnnotations":{},"fido2ConfigAdditionalAnnotations":{},"fido2ConfigEnabled":false,"fido2ConfigLabels":{},"fido2Enabled":false,"fido2Labels":{},"fido2WebauthnAdditionalAnnotations":{},"fido2WebauthnEnabled":false,"fido2WebauthnLabels":{}}` | Enable endpoints in either istio or nginx ingress depending on users choice | | fido2.ingress.fido2AdditionalAnnotations | object | `{}` | fido2 ingress resource additional annotations. | | fido2.ingress.fido2ConfigAdditionalAnnotations | object | `{}` | fido2 config ingress resource additional annotations. | | fido2.ingress.fido2ConfigEnabled | bool | `false` | Enable endpoint /.well-known/fido2-configuration | | fido2.ingress.fido2ConfigLabels | object | `{}` | fido2 config ingress resource labels. key app is taken | | fido2.ingress.fido2Enabled | bool | `false` | Enable endpoint /jans-fido2 | | fido2.ingress.fido2Labels | object | `{}` | fido2 ingress resource labels. key app is taken | +| fido2.ingress.fido2WebauthnAdditionalAnnotations | object | `{}` | fido2 webauthn ingress resource additional annotations. | +| fido2.ingress.fido2WebauthnEnabled | bool | `false` | Enable endpoint /.well-known/webauthn | +| fido2.ingress.fido2WebauthnLabels | object | `{}` | fido2 webauthn ingress resource labels. key app is taken | | fqdn | string | `"demoexample.jans.io"` | Fully qualified domain name to be used for Janssen installation. This address will be used to reach Janssen services. | | fullNameOverride | string | `""` | | | hpa | object | `{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50}` | Configure the HorizontalPodAutoscaler | diff --git a/charts/janssen-all-in-one/templates/nginx-ingress.yaml b/charts/janssen-all-in-one/templates/nginx-ingress.yaml index 25cbc837361..722be3adf99 100644 --- a/charts/janssen-all-in-one/templates/nginx-ingress.yaml +++ b/charts/janssen-all-in-one/templates/nginx-ingress.yaml @@ -566,6 +566,54 @@ spec: --- +{{ if .Values.fido2.ingress.fido2WebauthnEnabled -}} +{{ $fullName := include "janssen-all-in-one.fullname" . -}} +{{- $ingressPath := index .Values "nginx-ingress" "ingress" "path" -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }}-webauthn + labels: + app: {{ $fullName }}-fido2 +{{- if index .Values "nginx-ingress" "ingress" "additionalLabels" }} +{{ toYaml (index .Values "nginx-ingress" "ingress" "additionalLabels") | indent 4 }} +{{- end }} +{{- if .Values.fido2.ingress.fido2WebauthnLabels }} +{{ toYaml .Values.fido2.ingress.fido2WebauthnLabels | indent 4 }} +{{- end }} + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "false" + nginx.ingress.kubernetes.io/proxy-read-timeout: "300" + nginx.ingress.kubernetes.io/rewrite-target: /jans-fido2/restv1/webauthn/configuration +{{- if .Values.fido2.ingress.fido2WebauthnAdditionalAnnotations }} +{{ toYaml .Values.fido2.ingress.fido2WebauthnAdditionalAnnotations | indent 4 }} +{{- end }} +{{- if index .Values "nginx-ingress" "ingress" "additionalAnnotations" }} +{{ toYaml (index .Values "nginx-ingress" "ingress" "additionalAnnotations") | indent 4 }} +{{- end }} +spec: + ingressClassName: {{ index .Values "nginx-ingress" "ingress" "ingressClassName" }} +{{- if index .Values "nginx-ingress" "ingress" "tlsSecretName" }} + tls: + - hosts: + - {{ .Values.fqdn | quote }} + secretName: {{ index .Values "nginx-ingress" "ingress" "tlsSecretName" }} +{{- end }} + rules: + - host: {{ .Values.fqdn | quote }} + http: + paths: + - path: /.well-known/webauthn + pathType: Exact + backend: + service: + name: {{ .Values.service.name }} + port: + number: 8080 +{{- end }} + +--- + {{ if index .Values "auth-server" "ingress" "authServerEnabled" -}} {{ $fullName := include "janssen-all-in-one.fullname" . -}} {{- $ingressPath := index .Values "nginx-ingress" "ingress" "path" -}} diff --git a/charts/janssen-all-in-one/values.yaml b/charts/janssen-all-in-one/values.yaml index 3c74ac48e3f..5e694be37ab 100644 --- a/charts/janssen-all-in-one/values.yaml +++ b/charts/janssen-all-in-one/values.yaml @@ -440,6 +440,8 @@ fido2: fido2ConfigEnabled: false # -- Enable endpoint /jans-fido2 fido2Enabled: false + # -- Enable endpoint /.well-known/webauthn + fido2WebauthnEnabled: false # -- fido2 config ingress resource labels. key app is taken fido2ConfigLabels: { } # -- fido2 config ingress resource additional annotations. @@ -448,6 +450,10 @@ fido2: fido2Labels: { } # -- fido2 ingress resource additional annotations. fido2AdditionalAnnotations: { } + # -- fido2 webauthn ingress resource labels. key app is taken + fido2WebauthnLabels: { } + # -- fido2 webauthn ingress resource additional annotations. + fido2WebauthnAdditionalAnnotations: { } scim: # -- Name of the scim service. Please keep it as default. scimServiceName: scim diff --git a/charts/janssen/README.md b/charts/janssen/README.md index 76fc615ab7b..cec5697dcce 100644 --- a/charts/janssen/README.md +++ b/charts/janssen/README.md @@ -265,7 +265,7 @@ Kubernetes: `>=v1.22.0-0` | fido2.usrEnvs.secret | object | `{}` | Add custom secret envs to the service variable1: value1 | | fido2.volumeMounts | list | `[]` | Configure any additional volumesMounts that need to be attached to the containers | | fido2.volumes | list | `[]` | Configure any additional volumes that need to be attached to the pod | -| global | object | `{"alb":{"ingress":false},"auth-server":{"appLoggers":{"auditStatsLogLevel":"INFO","auditStatsLogTarget":"FILE","authLogLevel":"INFO","authLogTarget":"STDOUT","enableStdoutLogPrefix":"true","httpLogLevel":"INFO","httpLogTarget":"FILE","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"authEncKeys":"RSA1_5 RSA-OAEP","authServerServiceName":"auth-server","authSigKeys":"RS256 RS384 RS512 ES256 ES384 ES512 PS256 PS384 PS512","cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"authServerAdditionalAnnotations":{},"authServerEnabled":true,"authServerLabels":{},"authzenAdditionalAnnotations":{},"authzenConfigEnabled":true,"authzenConfigLabels":{},"deviceCodeAdditionalAnnotations":{},"deviceCodeEnabled":true,"deviceCodeLabels":{},"firebaseMessagingAdditionalAnnotations":{},"firebaseMessagingEnabled":true,"firebaseMessagingLabels":{},"lockAdditionalAnnotations":{},"lockConfigAdditionalAnnotations":{},"lockConfigEnabled":false,"lockConfigLabels":{},"lockEnabled":false,"lockLabels":{},"openidAdditionalAnnotations":{},"openidConfigEnabled":true,"openidConfigLabels":{},"u2fAdditionalAnnotations":{},"u2fConfigEnabled":true,"u2fConfigLabels":{},"uma2AdditionalAnnotations":{},"uma2ConfigEnabled":true,"uma2ConfigLabels":{},"webdiscoveryAdditionalAnnotations":{},"webdiscoveryEnabled":true,"webdiscoveryLabels":{},"webfingerAdditionalAnnotations":{},"webfingerEnabled":true,"webfingerLabels":{}},"lockEnabled":false},"auth-server-key-rotation":{"customAnnotations":{"cronjob":{},"secret":{},"service":{}},"enabled":true,"initKeysLife":48},"awsStorageType":"io1","azureStorageAccountType":"Standard_LRS","azureStorageKind":"Managed","casa":{"appLoggers":{"casaLogLevel":"INFO","casaLogTarget":"STDOUT","enableStdoutLogPrefix":"true","timerLogLevel":"INFO","timerLogTarget":"FILE"},"casaServiceName":"casa","cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"casaAdditionalAnnotations":{},"casaEnabled":false,"casaLabels":{}}},"cloud":{"testEnviroment":false},"cnAwsConfigFile":"/etc/jans/conf/aws_config_file","cnAwsSecretsReplicaRegionsFile":"/etc/jans/conf/aws_secrets_replica_regions","cnAwsSharedCredentialsFile":"/etc/jans/conf/aws_shared_credential_file","cnConfiguratorConfigurationFile":"/etc/jans/conf/configuration.json","cnConfiguratorCustomSchema":{"secretName":""},"cnConfiguratorDumpFile":"/etc/jans/conf/configuration.out.json","cnDocumentStoreType":"DB","cnGoogleApplicationCredentials":"/etc/jans/conf/google-credentials.json","cnPersistenceType":"sql","cnPrometheusPort":"","cnSqlPasswordFile":"/etc/jans/conf/sql_password","config":{"customAnnotations":{"clusterRoleBinding":{},"configMap":{},"job":{},"role":{},"roleBinding":{},"secret":{},"service":{},"serviceAccount":{}},"enabled":true},"config-api":{"appLoggers":{"configApiLogLevel":"INFO","configApiLogTarget":"STDOUT","enableStdoutLogPrefix":"true","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","configApiServerServiceName":"config-api","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"configApiAdditionalAnnotations":{},"configApiEnabled":true,"configApiLabels":{}},"plugins":"fido2,scim,user-mgt"},"configAdapterName":"kubernetes","configSecretAdapter":"kubernetes","fido2":{"appLoggers":{"enableStdoutLogPrefix":"true","fido2LogLevel":"INFO","fido2LogTarget":"STDOUT","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"fido2ServiceName":"fido2","ingress":{"fido2AdditionalAnnotations":{},"fido2ConfigAdditionalAnnotations":{},"fido2ConfigEnabled":false,"fido2ConfigLabels":{},"fido2Enabled":false,"fido2Labels":{}}},"fqdn":"demoexample.jans.io","gcePdStorageType":"pd-standard","isFqdnRegistered":false,"istio":{"additionalAnnotations":{},"additionalLabels":{},"enabled":false,"gateways":[],"ingress":false,"namespace":"istio-system"},"jobTtlSecondsAfterFinished":300,"kc-scheduler":{"enabled":false},"lbIp":"22.22.22.22","link":{"appLoggers":{"enableStdoutLogPrefix":"true","linkLogLevel":"INFO","linkLogTarget":"STDOUT","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"service":{},"virtualService":{}},"enabled":false,"ingress":{"linkEnabled":true},"linkServiceName":"link"},"nginx-ingress":{"enabled":true},"persistence":{"customAnnotations":{"job":{},"secret":{},"service":{}},"enabled":true},"saml":{"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":false,"ingress":{"samlAdditionalAnnotations":{},"samlEnabled":false,"samlLabels":{}},"samlServiceName":"saml"},"scim":{"appLoggers":{"enableStdoutLogPrefix":"true","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scimLogLevel":"INFO","scimLogTarget":"STDOUT","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"scimAdditionalAnnotations":{},"scimConfigAdditionalAnnotations":{},"scimConfigEnabled":false,"scimConfigLabels":{},"scimEnabled":false,"scimLabels":{}},"scimServiceName":"scim"},"serviceAccountName":"default","storageClass":{"allowVolumeExpansion":true,"allowedTopologies":[],"mountOptions":["debug"],"parameters":{},"provisioner":"microk8s.io/hostpath","reclaimPolicy":"Retain","volumeBindingMode":"WaitForFirstConsumer"},"usrEnvs":{"normal":{},"secret":{}}}` | Parameters used globally across all services helm charts. | +| global | object | `{"alb":{"ingress":false},"auth-server":{"appLoggers":{"auditStatsLogLevel":"INFO","auditStatsLogTarget":"FILE","authLogLevel":"INFO","authLogTarget":"STDOUT","enableStdoutLogPrefix":"true","httpLogLevel":"INFO","httpLogTarget":"FILE","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"authEncKeys":"RSA1_5 RSA-OAEP","authServerServiceName":"auth-server","authSigKeys":"RS256 RS384 RS512 ES256 ES384 ES512 PS256 PS384 PS512","cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"authServerAdditionalAnnotations":{},"authServerEnabled":true,"authServerLabels":{},"authzenAdditionalAnnotations":{},"authzenConfigEnabled":true,"authzenConfigLabels":{},"deviceCodeAdditionalAnnotations":{},"deviceCodeEnabled":true,"deviceCodeLabels":{},"firebaseMessagingAdditionalAnnotations":{},"firebaseMessagingEnabled":true,"firebaseMessagingLabels":{},"lockAdditionalAnnotations":{},"lockConfigAdditionalAnnotations":{},"lockConfigEnabled":false,"lockConfigLabels":{},"lockEnabled":false,"lockLabels":{},"openidAdditionalAnnotations":{},"openidConfigEnabled":true,"openidConfigLabels":{},"u2fAdditionalAnnotations":{},"u2fConfigEnabled":true,"u2fConfigLabels":{},"uma2AdditionalAnnotations":{},"uma2ConfigEnabled":true,"uma2ConfigLabels":{},"webdiscoveryAdditionalAnnotations":{},"webdiscoveryEnabled":true,"webdiscoveryLabels":{},"webfingerAdditionalAnnotations":{},"webfingerEnabled":true,"webfingerLabels":{}},"lockEnabled":false},"auth-server-key-rotation":{"customAnnotations":{"cronjob":{},"secret":{},"service":{}},"enabled":true,"initKeysLife":48},"awsStorageType":"io1","azureStorageAccountType":"Standard_LRS","azureStorageKind":"Managed","casa":{"appLoggers":{"casaLogLevel":"INFO","casaLogTarget":"STDOUT","enableStdoutLogPrefix":"true","timerLogLevel":"INFO","timerLogTarget":"FILE"},"casaServiceName":"casa","cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"casaAdditionalAnnotations":{},"casaEnabled":false,"casaLabels":{}}},"cloud":{"testEnviroment":false},"cnAwsConfigFile":"/etc/jans/conf/aws_config_file","cnAwsSecretsReplicaRegionsFile":"/etc/jans/conf/aws_secrets_replica_regions","cnAwsSharedCredentialsFile":"/etc/jans/conf/aws_shared_credential_file","cnConfiguratorConfigurationFile":"/etc/jans/conf/configuration.json","cnConfiguratorCustomSchema":{"secretName":""},"cnConfiguratorDumpFile":"/etc/jans/conf/configuration.out.json","cnDocumentStoreType":"DB","cnGoogleApplicationCredentials":"/etc/jans/conf/google-credentials.json","cnPersistenceType":"sql","cnPrometheusPort":"","cnSqlPasswordFile":"/etc/jans/conf/sql_password","config":{"customAnnotations":{"clusterRoleBinding":{},"configMap":{},"job":{},"role":{},"roleBinding":{},"secret":{},"service":{},"serviceAccount":{}},"enabled":true},"config-api":{"appLoggers":{"configApiLogLevel":"INFO","configApiLogTarget":"STDOUT","enableStdoutLogPrefix":"true","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","configApiServerServiceName":"config-api","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"configApiAdditionalAnnotations":{},"configApiEnabled":true,"configApiLabels":{}},"plugins":"fido2,scim,user-mgt"},"configAdapterName":"kubernetes","configSecretAdapter":"kubernetes","fido2":{"appLoggers":{"enableStdoutLogPrefix":"true","fido2LogLevel":"INFO","fido2LogTarget":"STDOUT","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"fido2ServiceName":"fido2","ingress":{"fido2AdditionalAnnotations":{},"fido2ConfigAdditionalAnnotations":{},"fido2ConfigEnabled":false,"fido2ConfigLabels":{},"fido2Enabled":false,"fido2Labels":{},"fido2WebauthnAdditionalAnnotations":{},"fido2WebauthnEnabled":false,"fido2WebauthnLabels":{}}},"fqdn":"demoexample.jans.io","gcePdStorageType":"pd-standard","isFqdnRegistered":false,"istio":{"additionalAnnotations":{},"additionalLabels":{},"enabled":false,"gateways":[],"ingress":false,"namespace":"istio-system"},"jobTtlSecondsAfterFinished":300,"kc-scheduler":{"enabled":false},"lbIp":"22.22.22.22","link":{"appLoggers":{"enableStdoutLogPrefix":"true","linkLogLevel":"INFO","linkLogTarget":"STDOUT","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"service":{},"virtualService":{}},"enabled":false,"ingress":{"linkEnabled":true},"linkServiceName":"link"},"nginx-ingress":{"enabled":true},"persistence":{"customAnnotations":{"job":{},"secret":{},"service":{}},"enabled":true},"saml":{"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":false,"ingress":{"samlAdditionalAnnotations":{},"samlEnabled":false,"samlLabels":{}},"samlServiceName":"saml"},"scim":{"appLoggers":{"enableStdoutLogPrefix":"true","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scimLogLevel":"INFO","scimLogTarget":"STDOUT","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"scimAdditionalAnnotations":{},"scimConfigAdditionalAnnotations":{},"scimConfigEnabled":false,"scimConfigLabels":{},"scimEnabled":false,"scimLabels":{}},"scimServiceName":"scim"},"serviceAccountName":"default","storageClass":{"allowVolumeExpansion":true,"allowedTopologies":[],"mountOptions":["debug"],"parameters":{},"provisioner":"microk8s.io/hostpath","reclaimPolicy":"Retain","volumeBindingMode":"WaitForFirstConsumer"},"usrEnvs":{"normal":{},"secret":{}}}` | Parameters used globally across all services helm charts. | | global.alb.ingress | bool | `false` | Activates ALB ingress | | global.auth-server-key-rotation.enabled | bool | `true` | Boolean flag to enable/disable the auth-server-key rotation cronjob chart. | | global.auth-server-key-rotation.initKeysLife | int | `48` | The initial auth server key rotation keys life in hours | @@ -382,13 +382,16 @@ Kubernetes: `>=v1.22.0-0` | global.fido2.cnCustomJavaOptions | string | `""` | passing custom java options to fido2. Notice you do not need to pass in any loggers options as they are introduced below in appLoggers. DO NOT PASS JAVA_OPTIONS in envs. | | global.fido2.enabled | bool | `true` | Boolean flag to enable/disable the fido2 chart. | | global.fido2.fido2ServiceName | string | `"fido2"` | Name of the fido2 service. Please keep it as default. | -| global.fido2.ingress | object | `{"fido2AdditionalAnnotations":{},"fido2ConfigAdditionalAnnotations":{},"fido2ConfigEnabled":false,"fido2ConfigLabels":{},"fido2Enabled":false,"fido2Labels":{}}` | Enable endpoints in either istio or nginx ingress depending on users choice | +| global.fido2.ingress | object | `{"fido2AdditionalAnnotations":{},"fido2ConfigAdditionalAnnotations":{},"fido2ConfigEnabled":false,"fido2ConfigLabels":{},"fido2Enabled":false,"fido2Labels":{},"fido2WebauthnAdditionalAnnotations":{},"fido2WebauthnEnabled":false,"fido2WebauthnLabels":{}}` | Enable endpoints in either istio or nginx ingress depending on users choice | | global.fido2.ingress.fido2AdditionalAnnotations | object | `{}` | fido2 ingress resource additional annotations. | | global.fido2.ingress.fido2ConfigAdditionalAnnotations | object | `{}` | fido2 config ingress resource additional annotations. | | global.fido2.ingress.fido2ConfigEnabled | bool | `false` | Enable endpoint /.well-known/fido2-configuration | | global.fido2.ingress.fido2ConfigLabels | object | `{}` | fido2 config ingress resource labels. key app is taken | | global.fido2.ingress.fido2Enabled | bool | `false` | Enable endpoint /jans-fido2 | | global.fido2.ingress.fido2Labels | object | `{}` | fido2 ingress resource labels. key app is taken | +| global.fido2.ingress.fido2WebauthnAdditionalAnnotations | object | `{}` | fido2 webauthn ingress resource additional annotations. | +| global.fido2.ingress.fido2WebauthnEnabled | bool | `false` | Enable endpoint /.well-known/webauthn | +| global.fido2.ingress.fido2WebauthnLabels | object | `{}` | fido2 webauthn ingress resource labels. key app is taken | | global.fqdn | string | `"demoexample.jans.io"` | Fully qualified domain name to be used for Janssen installation. This address will be used to reach Janssen services. | | global.gcePdStorageType | string | `"pd-standard"` | GCE storage kind if using Google disks | | global.isFqdnRegistered | bool | `false` | Boolean flag to enable mapping global.lbIp to global.fqdn inside pods on clouds that provide static ip for load balancers. On cloud that provide only addresses to the LB this flag will enable a script to actively scan config.configmap.lbAddr and update the hosts file inside the pods automatically. | diff --git a/charts/janssen/charts/fido2/templates/fido2-virtual-services.yaml b/charts/janssen/charts/fido2/templates/fido2-virtual-services.yaml index 029f4cef87b..8066e0a8ec9 100644 --- a/charts/janssen/charts/fido2/templates/fido2-virtual-services.yaml +++ b/charts/janssen/charts/fido2/templates/fido2-virtual-services.yaml @@ -56,4 +56,18 @@ spec: number: 8080 weight: 100 {{- end }} + {{- if .Values.global.fido2.ingress.fido2WebauthnEnabled }} + - name: {{ .Release.Name }}-istio-webauthn + match: + - uri: + prefix: /.well-known/webauthn + rewrite: + uri: /jans-fido2/restv1/webauthn/configuration + route: + - destination: + host: {{ .Values.global.fido2.fido2ServiceName }}.{{.Release.Namespace}}.svc.cluster.local + port: + number: 8080 + weight: 100 + {{- end }} {{- end }} diff --git a/charts/janssen/charts/nginx-ingress/templates/ingress.yaml b/charts/janssen/charts/nginx-ingress/templates/ingress.yaml index 16d17b134cd..78ac1f1e820 100644 --- a/charts/janssen/charts/nginx-ingress/templates/ingress.yaml +++ b/charts/janssen/charts/nginx-ingress/templates/ingress.yaml @@ -672,6 +672,63 @@ spec: --- +{{ if .Values.global.fido2.ingress.fido2WebauthnEnabled -}} +{{ $fullName := include "nginx-ingress.fullname" . -}} +{{- $ingressPath := .Values.ingress.path -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }}-webauthn + labels: + app: {{ $fullName }}-fido2 +{{- if .Values.ingress.additionalLabels }} +{{ toYaml .Values.ingress.additionalLabels | indent 4 }} +{{- end }} +{{- if .Values.global.fido2.ingress.fido2WebauthnLabels }} +{{ toYaml .Values.global.fido2.ingress.fido2WebauthnLabels | indent 4 }} +{{- end }} + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "false" + nginx.ingress.kubernetes.io/proxy-read-timeout: "300" + nginx.ingress.kubernetes.io/rewrite-target: /jans-fido2/restv1/webauthn/configuration +{{- if .Values.global.fido2.ingress.fido2WebauthnAdditionalAnnotations }} +{{ toYaml .Values.global.fido2.ingress.fido2WebauthnAdditionalAnnotations | indent 4 }} +{{- end }} +{{- if .Values.ingress.additionalAnnotations }} +{{ toYaml .Values.ingress.additionalAnnotations | indent 4 }} +{{- end }} +spec: + ingressClassName: {{ .Values.ingress.ingressClassName }} +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + {{- $host := . -}} + {{- with $ }} + - host: {{ $host | quote }} + http: + paths: + - path: /.well-known/webauthn + pathType: Exact + backend: + service: + name: {{ .Values.global.fido2.fido2ServiceName }} + port: + number: 8080 + {{- end }} + {{- end }} +{{- end }} + +--- + {{ if index .Values "global" "auth-server" "ingress" "authServerEnabled" -}} {{ $fullName := include "nginx-ingress.fullname" . -}} {{- $ingressPath := .Values.ingress.path -}} diff --git a/charts/janssen/values.yaml b/charts/janssen/values.yaml index 3f60fccbe18..a647d490640 100644 --- a/charts/janssen/values.yaml +++ b/charts/janssen/values.yaml @@ -1008,6 +1008,8 @@ global: fido2ConfigEnabled: false # -- Enable endpoint /jans-fido2 fido2Enabled: false + # -- Enable endpoint /.well-known/webauthn + fido2WebauthnEnabled: false # -- fido2 config ingress resource labels. key app is taken fido2ConfigLabels: { } # -- fido2 config ingress resource additional annotations. @@ -1016,6 +1018,10 @@ global: fido2Labels: { } # -- fido2 ingress resource additional annotations. fido2AdditionalAnnotations: { } + # -- fido2 webauthn ingress resource labels. key app is taken + fido2WebauthnLabels: { } + # -- fido2 webauthn ingress resource additional annotations. + fido2WebauthnAdditionalAnnotations: { } # -- GCE storage kind if using Google disks gcePdStorageType: pd-standard # -- Boolean flag to enable mapping global.lbIp to global.fqdn inside pods on clouds that provide static ip for load balancers. On cloud that provide only addresses to the LB this flag will enable a script to actively scan config.configmap.lbAddr and update the hosts file inside the pods automatically. diff --git a/docker-jans-all-in-one/Dockerfile b/docker-jans-all-in-one/Dockerfile index e2b5fe07547..a29b146bd3d 100644 --- a/docker-jans-all-in-one/Dockerfile +++ b/docker-jans-all-in-one/Dockerfile @@ -58,7 +58,7 @@ RUN apk update \ # Assets sync # =========== -ENV JANS_SOURCE_VERSION=39e74a153edf01db8ab43be81f5585bc4210818d +ENV JANS_SOURCE_VERSION=c9abc7f8ee7e45c170b7b2c832cf35b3c6d28bd6 # note that as we're pulling from a monorepo (with multiple project in it) # we are using partial-clone and sparse-checkout to get the assets diff --git a/docker-jans-fido2/Dockerfile b/docker-jans-fido2/Dockerfile index 0bc602ab5cf..4b23948ed4a 100644 --- a/docker-jans-fido2/Dockerfile +++ b/docker-jans-fido2/Dockerfile @@ -42,7 +42,7 @@ RUN wget -q https://maven.jans.io/maven/io/jans/jython-installer/${JYTHON_VERSIO ENV CN_VERSION=0.0.0-nightly -ENV CN_BUILD_DATE='2024-11-15 09:10' +ENV CN_BUILD_DATE='2024-12-15 10:17' ENV CN_SOURCE_URL=https://jenkins.jans.io/maven/io/jans/jans-fido2-server/${CN_VERSION}/jans-fido2-server-${CN_VERSION}.war @@ -61,7 +61,7 @@ RUN mkdir -p ${JETTY_BASE}/jans-fido2/webapps \ # Assets sync # =========== -ENV JANS_SOURCE_VERSION=39e74a153edf01db8ab43be81f5585bc4210818d +ENV JANS_SOURCE_VERSION=c9abc7f8ee7e45c170b7b2c832cf35b3c6d28bd6 ARG JANS_SETUP_DIR=jans-linux-setup/jans_setup # note that as we're pulling from a monorepo (with multiple project in it) @@ -90,10 +90,7 @@ RUN cd /tmp/jans \ && cp ${JANS_SETUP_DIR}/schema/jans_schema.json /app/schema/ \ && cp ${JANS_SETUP_DIR}/schema/custom_schema.json /app/schema/ \ && cp ${JANS_SETUP_DIR}/schema/opendj_types.json /app/schema/ \ - && cp ${JANS_SETUP_DIR}/templates/jans-fido2/fido2.ldif /app/templates/jans-fido2/ \ - && cp ${JANS_SETUP_DIR}/templates/jans-fido2/dynamic-conf.json /app/templates/jans-fido2/ \ - && cp ${JANS_SETUP_DIR}/templates/jans-fido2/static-conf.json /app/templates/jans-fido2/ \ - && cp ${JANS_SETUP_DIR}/templates/jans-fido2/jans-fido2-errors.json /app/templates/jans-fido2/ \ + && cp -R ${JANS_SETUP_DIR}/templates/jans-fido2/* /app/templates/jans-fido2/ \ && mkdir -p org/eclipse/jetty \ && cp ${JANS_SETUP_DIR}/static/favicon.ico org/eclipse/jetty/favicon.ico \ && zip -r ${JETTY_HOME}/lib/jetty-server-${JETTY_VERSION}.jar org/eclipse/jetty/favicon.ico diff --git a/docker-jans-fido2/scripts/bootstrap.py b/docker-jans-fido2/scripts/bootstrap.py index fbfa6950e86..8af6dc1d5e9 100644 --- a/docker-jans-fido2/scripts/bootstrap.py +++ b/docker-jans-fido2/scripts/bootstrap.py @@ -2,6 +2,9 @@ import logging.config import os import typing as _t +import uuid +from datetime import datetime +from datetime import UTC from functools import cached_property from string import Template @@ -168,24 +171,43 @@ def ctx(self) -> dict[str, _t.Any]: ctx = { "hostname": self.manager.config.get("hostname"), "fido2ConfigFolder": "/etc/jans/conf/fido2", + "fido_document_certs_dir": "/etc/jans/conf/fido2/mds/cert", + "fido_document_tocs_dir": "/etc/jans/conf/fido2/mds/toc", } # pre-populate fido2_dynamic_conf_base64 with open("/app/templates/jans-fido2/dynamic-conf.json") as f: ctx["fido2_dynamic_conf_base64"] = generate_base64_contents(f.read() % ctx) - # pre-populate fido2_static_conf_base64 - with open("/app/templates/jans-fido2/static-conf.json") as f: - ctx["fido2_static_conf_base64"] = generate_base64_contents(f.read()) + # pre-populate static ctx + for tmpl, ctx_name, mode in [ + ("/app/templates/jans-fido2/static-conf.json", "fido2_static_conf_base64", "r"), + ("/app/templates/jans-fido2/jans-fido2-errors.json", "fido2_error_base64", "r"), + ("/etc/jans/conf/fido2/mds/toc/toc.jwt", "fido_document_tocs_base64", "r"), + ("/etc/jans/conf/fido2/mds/cert/root-r3.crt", "fido_document_certs_base64", "rb"), + ]: + with open(tmpl, mode) as f: + ctx[ctx_name] = generate_base64_contents(f.read()) - # pre-populate fido2_error_base64 - with open("/app/templates/jans-fido2/jans-fido2-errors.json") as f: - ctx["fido2_error_base64"] = generate_base64_contents(f.read()) + # docs inum + for inum in ["fido_document_certs_inum", "fido_document_tocs_inum"]: + ctx[inum] = self.manager.config.get(inum) + + if not ctx[inum]: + ctx[inum] = str(uuid.uuid4()) + self.manager.config.set(inum, ctx[inum]) + + ctx["fido_document_creation_date"] = generalized_time_utc() + + # finalized ctx return ctx @cached_property def ldif_files(self) -> list[str]: - return ["/app/templates/jans-fido2/fido2.ldif"] + return [ + "/app/templates/jans-fido2/fido2.ldif", + "/app/templates/jans-fido2/docuemts.ldif", + ] def import_ldif_files(self) -> None: for file_ in self.ldif_files: @@ -193,5 +215,16 @@ def import_ldif_files(self) -> None: self.client.create_from_ldif(file_, self.ctx) +def utcnow(): + return datetime.now(UTC) + + +def generalized_time_utc(dtime=None): + """Calculate LDAP generalized time.""" + if not dtime: + dtime = utcnow() + return dtime.strftime("%Y%m%d%H%M%SZ") + + if __name__ == "__main__": main() diff --git a/docker-jans-fido2/scripts/upgrade.py b/docker-jans-fido2/scripts/upgrade.py index e7c6d113c74..213c530f0b4 100644 --- a/docker-jans-fido2/scripts/upgrade.py +++ b/docker-jans-fido2/scripts/upgrade.py @@ -21,22 +21,38 @@ def _transform_fido2_dynamic_config(conf): should_update = False - # add missing config (if not exist) + # add missing top-level config (if not exist) for k, v in [ - ("superGluuEnabled", False), - ("metadataUrlsProvider", ""), ("errorReasonEnabled", False), - ("skipDownloadMdsEnabled", False), - ("attestationMode", "monitor"), ("sessionIdPersistInCache", False), - ("assertionOptionsGenerateEndpointEnabled", True), ]: - # dont update if key exists - if k in conf: - continue + # update if key not exist + if k not in conf: + conf[k] = v + should_update = True - conf[k] = v - should_update = True + # add missing fido2Configuration config (if not exist) + for k, v in [ + ("metadataUrlsProvider", conf.pop("metadataUrlsProvider", "")), + ("skipDownloadMdsEnabled", conf.pop("skipDownloadMdsEnabled", False)), + ("attestationMode", conf.pop("attestationMode", "monitor")), + ("enabledFidoAlgorithms", conf["fido2Configuration"].pop("requestedCredentialTypes", ["RS256", "ES256"])), + ("metadataServers", [{"url": "https://mds.fidoalliance.org/"}]), + ("enterpriseAttestation", False), + ("hints", ["security-key", "client-device", "hybrid"]), + ("rp", conf["fido2Configuration"].pop("requestedParties", [])), + ]: + # update if key not exist + if k not in conf["fido2Configuration"]: + conf["fido2Configuration"][k] = v + should_update = True + + # rename name and domains attributes of fido2Configuration.rp (if any) + for rp in conf["fido2Configuration"]["rp"]: + if not all(["id" in rp, "origins" in rp]): + rp["id"] = rp.pop("name", "") + rp["origins"] = rp.pop("domains", []) + should_update = True # return modified config (if any) and update flag return conf, should_update From 8bdb40189a5c0560ea19bb2c00563bdd61cd9ae7 Mon Sep 17 00:00:00 2001 From: Safin Wasi <6601566+SafinWasi@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:02:10 -0600 Subject: [PATCH 02/10] fix(jans-cedarling): add handling nonexistent authorization decisions (#10431) Signed-off-by: SafinWasi <6601566+SafinWasi@users.noreply.github.com> --- .../flask-sidecar/main/base/cedarling/cedarling.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/jans-cedarling/flask-sidecar/main/base/cedarling/cedarling.py b/jans-cedarling/flask-sidecar/main/base/cedarling/cedarling.py index 6b1f7dbadc5..554fa02357d 100644 --- a/jans-cedarling/flask-sidecar/main/base/cedarling/cedarling.py +++ b/jans-cedarling/flask-sidecar/main/base/cedarling/cedarling.py @@ -102,16 +102,22 @@ def authorize(self, result_dict["decision"] = False person_result = authorize_result.person() workload_result = authorize_result.workload() + person_value = None + workload_value = None + if person_result is not None: + person_value = person_result.decision.value + if workload_result is not None: + workload_value = workload_result.decision.value person_diagnostic = self.generate_report(person_result, "reason") person_error = self.generate_report(person_result, "error") workload_diagnostic = self.generate_report(workload_result, "reason") workload_error = self.generate_report(workload_result, "error") result_dict["context"] = { "reason_admin": { - "person evaluation": authorize_result.person().decision.value, + "person evaluation": person_value, "person diagnostics": person_diagnostic, "person error": person_error, - "workload evaluation": authorize_result.workload().decision.value, + "workload evaluation": workload_value, "workload diagnostics": workload_diagnostic, "workload_error": workload_error } From ec815b1331bcd36a8d326f11439cabbeb20f1671 Mon Sep 17 00:00:00 2001 From: Richard Marin <34529290+rmarinn@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:37:23 +0800 Subject: [PATCH 03/10] feat(jans-cedarling): automatically add entity references into the context (#10387) * feat(jans-cedarling): pass entities data into the context - automatically pass in the `user`, `workload`, `access_token`, `id_token`, and `userinfo_token` entities into the Cedar context to be able to be used for ABAC. Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * chore(jans-cedarling): remove print statement Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * chore(jans-cedarling): rename add_entities_to_context to build_context Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * fix(jans-cedarling): add missing fields on LogEntry struct - add missing fields on LogEntry struct which prevents the tests from starting Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * chore(jans-cedarling): rename add_entities_to_context to build_context Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * feat(jans-cedarling): add deserializable Action struct for Cedar schema actions - Implements the serde Deserializable `Action` struct to represent actions defined in a Cedar schema. Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * refactor(jans-cedarling): use existing structs for Action field types Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * feat(jans-cedarling): implement CedarSchemaJson::find_action Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * refactor(jans-cedarling): define EntityRef type for clarity Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * chore(jans-cedarling): add license header Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * feat(jans-cedarling): implement find_action for CedarSchemaJson - implement find_action function for CedarSchemaJson that returns an Action struct that contains a HashSet of entities that are needed for that certain action Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * feat(jans-cedarling): implement building json context from action schema Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * feat(jans-cedarling): implement schema check before building context - implement checking the schema first before automatically inserting entity references into the context Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * refactor(jans-cedarling): remove unused value mapping when building ctx - remove the required but unused `value_mapping` param when using build_ctx_entities_json - rename build_ctx_entities_json to build_ctx_entity_refs_json Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * refactor(jans-cedarling): make build_ctx_entity_refs_json func param immutable - make the id_mapping param for the build_ctx_entity_refs_json function immutable Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * refactor(jans-cedarling): use CEDAR_POLICY_SEPARATOR const instead of :: Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * refactor(jans-cedarling): use a functional approach instead of looping Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * refactor(jans-cedarling): reduce process_action_context function param Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * test(jans-cedarling): use one file for can_parse_action_with_ctx test Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * refactor(jans-cedarling): use map instead of and_then Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * refactor(jans-cedarling): refactor some string operations - remove string cloning - use CEDAR_POLICY_SEPARATOR instead of '::' Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * docs(jans-cedarling): add docs on adding entities automatically Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * chore(jans-cedarling): resolve clippy issues Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> * docs(jans-cedarling): simplify auto loading entities section Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> --------- Signed-off-by: rmarinn <34529290+rmarinn@users.noreply.github.com> --- docs/cedarling/cedarling-authz.md | 85 +++++++ .../cedarling_python/src/authorize/errors.rs | 8 + .../cedarling/src/authz/entities/mod.rs | 1 + .../cedarling/src/authz/merge_json.rs | 52 ++++ jans-cedarling/cedarling/src/authz/mod.rs | 94 +++++++- .../src/common/cedar_schema/cedar_json.rs | 109 +++++++-- .../common/cedar_schema/cedar_json/action.rs | 224 +++++++++++++++++- .../cedar_schema/cedar_json/entity_types.rs | 12 +- .../cedar_schema/test_files/test_schema.json | 185 +++++++++++++++ .../cases_authorize_without_check_jwt.rs | 68 ++++++ .../test_files/policy-store_ok.yaml | 9 +- .../test_files/policy-store_ok_2.yaml | 21 +- .../test_files/policy-store_ok_abac.yaml | 82 +++++++ .../policy-store_ok_namespace_Jans2.yaml | 9 +- 14 files changed, 908 insertions(+), 51 deletions(-) create mode 100644 jans-cedarling/cedarling/src/authz/merge_json.rs create mode 100644 jans-cedarling/cedarling/src/common/cedar_schema/test_files/test_schema.json create mode 100644 jans-cedarling/test_files/policy-store_ok_abac.yaml diff --git a/docs/cedarling/cedarling-authz.md b/docs/cedarling/cedarling-authz.md index 3135c7b38b9..03fec2f3145 100644 --- a/docs/cedarling/cedarling-authz.md +++ b/docs/cedarling/cedarling-authz.md @@ -77,3 +77,88 @@ input = { decision_result = authz(input) ``` + +## Automatically Adding Entity References to the Context + +Cedarling simplifies context creation by automatically including certain entities. This means you don't need to manually pass their references when using them in your policies. The following entities are automatically added to the context, along with their naming conventions in `lower_snake_case` format: + +- **Workload Entity**: `workload` +- **User Entity**: `user` +- **Resource Entity**: `resource` +- **Access Token Entity**: `access_token` +- **ID Token Entity**: `id_token` +- **Userinfo Token Entity**: `userinfo_token` + +### Example Policy + +Below is an example policy schema that illustrates how entities are used: + +```cedarschema +type Context = { + "access_token": Access_token, + "time": Long, + "user": User, + "workload": Workload +}; + +type Url = { + "host": String, + "path": String, + "protocol": String +}; + +entity Access_token = { + "exp": Long, + "iss": TrustedIssuer +}; + +entity Issue = { + "country": String, + "org_id": String +}; + +entity Role; + +entity TrustedIssuer = { + "issuer_entity_id": Url +}; + +entity User in [Role] = { + "country": String, + "email": String, + "sub": String, + "username": String +}; + +entity Workload = { + "client_id": String, + "iss": TrustedIssuer, + "name": String, + "org_id": String +}; + +action "Update" appliesTo { + principal: [Role, Workload, User], + resource: [Issue], + context: { + "access_token": Access_token, + "time": Long, + "user": User, + "workload": Workload + } +}; + +action "View" appliesTo { + principal: [Role, Workload, User], + resource: [Issue], + context: Context +}; +``` + +With this schema, you only need to provide the fields that are not automatically included. For instance, to define the `time` in the context: + +```js +let context = { + "time": 1719266610.98636, +} +``` diff --git a/jans-cedarling/bindings/cedarling_python/src/authorize/errors.rs b/jans-cedarling/bindings/cedarling_python/src/authorize/errors.rs index 9aef18906f9..11a6fd9c95e 100644 --- a/jans-cedarling/bindings/cedarling_python/src/authorize/errors.rs +++ b/jans-cedarling/bindings/cedarling_python/src/authorize/errors.rs @@ -120,6 +120,13 @@ create_exception!( "Error encountered while parsing all entities to json for logging" ); +create_exception!( + authorize_errors, + AddEntitiesIntoContextError, + AuthorizeError, + "Error encountered while adding entities into context" +); + #[pyclass] #[derive()] pub struct ErrorPayload(CedarlingAuthorizeError); @@ -170,6 +177,7 @@ errors_functions! { CreateContext => CreateContextError, CreateRequestWorkloadEntity => CreateRequestWorkloadEntityError, CreateRequestUserEntity => CreateRequestUserEntityError, + BuildContext => AddEntitiesIntoContextError, Entities => EntitiesError, EntitiesToJson => EntitiesToJsonError } diff --git a/jans-cedarling/cedarling/src/authz/entities/mod.rs b/jans-cedarling/cedarling/src/authz/entities/mod.rs index 50518f4108f..e319e81b48b 100644 --- a/jans-cedarling/cedarling/src/authz/entities/mod.rs +++ b/jans-cedarling/cedarling/src/authz/entities/mod.rs @@ -25,6 +25,7 @@ use crate::jwt; use cedar_policy::EntityUid; pub use create::CedarPolicyCreateTypeError; use create::EntityParsedTypeName; +pub use create::CEDAR_POLICY_SEPARATOR; use create::{build_entity_uid, create_entity, parse_namespace_and_typename, EntityMetadata}; use super::request::ResourceData; diff --git a/jans-cedarling/cedarling/src/authz/merge_json.rs b/jans-cedarling/cedarling/src/authz/merge_json.rs new file mode 100644 index 00000000000..2d764066819 --- /dev/null +++ b/jans-cedarling/cedarling/src/authz/merge_json.rs @@ -0,0 +1,52 @@ +use serde_json::Value; + +#[derive(Debug, thiserror::Error)] +pub enum MergeError { + #[error("Failed to merge JSON objects due to conflicting keys: {0}")] + KeyConflict(String), +} + +pub fn merge_json_values(mut base: Value, other: Value) -> Result { + if let (Some(base_map), Some(additional_map)) = (base.as_object_mut(), other.as_object()) { + for (key, value) in additional_map { + if base_map.contains_key(key) { + return Err(MergeError::KeyConflict(key.clone())); + } + base_map.insert(key.clone(), value.clone()); + } + } + Ok(base) +} + +#[cfg(test)] +mod test { + use serde_json::json; + + use crate::authz::merge_json::MergeError; + + use super::merge_json_values; + + #[test] + fn can_merge_json_objects() { + let obj1 = json!({ "a": 1, "b": 2 }); + let obj2 = json!({ "c": 3, "d": 4 }); + let expected = json!({"a": 1, "b": 2, "c": 3, "d": 4}); + + let result = merge_json_values(obj1, obj2).expect("Should merge JSON objects"); + + assert_eq!(result, expected); + } + + #[test] + fn errors_on_same_keys() { + // Test for only two objects + let obj1 = json!({ "a": 1, "b": 2 }); + let obj2 = json!({ "b": 3, "c": 4 }); + let result = merge_json_values(obj1, obj2); + + assert!( + matches!(result, Err(MergeError::KeyConflict(key)) if key.as_str() == "b"), + "Expected an error due to conflicting keys" + ); + } +} diff --git a/jans-cedarling/cedarling/src/authz/mod.rs b/jans-cedarling/cedarling/src/authz/mod.rs index 425699b5028..848006ac409 100644 --- a/jans-cedarling/cedarling/src/authz/mod.rs +++ b/jans-cedarling/cedarling/src/authz/mod.rs @@ -15,6 +15,7 @@ use std::sync::Arc; use crate::bootstrap_config::AuthorizationConfig; use crate::common::app_types; +use crate::common::cedar_schema::cedar_json::{BuildJsonCtxError, FindActionError}; use crate::common::policy_store::PolicyStoreWithID; use crate::{jwt, LogLevel}; use crate::log::interface::LogWriter; @@ -24,14 +25,15 @@ use crate::log::{ use std::io::Cursor; mod authorize_result; +mod merge_json; pub(crate) mod entities; pub(crate) mod request; mod token_data; pub use authorize_result::AuthorizeResult; -use cedar_policy::{Entities, Entity, EntityUid, Response}; -use entities::create_resource_entity; +use cedar_policy::{ContextJsonError, Entities, Entity, EntityUid, Response}; +use entities::{create_resource_entity, CEDAR_POLICY_SEPARATOR}; use entities::CedarPolicyCreateTypeError; use entities::ProcessTokensResult; use entities::ResourceEntityError; @@ -39,7 +41,9 @@ use entities::{ create_access_token, create_id_token_entity, create_role_entities, create_user_entity, create_userinfo_token_entity, create_workload, RoleEntityError, }; +use merge_json::{merge_json_values, MergeError}; use request::Request; +use serde_json::Value; use std::time::Instant; use token_data::{AccessTokenData, IdTokenData, UserInfoTokenData}; @@ -110,12 +114,6 @@ impl Authz { let action = cedar_policy::EntityUid::from_str(request.action.as_str()) .map_err(AuthorizeError::Action)?; - // Parse context. - let context: cedar_policy::Context = cedar_policy::Context::from_json_value( - request.context.clone(), - Some((&schema.schema, &action)), - )?; - // Parse [`cedar_policy::Entity`]-s to [`AuthorizeEntitiesData`] that hold all entities (for usability). let entities_data: AuthorizeEntitiesData = self.authorize_entities_data(&request,&tokens)?; @@ -124,6 +122,14 @@ impl Authz { let resource_uid = entities_data.resource_entity.uid(); let principal_user_entity_uid = entities_data.user_entity.uid(); + let context = build_context( + &self.config, + request.context.clone(), + &entities_data, + &schema.schema, + &action, + )?; + // Convert [`AuthorizeEntitiesData`] to [`cedar_policy::Entities`] structure, // hold all entities that will be used on authorize check. let entities = entities_data.entities(Some(&schema.schema))?; @@ -290,7 +296,6 @@ impl Authz { let role_entities = create_role_entities(policy_store, tokens, trusted_issuer)?; - // Populate the `AuthorizeEntitiesData` structure using the builder pattern let data = AuthorizeEntitiesData::builder() // Add workload entity .workload_entity(create_workload(auth_conf.mapping_workload.as_deref(), policy_store, @@ -339,6 +344,41 @@ impl Authz { } } +/// Constructs the authorization context by adding the built entities from the tokens +fn build_context( + config: &AuthzConfig, + request_context: Value, + entities_data: &AuthorizeEntitiesData, + schema: &cedar_policy::Schema, + action: &cedar_policy::EntityUid, +) -> Result { + let namespace = config.policy_store.namespace(); + let action_name = action.id().escaped().to_string(); + let action_schema = config.policy_store.schema.json. + find_action(&action_name, namespace) + .map_err(|e| BuildContextError::FindActionSchema(action_name.clone(), e))? + .ok_or(BuildContextError::MissingActionSchema(action_name))?; + + let mut id_mapping = HashMap::new(); + for entity in entities_data.iter() { + // we strip the namespace from the type_name then make it lowercase + // example: 'Jans::Id_token' -> 'id_token' + let type_name = entity.uid().type_name().to_string(); + let type_name = type_name.strip_prefix(&format!("{}{}", namespace, CEDAR_POLICY_SEPARATOR)).unwrap_or(&type_name).to_lowercase(); + let type_id = entity.uid().id().escaped(); + id_mapping.insert(type_name, type_id.to_string()); + } + + let entities_context = action_schema.build_ctx_entity_refs_json(id_mapping).unwrap(); + + let context = merge_json_values(entities_context, request_context)?; + + let context: cedar_policy::Context = + cedar_policy::Context::from_json_value(context, Some((schema, action)))?; + + Ok(context) +} + /// Helper struct to hold named parameters for [`Authz::execute_authorize`] method. struct ExecuteAuthorizeParameters<'a> { entities: &'a Entities, @@ -378,6 +418,19 @@ impl AuthorizeEntitiesData { .chain(self.role_entities) } + /// Create iterator to get all entities + fn iter(&self) -> impl Iterator { + vec![ + &self.workload_entity, + &self.access_token, + &self.id_token_entity, + &self.userinfo_token, + &self.user_entity, + &self.resource_entity, + ] + .into_iter() + } + /// Collect all entities to [`cedar_policy::Entities`] fn entities( self, @@ -432,8 +485,31 @@ pub enum AuthorizeError { /// Error encountered while parsing all entities to json for logging #[error("could convert entities to json: {0}")] EntitiesToJson(serde_json::Error), + /// Error encountered while building the context for the request + #[error("Failed to build context: {0}")] + BuildContext(#[from] BuildContextError), } +#[derive(Debug, thiserror::Error)] +pub enum BuildContextError { + /// Error encountered while validating context according to the schema + #[error(transparent)] + Merge(#[from] MergeError), + /// Error encountered while deserializing the Context from JSON + #[error(transparent)] + DeserializeFromJson(#[from] ContextJsonError), + /// Error encountered while deserializing the Context from JSON + #[error("Failed to find the action `{0}` in the schema: {0}")] + FindActionSchema(String, FindActionError), + /// Error encountered while deserializing the Context from JSON + #[error("The action `{0}` was not found in the schema")] + MissingActionSchema(String), + /// Error encountered while deserializing the Context from JSON + #[error(transparent)] + BuildJson(#[from] BuildJsonCtxError), +} + + #[derive(Debug, derive_more::Error, derive_more::Display)] #[display("could not create request user entity principal for {uid}: {err}")] pub struct CreateRequestRoleError { diff --git a/jans-cedarling/cedarling/src/common/cedar_schema/cedar_json.rs b/jans-cedarling/cedarling/src/common/cedar_schema/cedar_json.rs index 5972ca544a1..a32d47ef3a7 100644 --- a/jans-cedarling/cedarling/src/common/cedar_schema/cedar_json.rs +++ b/jans-cedarling/cedarling/src/common/cedar_schema/cedar_json.rs @@ -11,14 +11,18 @@ //! `cedar translate-schema --direction cedar-to-json -s .\cedar.schema` //! [cedar json schema grammar](https://docs.cedarpolicy.com/schema/json-schema-grammar.html) - documentation about json structure of cedar schema. -use std::collections::HashMap; mod action; mod entity_types; -use action::Action; +use action::ActionSchema; +use derive_more::derive::Display; +use std::collections::HashMap; + +pub use action::{BuildJsonCtxError, FindActionError}; pub use entity_types::{CedarSchemaEntityShape, CedarSchemaRecord}; /// Represent `cedar-policy` schema type for external usage. +#[derive(Debug, PartialEq, Hash, Eq, Display)] pub enum CedarType { Long, String, @@ -85,12 +89,6 @@ impl CedarSchemaJson { None } - - /// Find the action in the schema - pub fn find_action(&self, action_name: &str, namespace: &str) -> Option { - let namespace = self.namespace.get(namespace)?; - namespace.actions.get(action_name).cloned() - } } /// CedarSchemaEntities hold all entities and their shapes in the namespace. @@ -102,17 +100,16 @@ pub struct CedarSchemaEntities { pub entity_types: HashMap, #[serde(rename = "commonTypes", default)] pub common_types: HashMap, - pub actions: HashMap, + pub actions: HashMap, } #[cfg(test)] mod tests { - - use std::collections::HashSet; - use super::entity_types::*; - use super::*; + use action::CtxAttribute; + use serde_json::json; + use std::collections::HashSet; use test_utils::assert_eq; use test_utils::SortedJson; @@ -243,7 +240,7 @@ mod tests { let actions = HashMap::from([( "Update".to_string(), - Action { + ActionSchema { resource_types: HashSet::from(["Issue"].map(|x| x.to_string())), principal_types: HashSet::from(["Access_token", "Role"].map(|x| x.to_string())), context: None, @@ -310,4 +307,88 @@ mod tests { serde_json::from_str::(json_value).expect_err("should fail to parse"); assert_eq!(parse_error.to_string(),"could not deserialize CedarSchemaEntityAttribute, field 'is_required': invalid type: integer `1234`, expected a boolean at line 22 column 1") } + + #[test] + fn can_parse_action_with_ctx() { + let expected_principal_entities = + HashSet::from(["Jans::Workload".into(), "Jans::User".into()]); + let expected_resource_entities = HashSet::from(["Jans::Issue".into()]); + let expected_context_entities = Some(HashSet::from([ + CtxAttribute { + namespace: "Jans".into(), + key: "access_token".into(), + kind: CedarType::TypeName("Access_token".to_string()), + }, + CtxAttribute { + namespace: "Jans".into(), + key: "time".into(), + kind: CedarType::Long, + }, + CtxAttribute { + namespace: "Jans".into(), + key: "user".into(), + kind: CedarType::TypeName("User".to_string()), + }, + CtxAttribute { + namespace: "Jans".into(), + key: "workload".into(), + kind: CedarType::TypeName("Workload".to_string()), + }, + ])); + + // Test case where the context is a record: + // action "Update" appliesTo { + // principal: [Workload, User], + // resource: [Issue], + // context: { + // time: Long, + // user: User, + // workload: Workload, + // access_token: Access_token, + // }}; + let json_value = include_str!("./test_files/test_schema.json"); + let parsed_cedar_schema: CedarSchemaJson = + serde_json::from_str(json_value).expect("Should parse JSON schema"); + let action = parsed_cedar_schema + .find_action("UpdateWithRecordCtx", "Jans") + .expect("Should not error while finding action") + .expect("Action should not be none"); + assert_eq!(action.principal_entities, expected_principal_entities); + assert_eq!(action.resource_entities, expected_resource_entities); + assert_eq!(action.context_entities, expected_context_entities); + + // Test case where the context is a type: + // action "Update" appliesTo { + // principal: [Workload, User], + // resource: [Issue], + // context: Context + // }; + let json_value = include_str!("./test_files/test_schema.json"); + let parsed_cedar_schema: CedarSchemaJson = + serde_json::from_str(json_value).expect("Should parse JSON schema"); + let action = parsed_cedar_schema + .find_action("UpdateWithTypeCtx", "Jans") + .expect("Should not error while finding action") + .expect("Action should not be none"); + assert_eq!(action.principal_entities, expected_principal_entities); + assert_eq!(action.resource_entities, expected_resource_entities); + assert_eq!(action.context_entities, expected_context_entities); + + let id_mapping = HashMap::from([ + ("access_token".into(), "tkn-1".into()), + ("user".into(), "user-123".into()), + ("workload".into(), "workload-321".into()), + ]); + let ctx_json = action + .build_ctx_entity_refs_json(id_mapping) + .expect("Should build JSON context"); + assert_eq!( + ctx_json, + json!({ + "access_token": { "type": "Jans::Access_token", "id": "tkn-1" }, + "user": { "type": "Jans::User", "id": "user-123" }, + "workload": { "type": "Jans::Workload", "id": "workload-321" }, + }) + ) + } } diff --git a/jans-cedarling/cedarling/src/common/cedar_schema/cedar_json/action.rs b/jans-cedarling/cedarling/src/common/cedar_schema/cedar_json/action.rs index 04144c04aba..43a4365124a 100644 --- a/jans-cedarling/cedarling/src/common/cedar_schema/cedar_json/action.rs +++ b/jans-cedarling/cedarling/src/common/cedar_schema/cedar_json/action.rs @@ -5,27 +5,216 @@ * Copyright (c) 2024, Gluu, Inc. */ +use crate::{ + authz::entities::CEDAR_POLICY_SEPARATOR, common::cedar_schema::cedar_json::SchemaDefinedType, +}; + use super::{ entity_types::{ CedarSchemaEntityAttribute, CedarSchemaEntityType, PrimitiveType, PrimitiveTypeKind, }, - CedarSchemaRecord, + CedarSchemaEntities, CedarSchemaJson, CedarSchemaRecord, CedarType, GetCedarTypeError, }; use serde::{de, ser::SerializeMap, Deserialize, Serialize}; use serde_json::{json, Value}; use std::collections::{HashMap, HashSet}; -pub type EntityRef = String; +type AttrName = String; + +#[derive(Debug, Eq, Hash, PartialEq)] +pub struct CtxAttribute { + pub namespace: String, + pub key: String, + pub kind: CedarType, +} + +pub struct Action<'a> { + pub principal_entities: HashSet, + pub resource_entities: HashSet, + pub context_entities: Option>, + pub schema_entities: &'a CedarSchemaEntities, + pub schema: &'a ActionSchema, +} + +impl Action<'_> { + /// Builds the JSON representation of context entities for a given action. + /// + /// This method processes the context attributes of the action and generates a + /// corresponding JSON value. The context may include entity references (with + /// `type` and `id`) and other values, which can be mapped through the provided + /// `id_mapping` and `value_mapping`. + /// + /// The `id_mapping` param is a A `HashMap` that maps context attribute keys + /// (like `"access_token"`) to their corresponding `id`s (like `"acs-tkn-1"`). + /// + /// # Usage Example + /// + /// ```rs + /// let id_mapping = HashMap::from([("access_token".to_string(), "acs-tkn-1".to_string())]); + /// let json = action.build_ctx_entities_json(id_mapping, value_mapping); + /// ``` + pub fn build_ctx_entity_refs_json( + &self, + id_mapping: HashMap, + ) -> Result { + let mut json = json!({}); + + if let Some(ctx_entities) = &self.context_entities { + for attr in ctx_entities.iter() { + if let CedarType::TypeName(type_name) = &attr.kind { + let id = match id_mapping.get(&attr.key) { + Some(val) => val, + None => Err(BuildJsonCtxError::MissingIdMapping(attr.key.clone()))?, + }; + let type_name = + [attr.namespace.as_str(), type_name].join(CEDAR_POLICY_SEPARATOR); + json[attr.key.as_str()] = json!({"type": type_name, "id": id}); + } + } + } + + Ok(json) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum BuildJsonCtxError { + /// If an entity reference is provided but the ID is missing from `id_mapping`. + #[error("An entity reference for `{0}` is required by the schema but an ID was not provided via the `id_mapping`")] + MissingIdMapping(String), + /// If a non-entity attribute is provided but the value is missing from `value_mapping`. + #[error("A non-entity attribute for `{0}` is required by the schema but a value was not provided via the `value_mapping`")] + MissingValueMapping(String), +} + +impl CedarSchemaJson { + /// Find the action in the schema + pub fn find_action( + &self, + action_name: &str, + namespace: &str, + ) -> Result, FindActionError> { + let schema_entities = match self.namespace.get(namespace) { + Some(entities) => entities, + None => return Ok(None), + }; + + let action_schema = match schema_entities.actions.get(action_name) { + Some(schema) => schema, + None => return Ok(None), + }; + + let principal_entities = HashSet::from_iter( + action_schema + .principal_types + .iter() + .map(|principal_type| [namespace, principal_type].join(CEDAR_POLICY_SEPARATOR)), + ); + let resource_entities = HashSet::from_iter( + action_schema + .resource_types + .iter() + .map(|resource_type| [namespace, resource_type].join(CEDAR_POLICY_SEPARATOR)), + ); + let context_entities = action_schema + .context + .as_ref() + .map(|ctx| self.process_action_context(ctx, namespace)) + .transpose()?; + + Ok(Some(Action { + principal_entities, + resource_entities, + context_entities, + schema_entities, + schema: action_schema, + })) + } + + fn process_action_context( + &self, + ctx: &RecordOrType, + namespace: &str, + ) -> Result, FindActionError> { + let mut entities = HashSet::::new(); + + match ctx { + // Case: the context is defined as a record in the schema + // for example: + // Jans { + // action View appliesTo { + // principal: [User], + // resource: [File], + // context: { + // "status": String, + // "id_token": Id_token, + // }, + // }; + // } + RecordOrType::Record(record) => { + for (key, attr) in record.attributes.iter() { + entities.insert(CtxAttribute { + namespace: namespace.to_string(), + key: key.to_string(), + kind: attr.get_type()?, + }); + } + }, + // Case: the context is defined as a type in the schema + // for example: + // Jans { + // type Context = { + // "status": String, + // "id_token": Id_token, + // }; + // action View appliesTo { + // principal: [User], + // resource: [File], + // context: Context, + // }; + // } + RecordOrType::Type(entity_type) => match entity_type { + CedarSchemaEntityType::Primitive(primitive_type) => { + if let PrimitiveTypeKind::TypeName(type_name) = &primitive_type.kind { + let cedar_type = self.find_type(type_name, namespace).unwrap(); + match cedar_type { + SchemaDefinedType::CommonType(common) => { + for (key, attr) in common.attributes.iter() { + entities.insert(CtxAttribute { + namespace: namespace.to_string(), + key: key.to_string(), + kind: attr.get_type()?, + }); + } + }, + SchemaDefinedType::Entity(_) => { + Err(FindActionError::EntityContext(entity_type.clone()))? + }, + } + } + }, + CedarSchemaEntityType::Set(_) => { + Err(FindActionError::SetContext(entity_type.clone()))? + }, + CedarSchemaEntityType::Typed(_) => { + Err(FindActionError::TypedContext(entity_type.clone()))? + }, + }, + } + + Ok(entities) + } +} /// Represents an action in the Cedar JSON schema #[derive(Default, Debug, PartialEq, Clone)] -pub struct Action { - pub resource_types: HashSet, - pub principal_types: HashSet, +pub struct ActionSchema { + pub resource_types: HashSet, + pub principal_types: HashSet, pub context: Option, } -impl Serialize for Action { +impl Serialize for ActionSchema { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -43,7 +232,7 @@ impl Serialize for Action { } } -impl<'de> Deserialize<'de> for Action { +impl<'de> Deserialize<'de> for ActionSchema { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, @@ -78,10 +267,7 @@ impl<'de> Deserialize<'de> for Action { } } -type AttrName = String; - #[derive(Debug, PartialEq, Clone, Serialize)] -#[allow(dead_code)] pub enum RecordOrType { Record(CedarSchemaRecord), Type(CedarSchemaEntityType), @@ -123,9 +309,21 @@ impl<'de> Deserialize<'de> for RecordOrType { } } +#[derive(Debug, thiserror::Error)] +pub enum FindActionError { + #[error("Error while collecting entities from action schema: {0}")] + CollectEntities(#[from] GetCedarTypeError), + #[error("Using `Set` as the context type is unsupported: {0:#?}")] + SetContext(CedarSchemaEntityType), + #[error("Using `Entity` as the context type is unsupported: {0:#?}")] + EntityContext(CedarSchemaEntityType), + #[error("Using `Typed` as the context type is unsupported: {0:#?}")] + TypedContext(CedarSchemaEntityType), +} + #[cfg(test)] mod test { - use super::Action; + use super::ActionSchema; use crate::common::cedar_schema::cedar_json::{ action::RecordOrType, entity_types::{ @@ -141,7 +339,7 @@ mod test { type ActionType = String; #[derive(Deserialize, Debug, PartialEq)] struct MockJsonSchema { - actions: HashMap, + actions: HashMap, } fn build_schema(ctx: Option) -> Value { @@ -165,7 +363,7 @@ mod test { MockJsonSchema { actions: HashMap::from([( "Update".to_string(), - Action { + ActionSchema { resource_types: HashSet::from(["Issue"].map(|s| s.to_string())), principal_types: HashSet::from(["Workload", "User"].map(|s| s.to_string())), context: ctx, diff --git a/jans-cedarling/cedarling/src/common/cedar_schema/cedar_json/entity_types.rs b/jans-cedarling/cedarling/src/common/cedar_schema/cedar_json/entity_types.rs index 9408c303ef2..e50b1f8657f 100644 --- a/jans-cedarling/cedarling/src/common/cedar_schema/cedar_json/entity_types.rs +++ b/jans-cedarling/cedarling/src/common/cedar_schema/cedar_json/entity_types.rs @@ -35,7 +35,7 @@ impl CedarSchemaRecord { /// CedarSchemaRecordAttr defines possible type variants of the entity attribute. /// RecordAttr ::= STR ': {' Type [',' '"required"' ':' ( true | false )] '}' -#[derive(Debug, Clone, PartialEq, serde::Serialize)] +#[derive(Debug, Clone, PartialEq, serde::Serialize, Hash)] pub struct CedarSchemaEntityAttribute { pub cedar_type: CedarSchemaEntityType, pub required: bool, @@ -85,7 +85,7 @@ impl<'de> serde::Deserialize<'de> for CedarSchemaEntityAttribute { } } -#[derive(Debug, Clone, PartialEq, serde::Serialize)] +#[derive(Debug, Clone, PartialEq, serde::Serialize, Hash)] pub enum CedarSchemaEntityType { Set(Box), Typed(EntityType), @@ -151,7 +151,7 @@ impl<'de> serde::Deserialize<'de> for CedarSchemaEntityType { /// The Primitive element describes /// Primitive ::= '"type":' ('"Long"' | '"String"' | '"Boolean"' | TYPENAME) -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq)] +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq, Hash)] pub struct PrimitiveType { #[serde(rename = "type")] pub kind: PrimitiveTypeKind, @@ -159,7 +159,7 @@ pub struct PrimitiveType { /// Variants of primitive type. /// Primitive ::= '"type":' ('"Long"' | '"String"' | '"Boolean"' | TYPENAME) -#[derive(Debug, Clone, serde::Serialize, PartialEq)] +#[derive(Debug, Clone, serde::Serialize, PartialEq, Hash)] pub enum PrimitiveTypeKind { Long, String, @@ -195,7 +195,7 @@ impl<'de> serde::Deserialize<'de> for PrimitiveTypeKind { } /// This structure can hold `Extension`, `EntityOrCommon`, `EntityRef` -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq)] +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq, Hash)] pub struct EntityType { // it also can be primitive type #[serde(rename = "type")] @@ -218,11 +218,11 @@ impl EntityType { } } -#[derive(Debug, Clone, serde::Deserialize, PartialEq, serde::Serialize)] /// Describes the Set element /// Set ::= '"type": "Set", "element": ' TypeJson // // "type": "Set" checked during deserialization +#[derive(Debug, Clone, serde::Deserialize, PartialEq, serde::Serialize, Hash)] pub struct SetEntityType { pub element: CedarSchemaEntityType, } diff --git a/jans-cedarling/cedarling/src/common/cedar_schema/test_files/test_schema.json b/jans-cedarling/cedarling/src/common/cedar_schema/test_files/test_schema.json new file mode 100644 index 00000000000..0f56b9bca71 --- /dev/null +++ b/jans-cedarling/cedarling/src/common/cedar_schema/test_files/test_schema.json @@ -0,0 +1,185 @@ +{ + "Jans": { + "commonTypes": { + "Context": { + "type": "Record", + "attributes": { + "access_token": { + "type": "EntityOrCommon", + "name": "Access_token" + }, + "time": { + "type": "EntityOrCommon", + "name": "Long" + }, + "user": { + "type": "EntityOrCommon", + "name": "User" + }, + "workload": { + "type": "EntityOrCommon", + "name": "Workload" + } + } + }, + "Url": { + "type": "Record", + "attributes": { + "host": { + "type": "EntityOrCommon", + "name": "String" + }, + "path": { + "type": "EntityOrCommon", + "name": "String" + }, + "protocol": { + "type": "EntityOrCommon", + "name": "String" + } + } + } + }, + "entityTypes": { + "Access_token": { + "shape": { + "type": "Record", + "attributes": { + "exp": { + "type": "EntityOrCommon", + "name": "Long" + }, + "iss": { + "type": "EntityOrCommon", + "name": "TrustedIssuer" + } + } + } + }, + "Issue": { + "shape": { + "type": "Record", + "attributes": { + "country": { + "type": "EntityOrCommon", + "name": "String" + }, + "org_id": { + "type": "EntityOrCommon", + "name": "String" + } + } + } + }, + "Role": {}, + "TrustedIssuer": { + "shape": { + "type": "Record", + "attributes": { + "issuer_entity_id": { + "type": "EntityOrCommon", + "name": "Url" + } + } + } + }, + "User": { + "memberOfTypes": [ + "Role" + ], + "shape": { + "type": "Record", + "attributes": { + "country": { + "type": "EntityOrCommon", + "name": "String" + }, + "email": { + "type": "EntityOrCommon", + "name": "String" + }, + "sub": { + "type": "EntityOrCommon", + "name": "String" + }, + "username": { + "type": "EntityOrCommon", + "name": "String" + } + } + } + }, + "Workload": { + "shape": { + "type": "Record", + "attributes": { + "client_id": { + "type": "EntityOrCommon", + "name": "String" + }, + "iss": { + "type": "EntityOrCommon", + "name": "TrustedIssuer" + }, + "name": { + "type": "EntityOrCommon", + "name": "String" + }, + "org_id": { + "type": "EntityOrCommon", + "name": "String" + } + } + } + } + }, + "actions": { + "UpdateWithRecordCtx": { + "appliesTo": { + "resourceTypes": [ + "Issue" + ], + "principalTypes": [ + "Workload", + "User" + ], + "context": { + "type": "Record", + "attributes": { + "access_token": { + "type": "EntityOrCommon", + "name": "Access_token" + }, + "time": { + "type": "EntityOrCommon", + "name": "Long" + }, + "user": { + "type": "EntityOrCommon", + "name": "User" + }, + "workload": { + "type": "EntityOrCommon", + "name": "Workload" + } + } + } + } + }, + "UpdateWithTypeCtx": { + "appliesTo": { + "resourceTypes": [ + "Issue" + ], + "principalTypes": [ + "Workload", + "User" + ], + "context": { + "type": "Context" + } + } + } + } + } +} diff --git a/jans-cedarling/cedarling/src/tests/cases_authorize_without_check_jwt.rs b/jans-cedarling/cedarling/src/tests/cases_authorize_without_check_jwt.rs index 19baed82040..a3cc11e6840 100644 --- a/jans-cedarling/cedarling/src/tests/cases_authorize_without_check_jwt.rs +++ b/jans-cedarling/cedarling/src/tests/cases_authorize_without_check_jwt.rs @@ -10,6 +10,7 @@ use crate::{cmp_decision, cmp_policy}; // macros is defined in the cedarling\src use test_utils::assert_eq; static POLICY_STORE_RAW_YAML: &str = include_str!("../../../test_files/policy-store_ok_2.yaml"); +static POLICY_STORE_ABAC_YAML: &str = include_str!("../../../test_files/policy-store_ok_abac.yaml"); /// Success test case where all check a successful /// role field in the `userinfo_token` because we search here by default @@ -847,3 +848,70 @@ fn only_workload_and_role_permit() { assert!(result.is_allowed(), "request result should be allowed"); } + +#[test] +fn success_test_role_string_with_abac() { + let cedarling = get_cedarling(PolicyStoreSource::Yaml(POLICY_STORE_ABAC_YAML.to_string())); + + // deserialize `Request` from json + let request = Request::deserialize(serde_json::json!( + { + "access_token": generate_token_using_claims(json!({ + "org_id": "some_long_id", + "jti": "token1", + "client_id": "some_client_id", + "iss": "https://account.gluu.org", + "aud": "client123", + "exp": i64::MAX, + "iat": 0, + "name": "Worker123", + })), + "id_token": generate_token_using_claims(json!({ + "jti": "token2", + "iss": "https://account.gluu.org", + "aud": "client123", + "sub": "some_sub", + "exp": i64::MAX, + "iat": 0, + "amr": "some_amr", + "acr": "some_acr", + })), + "userinfo_token": generate_token_using_claims(json!({ + "jti": "token3", + "iss": "https://account.gluu.org", + "sub": "some_sub", + "country": "US", + "role": "Worker", + "email": "some_email@gluu.org", + "client_id": "some_client_id", + "username": "worker123", + "exp": i64::MAX, + "iat": 0, + })), + "action": "Jans::Action::\"Update\"", + "resource": { + "id": "random_id", + "type": "Jans::Issue", + "org_id": "some_long_id", + "country": "US" + }, + "context": {}, + } + )) + .expect("Request should be deserialized from json"); + + let result = cedarling + .authorize(request) + .expect("request should be parsed without errors"); + + cmp_decision!( + result.workload, + Decision::Allow, + "request result should be allowed for workload" + ); + cmp_policy!( + result.workload, + vec!["1"], + "reason of permit workload should be '1'" + ); +} diff --git a/jans-cedarling/test_files/policy-store_ok.yaml b/jans-cedarling/test_files/policy-store_ok.yaml index 096b6b0b7a2..c3f618a9fa0 100644 --- a/jans-cedarling/test_files/policy-store_ok.yaml +++ b/jans-cedarling/test_files/policy-store_ok.yaml @@ -52,9 +52,16 @@ policy_stores: entity Workload = {"client_id": String, "iss": TrustedIssuer, "name": String, "org_id": String}; entity id_token = {"acr": String, "amr": String, "aud": String, "exp": Long, "iat": Long, "iss": TrustedIssuer, "jti": String, "sub": String}; entity Userinfo_token = {"iss": String, "jti": String, "client_id": String}; + type Context = { + user: User, + workload: Workload, + access_token: Access_token, + id_token: id_token, + userinfo_token: Userinfo_token, + }; action "Update" appliesTo { principal: [Workload, User, Role], resource: [Issue], - context: {} + context: Context }; } diff --git a/jans-cedarling/test_files/policy-store_ok_2.yaml b/jans-cedarling/test_files/policy-store_ok_2.yaml index b2c73366926..2d18a36006d 100644 --- a/jans-cedarling/test_files/policy-store_ok_2.yaml +++ b/jans-cedarling/test_files/policy-store_ok_2.yaml @@ -76,40 +76,47 @@ policy_stores: entity Access_token = {"aud": String,"iss": String, "jti": String, "client_id": String,"org_id": String}; entity Userinfo_token = {"iss": String, "jti": String, "client_id": String}; entity Empty; + type Context = { + user: User, + workload: Workload, + access_token: Access_token, + id_token: id_token, + userinfo_token: Userinfo_token, + }; action "Update" appliesTo { principal: [Workload, User], resource: [Issue], - context: {} + context: Context }; action "UpdateForWorkload" appliesTo { principal: [Workload, User], resource: [Issue], - context: {} + context: Context }; action "UpdateForUser" appliesTo { principal: [Workload, User], resource: [Issue], - context: {} + context: Context }; action "UpdateForRole" appliesTo { principal: [Role], resource: [Issue], - context: {} + context: Context }; action "UpdateForUserAndRole" appliesTo { principal: [Workload, User], resource: [Issue], - context: {} + context: Context }; action "UpdateForWorkloadAndRole" appliesTo { principal: [Workload, User], resource: [Issue], - context: {} + context: Context }; action "NoApplies" appliesTo { principal: [Empty], resource: [Issue], - context: {} + context: Context }; action "AlwaysDeny" appliesTo { principal: [Workload, User], diff --git a/jans-cedarling/test_files/policy-store_ok_abac.yaml b/jans-cedarling/test_files/policy-store_ok_abac.yaml new file mode 100644 index 00000000000..dcaec43a4db --- /dev/null +++ b/jans-cedarling/test_files/policy-store_ok_abac.yaml @@ -0,0 +1,82 @@ +cedar_version: v4.0.0 +policy_stores: + a1bf93115de86de760ee0bea1d529b521489e5a11747: + cedar_version: v4.0.0 + name: Jans + description: A test policy store where everything is fine. + policies: + 1: + description: simple policy example for principal workload + creation_date: '2024-09-20T17:22:39.996050' + policy_content: + encoding: none + content_type: cedar + body: |- + permit( + principal is Jans::Workload, + action in [Jans::Action::"Update"], + resource is Jans::Issue + )when{ + principal.org_id == resource.org_id && + context.access_token.aud == "client123" && + context.access_token.aud == context.id_token.aud && + context.id_token.sub == "some_sub" && + principal.client_id == "some_client_id" && + principal.client_id == context.userinfo_token.client_id + }; + schema: + encoding: none + content_type: cedar + body: |- + namespace Jans { + type Url = {"host": String, "path": String, "protocol": String}; + entity Issue = {"country": String, "org_id": String}; + entity Role; + entity TrustedIssuer = { issuer_entity_id: Url }; + entity User in [Role] = { + "country": String, + "email": String, + "sub": String, + "username": String + }; + entity Workload = { + "client_id": String, + "iss": TrustedIssuer, + "name": String, + "org_id": String + }; + entity Access_token = { + "aud": String, + "exp": Long, + "iat": Long, + "iss": TrustedIssuer, + "jti": String + }; + entity id_token = { + "acr": String, + "amr": String, + "aud": String, + "exp": Long, + "iat": Long, + "iss": TrustedIssuer, + "jti": String, + "sub": String + }; + entity Userinfo_token = { + "iss": String, + "jti": String, + "client_id": String + }; + type Context = { + user: User, + workload: Workload, + access_token: Access_token, + id_token: id_token, + userinfo_token: Userinfo_token, + }; + action "Update" appliesTo { + principal: [Workload, User, Role], + resource: [Issue], + context: Context + }; + } diff --git a/jans-cedarling/test_files/policy-store_ok_namespace_Jans2.yaml b/jans-cedarling/test_files/policy-store_ok_namespace_Jans2.yaml index 7e862c12eab..08038bdc4d3 100644 --- a/jans-cedarling/test_files/policy-store_ok_namespace_Jans2.yaml +++ b/jans-cedarling/test_files/policy-store_ok_namespace_Jans2.yaml @@ -63,9 +63,16 @@ policy_stores: entity User in [Role] = {"country": String}; entity Workload = {"org_id": String}; entity Access_token = {"aud": String,"iss": String, "jti": String, "client_id": String,"org_id": String}; + type Context = { + user: User, + workload: Workload, + access_token: Access_token, + id_token: id_token, + userinfo_token: Userinfo_token, + }; action "Update" appliesTo { principal: [Workload, User, Role], resource: [Issue], - context: {} + context: Context }; } From aa0dd7f8780b6631350d7853172ff05b7233a69f Mon Sep 17 00:00:00 2001 From: Mohammad Abudayyeh <47318409+moabu@users.noreply.github.com> Date: Tue, 17 Dec 2024 07:32:19 +0000 Subject: [PATCH 04/10] chore: update codeowners (#10419) chore: update CODEOWNERS --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6d49a80fbf7..b9ff619a9af 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -18,7 +18,7 @@ /jans-core/ @yurem @yuriyz @yuriyzz /jans-orm/ @yurem @yuriyz /jans-auth-server/ @yurem @yuriyz @yuriyzz -/jans-fido2/ @yurem @yackermann +/jans-fido2/ @yurem /jans-lock/ @yurem /jans-scim/ @jgomer2001 /jans-config-api/ @pujavs @yuriyz @yurem From 0866167fe19f12365d1d78104e29b2c45f7c27ce Mon Sep 17 00:00:00 2001 From: moabu <47318409+moabu@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:10:16 +0300 Subject: [PATCH 05/10] ci: fix packaging builds --- automation/packaging/rpm/el8/checksum.sh | 2 +- automation/packaging/rpm/el8/run-build.sh | 2 +- automation/packaging/rpm/suse15/checksum.sh | 4 ++-- automation/packaging/rpm/suse15/run-build.sh | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/automation/packaging/rpm/el8/checksum.sh b/automation/packaging/rpm/el8/checksum.sh index 38dee899aa9..1256aa97a3d 100755 --- a/automation/packaging/rpm/el8/checksum.sh +++ b/automation/packaging/rpm/el8/checksum.sh @@ -13,7 +13,7 @@ echo "VERSION: $VERSION" echo "RELEASE: $RELEASE" if [[ $VERSION == "0.0.0" ]]; then echo "Creating checksum file for nightly build" - sha256sum jans-"$VERSION"-"$RELEASE".x86_64.rpm > jans-0.0.0-nightly-el8.x86_64.rpm.sha256sum + sha256sum jans-"$VERSION"-"$RELEASE".x86_64.rpm > jans-0.0.0-nightly-"$RELEASE".x86_64.rpm.sha256sum else echo "Creating checksum file for release build" sha256sum jans-"$VERSION"-"$RELEASE".x86_64.rpm > jans-"$VERSION"-"$RELEASE".x86_64.rpm.sha256sum diff --git a/automation/packaging/rpm/el8/run-build.sh b/automation/packaging/rpm/el8/run-build.sh index 4d405f925d7..5db1232bed5 100755 --- a/automation/packaging/rpm/el8/run-build.sh +++ b/automation/packaging/rpm/el8/run-build.sh @@ -7,7 +7,7 @@ sed -i "s/%VER%/$VERSION/g" jans.spec if [ -z "$REL" ]; then RELEASE="el8" else - RELEASE="$REL.el8" + RELEASE="$REL-el8" fi sed -i "s/%RELEASE%/$RELEASE/g" jans.spec rpmbuild_path="$current_dir/rpmbuild" diff --git a/automation/packaging/rpm/suse15/checksum.sh b/automation/packaging/rpm/suse15/checksum.sh index e407ba0f9c6..a0f5b8b94c4 100755 --- a/automation/packaging/rpm/suse15/checksum.sh +++ b/automation/packaging/rpm/suse15/checksum.sh @@ -5,14 +5,14 @@ REL=$(echo "%VERSION%" | sed "s/^${VERSION}//g" | sed "s/^-//g") if [ -z "$REL" ]; then RELEASE="suse15" else - RELEASE="$REL.suse15" + RELEASE="$REL-suse15" fi pushd rpmbuild/RPMS/x86_64 echo "VERSION: $VERSION" echo "RELEASE: $RELEASE" if [[ $VERSION == "0.0.0" ]]; then echo "Creating checksum file for nightly build" - sha256sum jans-"$VERSION"-"$RELEASE".x86_64.rpm > jans-0.0.0-nightly-suse15.x86_64.rpm.sha256sum + sha256sum jans-"$VERSION"-"$RELEASE".x86_64.rpm > jans-0.0.0-nightly-"$RELEASE".x86_64.rpm.sha256sum else echo "Creating checksum file for release build" sha256sum jans-"$VERSION"-"$RELEASE".x86_64.rpm > jans-"$VERSION"-"$RELEASE".x86_64.rpm.sha256sum diff --git a/automation/packaging/rpm/suse15/run-build.sh b/automation/packaging/rpm/suse15/run-build.sh index 46443568476..8d5802e531e 100755 --- a/automation/packaging/rpm/suse15/run-build.sh +++ b/automation/packaging/rpm/suse15/run-build.sh @@ -7,7 +7,7 @@ sed -i "s/%VER%/$VERSION/g" jans.spec if [ -z "$REL" ]; then RELEASE="suse15" else - RELEASE="$REL.suse15" + RELEASE="$REL-suse15" fi sed -i "s/%RELEASE%/$RELEASE/g" jans.spec rpmbuild_path="$current_dir/rpmbuild" From 8106a899eaec23359c6b1f73b6d75a245cc8274a Mon Sep 17 00:00:00 2001 From: moabu <47318409+moabu@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:19:11 +0300 Subject: [PATCH 06/10] ci: fix packaging builds --- automation/packaging/rpm/el8/run-build.sh | 2 +- automation/packaging/rpm/suse15/checksum.sh | 2 +- automation/packaging/rpm/suse15/run-build.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/automation/packaging/rpm/el8/run-build.sh b/automation/packaging/rpm/el8/run-build.sh index 5db1232bed5..4d405f925d7 100755 --- a/automation/packaging/rpm/el8/run-build.sh +++ b/automation/packaging/rpm/el8/run-build.sh @@ -7,7 +7,7 @@ sed -i "s/%VER%/$VERSION/g" jans.spec if [ -z "$REL" ]; then RELEASE="el8" else - RELEASE="$REL-el8" + RELEASE="$REL.el8" fi sed -i "s/%RELEASE%/$RELEASE/g" jans.spec rpmbuild_path="$current_dir/rpmbuild" diff --git a/automation/packaging/rpm/suse15/checksum.sh b/automation/packaging/rpm/suse15/checksum.sh index a0f5b8b94c4..01a51732a0d 100755 --- a/automation/packaging/rpm/suse15/checksum.sh +++ b/automation/packaging/rpm/suse15/checksum.sh @@ -5,7 +5,7 @@ REL=$(echo "%VERSION%" | sed "s/^${VERSION}//g" | sed "s/^-//g") if [ -z "$REL" ]; then RELEASE="suse15" else - RELEASE="$REL-suse15" + RELEASE="$REL.suse15" fi pushd rpmbuild/RPMS/x86_64 echo "VERSION: $VERSION" diff --git a/automation/packaging/rpm/suse15/run-build.sh b/automation/packaging/rpm/suse15/run-build.sh index 8d5802e531e..46443568476 100755 --- a/automation/packaging/rpm/suse15/run-build.sh +++ b/automation/packaging/rpm/suse15/run-build.sh @@ -7,7 +7,7 @@ sed -i "s/%VER%/$VERSION/g" jans.spec if [ -z "$REL" ]; then RELEASE="suse15" else - RELEASE="$REL-suse15" + RELEASE="$REL.suse15" fi sed -i "s/%RELEASE%/$RELEASE/g" jans.spec rpmbuild_path="$current_dir/rpmbuild" From 74fea31804526e27cd366295222856aae614dbcc Mon Sep 17 00:00:00 2001 From: moabu <47318409+moabu@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:31:21 +0300 Subject: [PATCH 07/10] ci: fix packaging builds --- .github/workflows/build-packages.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 41637d2066b..0bd98618478 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -30,14 +30,14 @@ jobs: sign_cmd: dpkg-sig -s builder -k DE92BEF14A1A4E542F678B64DC3C790386C73900 python_version: 3.8 - name: el8 - asset_suffix: -el8.x86_64.rpm + asset_suffix: .el8.x86_64.rpm build_files: rpm/el8 asset_prefix: '-' asset_path: jans/rpmbuild/RPMS/x86_64 sign_cmd: rpm --addsign python_version: 3.6 - name: suse15 - asset_suffix: -suse15.x86_64.rpm + asset_suffix: .suse15.x86_64.rpm build_files: rpm/suse15 asset_prefix: '-' asset_path: jans/rpmbuild/RPMS/x86_64 From 1e024edba68c7d426d9b051a1f7451567fef4cd3 Mon Sep 17 00:00:00 2001 From: moabu <47318409+moabu@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:40:22 +0300 Subject: [PATCH 08/10] ci: fix packaging builds --- .github/workflows/build-packages.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 0bd98618478..46f5ce31bb1 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -108,9 +108,6 @@ jobs: sed -i "s/%VERSION%/${{ steps.previoustag.outputs.version }}/g" run-build.sh cat run-build.sh sudo ./run-build.sh - if [[ ${{ matrix.name }} == "el8" || ${{ matrix.name }} == "suse15" ]] && [[ ${{ steps.previoustag.outputs.tag }} == "nightly" ]]; then - cp -r /home/runner/work/jans/jans/jans/rpmbuild/RPMS/x86_64/jans-0.0.0-nightly.${{ matrix.name }}.x86_64.rpm ${{github.workspace}}/${{ matrix.asset_path }}/jans${{ matrix.asset_prefix }}${{ steps.previoustag.outputs.version }}${{ matrix.asset_suffix }} - fi - name: Sign package id: sign_package run : | From 40714f50814b6b9eb55fc25582ef16fa6c213998 Mon Sep 17 00:00:00 2001 From: moabu <47318409+moabu@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:50:19 +0300 Subject: [PATCH 09/10] ci: fix packaging builds --- .github/workflows/build-packages.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 46f5ce31bb1..b6446aac31a 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -121,6 +121,7 @@ jobs: cd jans/ sed -i "s/%VERSION%/${{ steps.previoustag.outputs.version }}/g" checksum.sh sudo ./checksum.sh + ls ${{github.workspace}}/${{ matrix.asset_path }} - name: Upload binaries to release id: upload_binaries From 0b8caa51fa54941adbeb4509ae4188ca2736d1b2 Mon Sep 17 00:00:00 2001 From: moabu <47318409+moabu@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:59:57 +0300 Subject: [PATCH 10/10] ci: fix checksum.sh --- automation/packaging/rpm/el8/checksum.sh | 9 ++------- automation/packaging/rpm/suse15/checksum.sh | 9 ++------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/automation/packaging/rpm/el8/checksum.sh b/automation/packaging/rpm/el8/checksum.sh index 1256aa97a3d..4d4f419ca8d 100755 --- a/automation/packaging/rpm/el8/checksum.sh +++ b/automation/packaging/rpm/el8/checksum.sh @@ -11,11 +11,6 @@ pushd rpmbuild/RPMS/x86_64 echo "VERSION: $VERSION" echo "RELEASE: $RELEASE" -if [[ $VERSION == "0.0.0" ]]; then - echo "Creating checksum file for nightly build" - sha256sum jans-"$VERSION"-"$RELEASE".x86_64.rpm > jans-0.0.0-nightly-"$RELEASE".x86_64.rpm.sha256sum -else - echo "Creating checksum file for release build" - sha256sum jans-"$VERSION"-"$RELEASE".x86_64.rpm > jans-"$VERSION"-"$RELEASE".x86_64.rpm.sha256sum -fi +echo "Creating checksum file for release build" +sha256sum jans-"$VERSION"-"$RELEASE".x86_64.rpm > jans-"$VERSION"-"$RELEASE".x86_64.rpm.sha256sum popd diff --git a/automation/packaging/rpm/suse15/checksum.sh b/automation/packaging/rpm/suse15/checksum.sh index 01a51732a0d..bff673a7b4f 100755 --- a/automation/packaging/rpm/suse15/checksum.sh +++ b/automation/packaging/rpm/suse15/checksum.sh @@ -10,11 +10,6 @@ fi pushd rpmbuild/RPMS/x86_64 echo "VERSION: $VERSION" echo "RELEASE: $RELEASE" -if [[ $VERSION == "0.0.0" ]]; then - echo "Creating checksum file for nightly build" - sha256sum jans-"$VERSION"-"$RELEASE".x86_64.rpm > jans-0.0.0-nightly-"$RELEASE".x86_64.rpm.sha256sum -else - echo "Creating checksum file for release build" - sha256sum jans-"$VERSION"-"$RELEASE".x86_64.rpm > jans-"$VERSION"-"$RELEASE".x86_64.rpm.sha256sum -fi +echo "Creating checksum file for release build" +sha256sum jans-"$VERSION"-"$RELEASE".x86_64.rpm > jans-"$VERSION"-"$RELEASE".x86_64.rpm.sha256sum popd