diff --git a/.gitignore b/.gitignore index 66e4e335e7817b3622c0e3d74ea3ad807702da9a..651e1692f9c36007ca84ddbef9050d047a091be3 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ __pycache__/ *.crt *.zip *.srl +*.log services/nginx/certs/sign_req_body.json services/easy_rsa/certs/pki services/easy_rsa/certs/*EasyRSA* @@ -35,4 +36,5 @@ docs/testing_with_postman/package-lock.json results helm/capif/*.lock -helm/capif/charts \ No newline at end of file +helm/capif/charts +helm/capif/charts/tempo* diff --git a/helm/DELETE.txt b/helm/DELETE.txt new file mode 100644 index 0000000000000000000000000000000000000000..2d030d7bc1bbfbdee332aaf691447b30cdea375b --- /dev/null +++ b/helm/DELETE.txt @@ -0,0 +1 @@ +delete me diff --git a/helm/capif/Chart.yaml b/helm/capif/Chart.yaml index 0c8eb5f8848a25968679ee8be65691b6a6ae386c..fc0c9676150d16f0579ba61b5e98097d01e26768 100644 --- a/helm/capif/Chart.yaml +++ b/helm/capif/Chart.yaml @@ -20,6 +20,8 @@ version: v3.1.6 # It is recommended to use it with quotes. appVersion: "v3.1.6" dependencies: + - name: helper + version: "*" - name: "tempo" condition: tempo.enabled repository: "https://grafana.github.io/helm-charts" diff --git a/helm/capif/charts/helper/.helmignore b/helm/capif/charts/helper/.helmignore new file mode 100644 index 0000000000000000000000000000000000000000..0e8a0eb36f4ca2c939201c0d54b5d82a1ea34778 --- /dev/null +++ b/helm/capif/charts/helper/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm/capif/charts/helper/Chart.yaml b/helm/capif/charts/helper/Chart.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4ddfbf3967718e22006b5b754b7ec2d751d0e635 --- /dev/null +++ b/helm/capif/charts/helper/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: helper +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/helm/capif/charts/helper/templates/NOTES.txt b/helm/capif/charts/helper/templates/NOTES.txt new file mode 100644 index 0000000000000000000000000000000000000000..f8f6f774769a0eb1872df2436de39fd4b89dffc5 --- /dev/null +++ b/helm/capif/charts/helper/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "helper.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "helper.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "helper.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "helper.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/helm/capif/charts/helper/templates/_helpers.tpl b/helm/capif/charts/helper/templates/_helpers.tpl new file mode 100644 index 0000000000000000000000000000000000000000..f4a197b20c55e50ad4c5bab1462dcdae183dd3db --- /dev/null +++ b/helm/capif/charts/helper/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "helper.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "helper.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "helper.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "helper.labels" -}} +helm.sh/chart: {{ include "helper.chart" . }} +{{ include "helper.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "helper.selectorLabels" -}} +app.kubernetes.io/name: {{ include "helper.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "helper.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "helper.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm/capif/charts/helper/templates/deployment.yaml b/helm/capif/charts/helper/templates/deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a3f43d3ed01828f6e2c086d5649b6d9c5f4aa055 --- /dev/null +++ b/helm/capif/charts/helper/templates/deployment.yaml @@ -0,0 +1,76 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "helper.fullname" . }} + labels: + {{- include "helper.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "helper.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + date: "{{ now | unixEpoch }}" + checksum/config: {{ include (print $.Template.BasePath "/ocf-helper-configmap.yaml") . | sha256sum }} + labels: + {{- include "helper.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "helper.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + env: + - name: CAPIF_HOSTNAME + value: {{ quote .Values.env.capifHostname }} + - name: VAULT_HOSTNAME + value: {{ quote .Values.env.vaultHostname }} + - name: VAULT_PORT + value: {{ quote .Values.env.vaultPort }} + - name: VAULT_ACCESS_TOKEN + value: {{ quote .Values.env.vaultAccessToken }} + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm/capif/charts/helper/templates/hpa.yaml b/helm/capif/charts/helper/templates/hpa.yaml new file mode 100644 index 0000000000000000000000000000000000000000..046148d39279bc420739dfd71c5ef35e1498c513 --- /dev/null +++ b/helm/capif/charts/helper/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "helper.fullname" . }} + labels: + {{- include "helper.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "helper.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm/capif/charts/helper/templates/ingress.yaml b/helm/capif/charts/helper/templates/ingress.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b3817bf910c95f374b285b6ca8db0c3a5d079c16 --- /dev/null +++ b/helm/capif/charts/helper/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "helper.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "helper.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm/capif/charts/helper/templates/ocf-helper-configmap.yaml b/helm/capif/charts/helper/templates/ocf-helper-configmap.yaml new file mode 100644 index 0000000000000000000000000000000000000000..796a55cf7e451343b05c793a4bb1eb40486029f2 --- /dev/null +++ b/helm/capif/charts/helper/templates/ocf-helper-configmap.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: ocf-helper-configmap +data: + config.yaml: | + mongo: { + 'user': '{{ .Values.env.mongoInitdbRootUsername }}', + 'password': '{{ .Values.env.mongoInitdbRootPassword }}', + 'db': 'capif', + 'invoker_col': 'invokerdetails', + 'provider_col': 'providerenrolmentdetails', + 'col_services': "serviceapidescriptions", + 'col_security': "security", + 'col_event': "eventsdetails", + 'host': '{{ .Values.env.mongoHost }}', + 'port': "{{ .Values.env.mongoPort }}" + } + + ca_factory: { + "url": {{ quote .Values.env.vaultHostname }}, + "port": {{ quote .Values.env.vaultPort }}, + "token": {{ quote .Values.env.vaultAccessToken }} + } \ No newline at end of file diff --git a/helm/capif/charts/helper/templates/service.yaml b/helm/capif/charts/helper/templates/service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4a743709cbb1d317b323484402247bffacee3aaa --- /dev/null +++ b/helm/capif/charts/helper/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: helper + labels: + {{- include "helper.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "helper.selectorLabels" . | nindent 4 }} diff --git a/helm/capif/charts/helper/templates/serviceaccount.yaml b/helm/capif/charts/helper/templates/serviceaccount.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e0e6d79313e967d9ae9bedc817a49b6e82aa7642 --- /dev/null +++ b/helm/capif/charts/helper/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "helper.serviceAccountName" . }} + labels: + {{- include "helper.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/helm/capif/charts/helper/templates/tests/test-connection.yaml b/helm/capif/charts/helper/templates/tests/test-connection.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f3959cc03d5bb449a71b2b24a09fdf5f6bf3702c --- /dev/null +++ b/helm/capif/charts/helper/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "helper.fullname" . }}-test-connection" + labels: + {{- include "helper.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "helper.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/helm/capif/charts/helper/values.yaml b/helm/capif/charts/helper/values.yaml new file mode 100644 index 0000000000000000000000000000000000000000..147c003af380d92300cac7baa0c486ba71753fce --- /dev/null +++ b/helm/capif/charts/helper/values.yaml @@ -0,0 +1,119 @@ +# Default values for helper. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: "helper" + pullPolicy: Always + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +env: + vaultHostname: vault-internal.mon.svc.cluster.local + vaultPort: 8200 + vaultAccessToken: dev-only-token + mongoHost: mongo + mongoPort: 27017 + capifHostname: capif + mongoInitdbRootUsername: root + mongoInitdbRootPassword: example + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: + app: ocf-helper + +podLabels: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 8080 + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +livenessProbe: + tcpSocket: + port: 8080 +readinessProbe: + tcpSocket: + port: 8080 + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +# Additional volumes on the output Deployment definition. +volumes: + - name: ocf-helper-configmap + configMap: + name: ocf-helper-configmap + items: + - key: "config.yaml" + path: "config.yaml" + +# Additional volumeMounts on the output Deployment definition. +volumeMounts: + - name: ocf-helper-configmap + mountPath: /usr/src/app/config.yaml + subPath: config.yaml + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/helm/capif/templates/mongo-register-pvc.yaml b/helm/capif/templates/mongo-register-pvc.yaml index 4d1a259bb9fe43636afb4bf19aa0f3a706e4d04f..b5a11d663ee0d10949768b4f1073908154b5d0c2 100644 --- a/helm/capif/templates/mongo-register-pvc.yaml +++ b/helm/capif/templates/mongo-register-pvc.yaml @@ -9,7 +9,7 @@ metadata: spec: storageClassName: {{ .Values.mongoRegister.mongo.persistence.storageClass }} accessModes: - - ReadWriteOnce + - ReadWriteMany resources: requests: storage: {{ .Values.mongoRegister.mongo.persistence.storage }} diff --git a/helm/capif/templates/register-configmap.yaml b/helm/capif/templates/register-configmap.yaml index 51293a5b7824d6688d9545d600c5571f9db4d55d..7dcc300663333ac8276f068571b6a8380df4458e 100644 --- a/helm/capif/templates/register-configmap.yaml +++ b/helm/capif/templates/register-configmap.yaml @@ -18,4 +18,10 @@ data: "url": "{{ .Values.parametersVault.env.vaultHostname }}", "port": "{{ .Values.parametersVault.env.vaultPort }}", "token": "{{ .Values.parametersVault.env.vaultAccessToken }}" + } + register: { + register_uuid: '6ba7b810-9dad-11d1-80b4-00c04fd430c8', + refresh_expiration: 30, #days + token_expiration: 10, #mins + admin_users: {admin: "password123"} } \ No newline at end of file diff --git a/helm/capif/values.yaml b/helm/capif/values.yaml index 43083490092bc0e0656336ab460db9d3feaa5d45..f017ac0f0add5d0c6f03325104c3b5a3dbd2a365 100644 --- a/helm/capif/values.yaml +++ b/helm/capif/values.yaml @@ -36,7 +36,7 @@ accessControlPolicy: CapifClient: # -- If enable capif client. - enable: "true" + enable: "" image: # -- The docker image repository to use repository: "public.ecr.aws/o2v4a8t6/opencapif/client" @@ -523,6 +523,18 @@ parametersVault: vaultHostname: vault-internal.mon.svc.cluster.local vaultPort: 8200 vaultAccessToken: dev-only-token + +helper: + env: + vaultHostname: vault-internal.mon.svc.cluster.local + vaultPort: 8200 + vaultAccessToken: dev-only-token + mongoHost: mongo + mongoPort: 27017 + capifHostname: my-capif.apps.ocp-epg.hi.inet + mongoInitdbRootUsername: root + mongoInitdbRootPassword: example + # -- With tempo.enabled: false. It won't be deployed # -- If monitoring.enable: "true". Also enable tempo.enabled: true tempo: @@ -607,7 +619,7 @@ monitoring: prometheus: # -- With prometheus.enabled: "". It won't be deployed. prometheus.enable: "true" # -- It will deploy prometheus - enable: "true" + enable: "" image: # -- The docker image repository to use repository: "prom/prometheus" @@ -636,7 +648,7 @@ monitoring: # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - - host: prometheus.5gnacar.int + - host: prometheus.test.int paths: - path: / pathType: Prefix @@ -647,7 +659,7 @@ monitoring: # -- If ingressRoute enable=true, use monitoring.prometheus.ingress.enabled="" ingressRoute: enable: "" - host: prometheus.5gnacar.int + host: prometheus.test.int grafana: image: # -- The docker image repository to use @@ -686,7 +698,7 @@ monitoring: # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - - host: grafana.5gnacar.int + - host: grafana.test.int paths: - path: / pathType: Prefix diff --git a/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/controllers/default_controller.py b/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/controllers/default_controller.py index 9f94b19ee0222992649bf903fc19a7ea3e8577e9..9ea3c42833a20adcf5d6ef598fc17de3efdfd644 100644 --- a/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/controllers/default_controller.py +++ b/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/controllers/default_controller.py @@ -12,7 +12,6 @@ from ..core.publisher import Publisher from functools import wraps invoker_operations = InvokerManagementOperations() -publisher_ops = Publisher() valid_user = ControlAccess() @@ -56,11 +55,6 @@ def onboarded_invokers_onboarding_id_delete(onboarding_id): # noqa: E501 current_app.logger.info("Removing invoker") res = invoker_operations.remove_apiinvokerenrolmentdetail(onboarding_id) - if res.status_code == 204: - current_app.logger.info("Invoker Removed") - publisher_ops.publish_message("events", "API_INVOKER_OFFBOARDED") - publisher_ops.publish_message("internal-messages", f"invoker-removed:{onboarding_id}") - return res @cert_validation() @@ -81,10 +75,6 @@ def onboarded_invokers_onboarding_id_put(onboarding_id, body): # noqa: E501 body = APIInvokerEnrolmentDetails.from_dict(connexion.request.get_json()) # noqa: E501 res = invoker_operations.update_apiinvokerenrolmentdetail(onboarding_id,body) - if res.status_code == 200: - current_app.logger.info("Invoker Updated") - publisher_ops.publish_message("events", "API_INVOKER_UPDATED") - return res @@ -108,8 +98,5 @@ def onboarded_invokers_post(body): # noqa: E501 current_app.logger.info("Creating Invoker") res = invoker_operations.add_apiinvokerenrolmentdetail(body, username, uuid) - if res.status_code == 201: - current_app.logger.info("Invoker Created") - publisher_ops.publish_message("events", "API_INVOKER_ONBOARDED") return res diff --git a/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/core/apiinvokerenrolmentdetails.py b/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/core/apiinvokerenrolmentdetails.py index 7becdca29a7dbca35a1dbc89cf1c966da288cc75..3c3c38bdcc8f19bcf05deb7573e2a1f40110ccee 100644 --- a/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/core/apiinvokerenrolmentdetails.py +++ b/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/core/apiinvokerenrolmentdetails.py @@ -11,9 +11,10 @@ from .auth_manager import AuthManager from .resources import Resource from ..config import Config from api_invoker_management.models.api_invoker_enrolment_details import APIInvokerEnrolmentDetails +from .redis_event import RedisEvent +from .publisher import Publisher - - +publisher_ops = Publisher() class InvokerManagementOperations(Resource): def __check_api_invoker_id(self, api_invoker_id): @@ -83,7 +84,7 @@ class InvokerManagementOperations(Resource): invoker_dict["username"]=username invoker_dict["uuid"]=uuid - mycol.insert_one(apiinvokerenrolmentdetail.to_dict()) + mycol.insert_one(invoker_dict) current_app.logger.debug("Invoker inserted in database") current_app.logger.debug("Netapp onboarded sucessfuly") @@ -92,6 +93,10 @@ class InvokerManagementOperations(Resource): res = make_response(object=dict_to_camel_case(clean_empty(apiinvokerenrolmentdetail.to_dict())), status=201) res.headers['Location'] = "/api-invoker-management/v1/onboardedInvokers/" + str(api_invoker_id) + + if res.status_code == 201: + current_app.logger.info("Invoker Created") + RedisEvent("API_INVOKER_ONBOARDED", "apiInvokerIds", [str(api_invoker_id)]).send_event() return res # except Exception as e: @@ -131,6 +136,9 @@ class InvokerManagementOperations(Resource): invoker_updated = APIInvokerEnrolmentDetails().from_dict(dict_to_camel_case(result)) res = make_response(object=dict_to_camel_case(clean_empty(invoker_updated.to_dict())), status=200) + if res.status_code == 200: + current_app.logger.info("Invoker Updated") + RedisEvent("API_INVOKER_UPDATED", "apiInvokerIds", [onboard_id]).send_event() return res except Exception as e: @@ -154,7 +162,12 @@ class InvokerManagementOperations(Resource): current_app.logger.debug("Invoker resource removed from database") current_app.logger.debug("Netapp offboarded sucessfuly") out = "The Netapp matching onboardingId " + onboard_id + " was offboarded." - return make_response(out, status=204) + res = make_response(out, status=204) + if res.status_code == 204: + current_app.logger.info("Invoker Removed") + RedisEvent("API_INVOKER_OFFBOARDED", "apiInvokerIds", [onboard_id]).send_event() + publisher_ops.publish_message("internal-messages", f"invoker-removed:{onboard_id}") + return res except Exception as e: exception = "An exception occurred in remove invoker" diff --git a/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/core/redis_event.py b/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/core/redis_event.py new file mode 100644 index 0000000000000000000000000000000000000000..aadbdbb6b3116cf288648de41effdb12b9ca9143 --- /dev/null +++ b/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/core/redis_event.py @@ -0,0 +1,41 @@ +from ..encoder import JSONEncoder +from .publisher import Publisher +import json + +publisher_ops = Publisher() + + +class RedisEvent(): + def __init__(self, event, event_detail_key=None, information=None) -> None: + self.EVENTS_ENUM = [ + 'SERVICE_API_AVAILABLE', + 'SERVICE_API_UNAVAILABLE', + 'SERVICE_API_UPDATE', + 'API_INVOKER_ONBOARDED', + 'API_INVOKER_OFFBOARDED', + 'SERVICE_API_INVOCATION_SUCCESS', + 'SERVICE_API_INVOCATION_FAILURE', + 'ACCESS_CONTROL_POLICY_UPDATE', + 'ACCESS_CONTROL_POLICY_UNAVAILABLE', + 'API_INVOKER_AUTHORIZATION_REVOKED', + 'API_INVOKER_UPDATED', + 'API_TOPOLOGY_HIDING_CREATED', + 'API_TOPOLOGY_HIDING_REVOKED'] + if event not in self.EVENTS_ENUM: + raise Exception( + "Event (" + event + ") is not on event enum (" + ','.join(self.EVENTS_ENUM) + ")") + self.redis_event = { + "event": event + } + if event_detail_key != None and information != None: + self.redis_event['key'] = event_detail_key + self.redis_event['information'] = information + + def to_string(self): + return json.dumps(self.redis_event, cls=JSONEncoder) + + def send_event(self): + publisher_ops.publish_message("events", self.to_string()) + + def __call__(self): + return self.redis_event diff --git a/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/db/db.py b/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/db/db.py index bb933931c94cabeb427afe476c2afc6882ebdacc..6cb5a93bca3e130f884139e55951259ee7cb412a 100644 --- a/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/db/db.py +++ b/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/db/db.py @@ -15,7 +15,6 @@ class MongoDatabse(): self.config = Config().get_config() self.db = self.__connect() self.invoker_enrolment_details = self.config['mongo']['col'] - self.capif_users = self.config['mongo']['capif_users_col'] self.service_col = self.config['mongo']["service_col"] self.certs_col = self.config['mongo']['certs_col'] diff --git a/services/TS29222_CAPIF_API_Invoker_Management_API/config.yaml b/services/TS29222_CAPIF_API_Invoker_Management_API/config.yaml index e208a78ccfe2a48d16071e149b36575ba865e4d7..2a14561d485ebb8a81779e988a54b31f9c894fbb 100644 --- a/services/TS29222_CAPIF_API_Invoker_Management_API/config.yaml +++ b/services/TS29222_CAPIF_API_Invoker_Management_API/config.yaml @@ -3,20 +3,12 @@ mongo: { 'password': 'example', 'db': 'capif', 'col': 'invokerdetails', - 'capif_users_col': "user", 'certs_col': "certs", 'service_col': 'serviceapidescriptions', 'host': 'mongo', 'port': "27017" } -mongo_register: { - 'user': 'root', - 'password': 'example', - 'db': 'capif_users', - 'col': 'user', - 'host': 'mongo_register', - 'port': '27017' -} + ca_factory: { "url": "vault", "port": "8200", diff --git a/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/provider_enrolment_details_api.py b/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/provider_enrolment_details_api.py index 61ab74d5c4720a1a6439ea16c4a3ec18339fec5b..0b54ee8152e85552bd99c5d64560498e67e69721 100644 --- a/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/provider_enrolment_details_api.py +++ b/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/provider_enrolment_details_api.py @@ -94,7 +94,7 @@ class ProviderManagementOperations(Resource): self.auth_manager.remove_auth_provider([apf_id[0], aef_id[0], amf_id[0]]) - self.publish_ops.publish_message("internal-messages", f"provider-removed:{aef_id[0]}:{apf_id[0]}") + self.publish_ops.publish_message("internal-messages", f"provider-removed:{aef_id[0]}:{apf_id[0]}:{amf_id[0]}") return make_response(object=out, status=204) except Exception as e: diff --git a/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/db/db.py b/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/db/db.py index e2aba93033999abd5e5de371296534cf4f54761c..fcc6169db2432423538a7ea2dbf07ca0b3f3824c 100644 --- a/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/db/db.py +++ b/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/db/db.py @@ -16,7 +16,6 @@ class MongoDatabse(): self.config = Config().get_config() self.db = self.__connect() self.provider_enrolment_details = self.config['mongo']['col'] - self.capif_users = self.config['mongo']['capif_users'] self.certs_col = self.config['mongo']['certs_col'] def get_col_by_name(self, name): diff --git a/services/TS29222_CAPIF_API_Provider_Management_API/config.yaml b/services/TS29222_CAPIF_API_Provider_Management_API/config.yaml index 8de29a4385fffc05236cae4a9e72f9490969e47f..7d1899ab7a860fa1a6a0390f5fa4cf85271dc434 100644 --- a/services/TS29222_CAPIF_API_Provider_Management_API/config.yaml +++ b/services/TS29222_CAPIF_API_Provider_Management_API/config.yaml @@ -4,19 +4,10 @@ mongo: { 'db': 'capif', 'col': 'providerenrolmentdetails', 'certs_col': "certs", - 'capif_users': 'user', 'host': 'mongo', 'port': "27017" } -mongo_register: { - 'user': 'root', - 'password': 'example', - 'db': 'capif_users', - 'col': 'user', - 'host': 'mongo_register', - 'port': '27017' -} ca_factory: { "url": "vault", diff --git a/services/TS29222_CAPIF_Access_Control_Policy_API/capif_acl/core/accesscontrolpolicyapi.py b/services/TS29222_CAPIF_Access_Control_Policy_API/capif_acl/core/accesscontrolpolicyapi.py index 4a7280d2dcc40319f21b65d00bda4229fee0148d..d82ac984b04e746ad372a3e0574a67e50a77d88f 100644 --- a/services/TS29222_CAPIF_Access_Control_Policy_API/capif_acl/core/accesscontrolpolicyapi.py +++ b/services/TS29222_CAPIF_Access_Control_Policy_API/capif_acl/core/accesscontrolpolicyapi.py @@ -21,8 +21,8 @@ class accessControlPolicyApi(Resource): projection = {"_id":0} if api_invoker_id is not None: - query['apiInvokerPolicies.api_invoker_id'] = api_invoker_id - projection['apiInvokerPolicies.$'] = 1 + query['api_invoker_policies.api_invoker_id'] = api_invoker_id + projection['api_invoker_policies.$'] = 1 if supported_features is not None: current_app.logger.debug(f"SupportedFeatures present on query with value {supported_features}, but currently not used") @@ -38,8 +38,8 @@ class accessControlPolicyApi(Resource): current_app.logger.debug(policies) - api_invoker_policies = policies[0]['apiInvokerPolicies'] - current_app.logger.debug(f"apiinvokerPolicies: {api_invoker_policies}") + api_invoker_policies = policies[0]['api_invoker_policies'] + current_app.logger.debug(f"api_invoker_policies: {api_invoker_policies}") if not api_invoker_policies: current_app.logger.info(f"ACLs list is present but empty, then no ACLs found for the requested service: {service_api_id}, aef_id: {aef_id}, invoker: {api_invoker_id} and supportedFeatures: {supported_features}") #Not found error diff --git a/services/TS29222_CAPIF_Access_Control_Policy_API/capif_acl/core/internal_service_ops.py b/services/TS29222_CAPIF_Access_Control_Policy_API/capif_acl/core/internal_service_ops.py index 82d1f43d8060457f741795d1590d2842df203dd2..68c7ef57e1b9777022dbdb78bd40cddfee8d69c6 100644 --- a/services/TS29222_CAPIF_Access_Control_Policy_API/capif_acl/core/internal_service_ops.py +++ b/services/TS29222_CAPIF_Access_Control_Policy_API/capif_acl/core/internal_service_ops.py @@ -4,84 +4,109 @@ from .resources import Resource from models.api_invoker_policy import ApiInvokerPolicy from models.time_range_list import TimeRangeList from datetime import datetime, timedelta + from core.publisher import Publisher +from .redis_event import RedisEvent +from util import dict_to_camel_case, clean_empty publisher_ops = Publisher() class InternalServiceOps(Resource): - + def create_acl(self, invoker_id, service_id, aef_id): current_app.logger.info(f"Creating ACL for invoker: {invoker_id}") if "acls" not in self.db.db.list_collection_names(): self.db.db.create_collection("acls") - + mycol = self.db.get_col_by_name(self.db.acls) - res = mycol.find_one({"service_id": service_id, "aef_id":aef_id}, {"_id":0}) - + res = mycol.find_one( + {"service_id": service_id, "aef_id": aef_id}, {"_id": 0}) + if res: - current_app.logger.info(f"Adding invoker ACL for invoker {invoker_id}") - range_list = [TimeRangeList(datetime.utcnow(), datetime.utcnow()+timedelta(days=365))] - invoker_acl = ApiInvokerPolicy(invoker_id, current_app.config["invocations"]["total"], current_app.config["invocations"]["perSecond"], range_list) - r = mycol.find_one({"service_id": service_id, "aef_id":aef_id, "apiInvokerPolicies.api_invoker_id": invoker_id}, {"_id":0}) + current_app.logger.info( + f"Adding invoker ACL for invoker {invoker_id}") + range_list = [TimeRangeList( + datetime.utcnow(), datetime.utcnow()+timedelta(days=365))] + invoker_acl = ApiInvokerPolicy( + invoker_id, current_app.config["invocations"]["total"], current_app.config["invocations"]["perSecond"], range_list) + r = mycol.find_one({"service_id": service_id, "aef_id": aef_id, + "api_invoker_policies.api_invoker_id": invoker_id}, {"_id": 0}) if r is None: - mycol.update_one({"service_id": service_id, "aef_id":aef_id }, {"$push":{"apiInvokerPolicies":invoker_acl.to_dict()}}) + mycol.update_one({"service_id": service_id, "aef_id": aef_id}, { + "$push": {"api_invoker_policies": invoker_acl.to_dict()}}) else: - current_app.logger.info(f"Creating service ACLs for service: {service_id}") - range_list = [TimeRangeList(datetime.utcnow(), datetime.utcnow()+timedelta(days=365))] - invoker_acl = ApiInvokerPolicy(invoker_id, current_app.config["invocations"]["total"], current_app.config["invocations"]["perSecond"], range_list) - - + current_app.logger.info( + f"Creating service ACLs for service: {service_id}") + range_list = [TimeRangeList( + datetime.utcnow(), datetime.utcnow()+timedelta(days=365))] + invoker_acl = ApiInvokerPolicy( + invoker_id, current_app.config["invocations"]["total"], current_app.config["invocations"]["perSecond"], range_list) service_acls = { "service_id": service_id, "aef_id": aef_id, - "apiInvokerPolicies": [invoker_acl.to_dict()] + "api_invoker_policies": [invoker_acl.to_dict()] } - mycol.insert_one(service_acls) - publisher_ops.publish_message("events", "ACCESS_CONTROL_POLICY_UPDATE") - - current_app.logger.info(f"Invoker ACL added for invoker: {invoker_id} for service: {service_id}") - + result = mycol.insert_one(service_acls) + + inserted_service_acls=mycol.find_one({"_id": result.inserted_id}, {"_id": 0}) + current_app.logger.info(inserted_service_acls) + inserted_service_acls_camel=dict_to_camel_case(inserted_service_acls) + current_app.logger.info(inserted_service_acls_camel) + accCtrlPolListExt = { + "apiId": service_id, + "apiInvokerPolicies": inserted_service_acls_camel['apiInvokerPolicies'] + } + RedisEvent("ACCESS_CONTROL_POLICY_UPDATE", + "accCtrlPolList", accCtrlPolListExt).send_event() + + current_app.logger.info( + f"Invoker ACL added for invoker: {invoker_id} for service: {service_id}") + def remove_acl(self, invoker_id, service_id, aef_id): current_app.logger.info(f"Removing ACL for invoker: {invoker_id}") - + mycol = self.db.get_col_by_name(self.db.acls) - res = mycol.find_one({"service_id": service_id, "aef_id":aef_id}, {"_id":0}) - + res = mycol.find_one( + {"service_id": service_id, "aef_id": aef_id}, {"_id": 0}) + if res: - mycol.update_many({"service_id": service_id, "aef_id":aef_id}, - {"$pull":{ "apiInvokerPolicies": { "api_invoker_id": invoker_id }}} - ) + mycol.update_many({"service_id": service_id, "aef_id": aef_id}, + {"$pull": {"api_invoker_policies": { + "api_invoker_id": invoker_id}}} + ) else: - current_app.logger.info(f"Not found: {service_id} for api : {service_id}") - - publisher_ops.publish_message("events", "ACCESS_CONTROL_POLICY_UNAVAILABLE") - - current_app.logger.info(f"Invoker ACL removed for invoker: {invoker_id} for service: {service_id}") - + current_app.logger.info( + f"Not found: {service_id} for api : {service_id}") + + RedisEvent("ACCESS_CONTROL_POLICY_UNAVAILABLE").send_event() + + current_app.logger.info( + f"Invoker ACL removed for invoker: {invoker_id} for service: {service_id}") + def remove_invoker_acl(self, invoker_id): current_app.logger.info(f"Removing ACLs for invoker: {invoker_id}") mycol = self.db.get_col_by_name(self.db.acls) - - mycol.update_many({"apiInvokerPolicies.api_invoker_id": invoker_id}, - {"$pull":{ "apiInvokerPolicies": { "api_invoker_id": invoker_id }}} - ) - publisher_ops.publish_message("events", "ACCESS_CONTROL_POLICY_UNAVAILABLE") + + mycol.update_many({"api_invoker_policies.api_invoker_id": invoker_id}, + {"$pull": {"api_invoker_policies": { + "api_invoker_id": invoker_id}}} + ) + RedisEvent("ACCESS_CONTROL_POLICY_UNAVAILABLE").send_event() current_app.logger.info(f"ACLs for invoker: {invoker_id} removed") - + def remove_provider_acls(self, id): current_app.logger.info(f"Removing ACLs for provider/service: {id}") mycol = self.db.get_col_by_name(self.db.acls) - - mycol.delete_many({"$or":[{"service_id":id}, {"aef_id":id}]} - ) - publisher_ops.publish_message("events", "ACCESS_CONTROL_POLICY_UNAVAILABLE") - current_app.logger.info(f"ACLs for provider/service: {id} removed") \ No newline at end of file + + mycol.delete_many({"$or": [{"service_id": id}, {"aef_id": id}]}) + RedisEvent("ACCESS_CONTROL_POLICY_UNAVAILABLE").send_event() + current_app.logger.info(f"ACLs for provider/service: {id} removed") diff --git a/services/TS29222_CAPIF_Access_Control_Policy_API/capif_acl/core/redis_event.py b/services/TS29222_CAPIF_Access_Control_Policy_API/capif_acl/core/redis_event.py new file mode 100644 index 0000000000000000000000000000000000000000..40e3e406830e58a72359e4283d8ff6e0d66bb893 --- /dev/null +++ b/services/TS29222_CAPIF_Access_Control_Policy_API/capif_acl/core/redis_event.py @@ -0,0 +1,41 @@ +from encoder import JSONEncoder +from .publisher import Publisher +import json + +publisher_ops = Publisher() + + +class RedisEvent(): + def __init__(self, event, event_detail_key=None, information=None) -> None: + self.EVENTS_ENUM = [ + 'SERVICE_API_AVAILABLE', + 'SERVICE_API_UNAVAILABLE', + 'SERVICE_API_UPDATE', + 'API_INVOKER_ONBOARDED', + 'API_INVOKER_OFFBOARDED', + 'SERVICE_API_INVOCATION_SUCCESS', + 'SERVICE_API_INVOCATION_FAILURE', + 'ACCESS_CONTROL_POLICY_UPDATE', + 'ACCESS_CONTROL_POLICY_UNAVAILABLE', + 'API_INVOKER_AUTHORIZATION_REVOKED', + 'API_INVOKER_UPDATED', + 'API_TOPOLOGY_HIDING_CREATED', + 'API_TOPOLOGY_HIDING_REVOKED'] + if event not in self.EVENTS_ENUM: + raise Exception( + "Event (" + event + ") is not on event enum (" + ','.join(self.EVENTS_ENUM) + ")") + self.redis_event = { + "event": event + } + if event_detail_key != None and information != None: + self.redis_event['key'] = event_detail_key + self.redis_event['information'] = information + + def to_string(self): + return json.dumps(self.redis_event, cls=JSONEncoder) + + def send_event(self): + publisher_ops.publish_message("events", self.to_string()) + + def __call__(self): + return self.redis_event diff --git a/services/TS29222_CAPIF_Events_API/Dockerfile b/services/TS29222_CAPIF_Events_API/Dockerfile index ddcf2da935752d7eb90aa1dd59c2eb5811eaf5e0..1fef3f2ede15e1de5674980d38a35fbbeebb6c7b 100644 --- a/services/TS29222_CAPIF_Events_API/Dockerfile +++ b/services/TS29222_CAPIF_Events_API/Dockerfile @@ -13,6 +13,6 @@ COPY . /usr/src/app EXPOSE 8080 -ENTRYPOINT ["python3"] +ENTRYPOINT ["gunicorn"] -CMD ["-m", "capif_events"] \ No newline at end of file +CMD ["--bind", "0.0.0.0:8080", "--chdir", "/usr/src/app/capif_events", "wsgi:app"] \ No newline at end of file diff --git a/services/TS29222_CAPIF_Events_API/capif_events/__main__.py b/services/TS29222_CAPIF_Events_API/capif_events/app.py similarity index 95% rename from services/TS29222_CAPIF_Events_API/capif_events/__main__.py rename to services/TS29222_CAPIF_Events_API/capif_events/app.py index 1e602ef80d3468f980ce3c9b19d455cfeee1f285..d72ecc640eac6ca7c816323eb3c34988534b403e 100644 --- a/services/TS29222_CAPIF_Events_API/capif_events/__main__.py +++ b/services/TS29222_CAPIF_Events_API/capif_events/app.py @@ -4,21 +4,21 @@ import connexion import sys import logging -from capif_events import encoder +import encoder from flask import Flask, jsonify, request from flask_jwt_extended import JWTManager, jwt_required, create_access_token from pymongo import MongoClient -from .config import Config -from .core.notifications import Notifications -from .core.consumer_messager import Subscriber + +from core.notifications import Notifications +from core.consumer_messager import Subscriber from multiprocessing import Process from threading import Thread from flask_executor import Executor from flask_apscheduler import APScheduler from logging.handlers import RotatingFileHandler -from .config import Config +from config import Config import os from fluent import sender from flask_executor import Executor @@ -32,7 +32,6 @@ from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.instrumentation.redis import RedisInstrumentor - NAME = "Events-Service" def configure_monitoring(app, config): @@ -147,5 +146,5 @@ subscriber = Subscriber() def create_listener_message(): executor.submit(subscriber.listen) -if __name__ == '__main__': - app.run(debug=True, port=8080) +# if __name__ == '__main__': +# app.run(debug=True, port=8080) diff --git a/services/TS29222_CAPIF_Events_API/capif_events/config.py b/services/TS29222_CAPIF_Events_API/capif_events/config.py index 377b14f8c368cebf98409223d089fc31326d1987..bed212ac136607ee7459f333b0f50bf7f3481b6b 100644 --- a/services/TS29222_CAPIF_Events_API/capif_events/config.py +++ b/services/TS29222_CAPIF_Events_API/capif_events/config.py @@ -5,7 +5,7 @@ import os class Config: def __init__(self): self.cached = 0 - self.file="./config.yaml" + self.file="../config.yaml" self.my_config = {} stamp = os.stat(self.file).st_mtime if stamp != self.cached: diff --git a/services/TS29222_CAPIF_Events_API/capif_events/core/consumer_messager.py b/services/TS29222_CAPIF_Events_API/capif_events/core/consumer_messager.py index 0f9fa6ab3653ba97701c3f0e034809c69d6b5a08..ce6479afb5c3537246cb1b2192709c6e4ed83d8c 100644 --- a/services/TS29222_CAPIF_Events_API/capif_events/core/consumer_messager.py +++ b/services/TS29222_CAPIF_Events_API/capif_events/core/consumer_messager.py @@ -20,14 +20,18 @@ class Subscriber(): def listen(self): for raw_message in self.p.listen(): + current_app.logger.info(raw_message) if raw_message["type"] == "message" and raw_message["channel"].decode('utf-8') == "events": current_app.logger.info("Event received") - self.notification.send_notifications(raw_message["data"].decode('utf-8')) - + redis_event=json.loads(raw_message["data"].decode('utf-8')) + current_app.logger.info(json.dumps(redis_event, indent=4)) + self.notification.send_notifications(redis_event) elif raw_message["type"] == "message" and raw_message["channel"].decode('utf-8') == "internal-messages": - message, *invoker_id = raw_message["data"].decode('utf-8').split(":") - if message == "invoker-removed" and len(invoker_id)>0: - self.event_ops.delete_all_events(invoker_id[0]) + message, *subscriber_ids = raw_message["data"].decode('utf-8').split(":") + if message == "invoker-removed" and len(subscriber_ids)>0: + self.event_ops.delete_all_events(subscriber_ids) + if message == "provider-removed" and len(subscriber_ids)>0: + self.event_ops.delete_all_events(subscriber_ids) diff --git a/services/TS29222_CAPIF_Events_API/capif_events/core/events_apis.py b/services/TS29222_CAPIF_Events_API/capif_events/core/events_apis.py index 1ece644c124fcf17b47f8aaea59ab4c67c1b0e86..d6b5bdff29dcec3161150bd42b8c24b62636633b 100644 --- a/services/TS29222_CAPIF_Events_API/capif_events/core/events_apis.py +++ b/services/TS29222_CAPIF_Events_API/capif_events/core/events_apis.py @@ -12,7 +12,7 @@ from .resources import Resource from bson import json_util from .responses import internal_server_error, not_found_error, make_response, bad_request_error from ..db.db import MongoDatabse -from ..util import dict_to_camel_case +from ..util import dict_to_camel_case, clean_empty from .auth_manager import AuthManager class EventSubscriptionsOperations(Resource): @@ -73,7 +73,7 @@ class EventSubscriptionsOperations(Resource): self.auth_manager.add_auth_event(subscription_id, subscriber_id) - res = make_response(object=event_subscription, status=201) + res = make_response(object=dict_to_camel_case(clean_empty(event_subscription.to_dict())), status=201) res.headers['Location'] = "http://localhost:8080/capif-events/v1/" + \ str(subscriber_id) + "/subscriptions/" + str(subscription_id) diff --git a/services/TS29222_CAPIF_Events_API/capif_events/core/internal_event_ops.py b/services/TS29222_CAPIF_Events_API/capif_events/core/internal_event_ops.py index 3f4fc6a7274e9eddbfa51cf630f779d31bf18bfb..e49089a3756b4b3946bafa93b4138721e8a1be7b 100644 --- a/services/TS29222_CAPIF_Events_API/capif_events/core/internal_event_ops.py +++ b/services/TS29222_CAPIF_Events_API/capif_events/core/internal_event_ops.py @@ -8,22 +8,23 @@ class InternalEventOperations(Resource): Resource.__init__(self) self.auth_manager = AuthManager() - def delete_all_events(self, subscriber_id): + def delete_all_events(self, subscriber_ids): - mycol = self.db.get_col_by_name(self.db.event_collection) - my_query = {'subscriber_id': subscriber_id} - mycol.delete_many(my_query) + for subscriber_id in subscriber_ids: + mycol = self.db.get_col_by_name(self.db.event_collection) + my_query = {'subscriber_id': subscriber_id} + mycol.delete_many(my_query) - current_app.logger.info(f"Removed events for this subscriber: {subscriber_id}") + current_app.logger.info(f"Removed events for this subscriber: {subscriber_id}") #We dont need remove all auth events, becase when invoker is removed, remove auth entry #self.auth_manager.remove_auth_all_event(subscriber_id) def get_event_subscriptions(self, event): + current_app.logger.info("get subscription from db") try: mycol = self.db.get_col_by_name(self.db.event_collection) - - query= {'events':event} + query={'events':{'$in':[event]}} subscriptions = mycol.find(query) if subscriptions is None: @@ -39,21 +40,3 @@ class InternalEventOperations(Resource): except Exception as e: current_app.logger.error("An exception occurred ::" + str(e)) return False - - # def get_acls(self, service_id): - # try: - # mycol = self.db.get_col_by_name(self.db.acls_col) - - # query= {'api_id': service_id} - # acls = mycol.find(query) - - # if acls is None: - # current_app.logger.error("Not found event subscriptions") - - # else: - - # return acls - - # except Exception as e: - # current_app.logger.error("An exception occurred ::" + str(e)) - # return False \ No newline at end of file diff --git a/services/TS29222_CAPIF_Events_API/capif_events/core/notifications.py b/services/TS29222_CAPIF_Events_API/capif_events/core/notifications.py index a02f0abee8a4fa60a8405ea5175e20f3a33236c8..1ca9f2fe5fb17c91b31b845cb1716fc8c4a12df2 100644 --- a/services/TS29222_CAPIF_Events_API/capif_events/core/notifications.py +++ b/services/TS29222_CAPIF_Events_API/capif_events/core/notifications.py @@ -1,37 +1,41 @@ #import concurrent import requests from .internal_event_ops import InternalEventOperations -from ..models.event_notification import EventNotification -from ..models.access_control_policy_list_ext import AccessControlPolicyListExt -from ..models.capif_event_detail import CAPIFEventDetail -from ..encoder import JSONEncoder +from models.event_notification import EventNotification +from models.access_control_policy_list_ext import AccessControlPolicyListExt +from models.capif_event_detail import CAPIFEventDetail +from encoder import JSONEncoder import sys import json from flask import current_app +import asyncio +import aiohttp class Notifications(): def __init__(self): self.events_ops = InternalEventOperations() - def send_notifications(self, event): - current_app.logger.info("Received event, sending notifications") - subscriptions = self.events_ops.get_event_subscriptions(event) - # message, *ids = event.split(":") - + def send_notifications(self, redis_event): try: + if redis_event.get('event', None) == None: + raise("Event value is not present on received event from REDIS") + + current_app.logger.info("Received event " + redis_event.get('event') + ", sending notifications") + subscriptions = self.events_ops.get_event_subscriptions(redis_event.get('event')) + current_app.logger.info(subscriptions) + for sub in subscriptions: url = sub["notification_destination"] - data = EventNotification(sub["subscription_id"], events=event) - # details = CAPIFEventDetail() - # if message == "ACCESS_CONTROL_POLICY_UPDATE": - # current_app.logger.info("event: ACCESS_CONTROL_POLICY_UPDATE") - # acls = self.events_ops.get_acls(ids[0]) - # details.acc_ctrl_pol_list = AccessControlPolicyListExt(api_id=acls['service_id'], api_invoker_policies=acls['apiInvokerPolicies']) + current_app.logger.debug(url) + event_detail=None + if redis_event.get('key', None) != None and redis_event.get('information', None) != None: + event_detail={redis_event.get('key'):redis_event.get('information')} + current_app.logger.debug(event_detail) + data = EventNotification(sub["subscription_id"], events=redis_event.get('event'), event_detail=event_detail) + current_app.logger.debug(json.dumps(data,cls=JSONEncoder)) - # data.event_detail=details - self.request_post(url, data) - #current_app.logger.info("notification sended") + asyncio.run(self.send(url, json.loads(json.dumps(data,cls=JSONEncoder)))) except Exception as e: current_app.logger.error("An exception occurred ::" + str(e)) @@ -40,4 +44,20 @@ class Notifications(): def request_post(self, url, data): headers = {'content-type': 'application/json'} return requests.post(url, json={'text': str(data.to_str())}, headers=headers) - + + async def send_request(self, url, data): + async with aiohttp.ClientSession() as session: + timeout = aiohttp.ClientTimeout(total=10) # Establecer timeout a 10 segundos + headers = {'content-type': 'application/json'} + async with session.post(url, json=data, timeout=timeout, headers=headers) as response: + return await response.text() + + async def send(self, url, data): + try: + response = await self.send_request(url, data) + current_app.logger.debug(response) + except asyncio.TimeoutError: + current_app.logger.error("Timeout: Request timeout") + except Exception as e: + current_app.logger.error("An exception occurred sending notification::" + str(e)) + return False diff --git a/services/TS29222_CAPIF_Events_API/capif_events/core/resources.py b/services/TS29222_CAPIF_Events_API/capif_events/core/resources.py index 94e29ec07359c93a78d520f24fa91c88971d65c4..efbe3c2d4cf28cec298ef17b394011dcd6aa66cb 100644 --- a/services/TS29222_CAPIF_Events_API/capif_events/core/resources.py +++ b/services/TS29222_CAPIF_Events_API/capif_events/core/resources.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from ..db.db import MongoDatabse +from db.db import MongoDatabse class Resource(ABC): diff --git a/services/TS29222_CAPIF_Events_API/capif_events/core/responses.py b/services/TS29222_CAPIF_Events_API/capif_events/core/responses.py index 962c4b6dd6ae07bce79dc8e810f10a507d526663..7862390ddb6ad27d60a0e9a1d2e4f358111f2270 100644 --- a/services/TS29222_CAPIF_Events_API/capif_events/core/responses.py +++ b/services/TS29222_CAPIF_Events_API/capif_events/core/responses.py @@ -1,6 +1,7 @@ from ..models.problem_details import ProblemDetails from ..encoder import JSONEncoder from flask import Response +from ..util import dict_to_camel_case, clean_empty import json mimetype = "application/json" @@ -13,19 +14,35 @@ def make_response(object, status): def internal_server_error(detail, cause): prob = ProblemDetails(title="Internal Server Error", status=500, detail=detail, cause=cause) + prob = prob.to_dict() + prob = clean_empty(prob) + prob = dict_to_camel_case(prob) + return Response(json.dumps(prob, cls=JSONEncoder), status=500, mimetype=mimetype) def forbidden_error(detail, cause): prob = ProblemDetails(title="Forbidden", status=403, detail=detail, cause=cause) + prob = prob.to_dict() + prob = clean_empty(prob) + prob = dict_to_camel_case(prob) + return Response(json.dumps(prob, cls=JSONEncoder), status=403, mimetype=mimetype) def bad_request_error(detail, cause, invalid_params): prob = ProblemDetails(title="Bad Request", status=400, detail=detail, cause=cause, invalid_params=invalid_params) + prob = prob.to_dict() + prob = clean_empty(prob) + prob = dict_to_camel_case(prob) + return Response(json.dumps(prob, cls=JSONEncoder), status=400, mimetype=cause) def not_found_error(detail, cause): prob = ProblemDetails(title="Not Found", status=404, detail=detail, cause=cause) + prob = prob.to_dict() + prob = clean_empty(prob) + prob = dict_to_camel_case(prob) + return Response(json.dumps(prob, cls=JSONEncoder), status=404, mimetype=mimetype) \ No newline at end of file diff --git a/services/TS29222_CAPIF_Events_API/capif_events/db/db.py b/services/TS29222_CAPIF_Events_API/capif_events/db/db.py index 2f4d1190297508fd9b49946fc193ba92b6e2d99e..c68c8098fdb1a0139c1053fad19f2584c60bd856 100644 --- a/services/TS29222_CAPIF_Events_API/capif_events/db/db.py +++ b/services/TS29222_CAPIF_Events_API/capif_events/db/db.py @@ -2,7 +2,7 @@ import atexit import time from pymongo import MongoClient from pymongo.errors import AutoReconnect -from ..config import Config +from config import Config from bson.codec_options import CodecOptions import os from opentelemetry.instrumentation.pymongo import PymongoInstrumentor diff --git a/services/TS29222_CAPIF_Events_API/capif_events/encoder.py b/services/TS29222_CAPIF_Events_API/capif_events/encoder.py index f6b018f22f8e92eb3c919f4cb4546d61a550fcd0..80bad8fa9220ab873e044b7adc0a849746088ad5 100644 --- a/services/TS29222_CAPIF_Events_API/capif_events/encoder.py +++ b/services/TS29222_CAPIF_Events_API/capif_events/encoder.py @@ -1,7 +1,7 @@ from connexion.apps.flask_app import FlaskJSONEncoder import six -from capif_events.models.base_model_ import Model +from models.base_model_ import Model class JSONEncoder(FlaskJSONEncoder): diff --git a/services/TS29222_CAPIF_Events_API/capif_events/util.py b/services/TS29222_CAPIF_Events_API/capif_events/util.py index 396c6a5b258c051cf1c86b93d2bf79e998463ace..5a99e6772e3f91d6704a4e803819e32ff6da7d91 100644 --- a/services/TS29222_CAPIF_Events_API/capif_events/util.py +++ b/services/TS29222_CAPIF_Events_API/capif_events/util.py @@ -2,7 +2,19 @@ import datetime import six import typing -from capif_events import typing_utils +import typing_utils + + +def clean_empty(d): + if isinstance(d, dict): + return { + k: v + for k, v in ((k, clean_empty(v)) for k, v in d.items()) + if v + } + if isinstance(d, list): + return [v for v in map(clean_empty, d) if v] + return d def dict_to_camel_case(my_dict): diff --git a/services/TS29222_CAPIF_Events_API/capif_events/wsgi.py b/services/TS29222_CAPIF_Events_API/capif_events/wsgi.py new file mode 100644 index 0000000000000000000000000000000000000000..6026b0fa96078634d3455ab93d71dcdc78774276 --- /dev/null +++ b/services/TS29222_CAPIF_Events_API/capif_events/wsgi.py @@ -0,0 +1,4 @@ +from app import app + +if __name__ == "__main__": + app.run() diff --git a/services/TS29222_CAPIF_Events_API/requirements.txt b/services/TS29222_CAPIF_Events_API/requirements.txt index bbef950cda414a17f3ebb70ca8689ae35c121ca0..8cc1fc1c02bd25ce5c4782ce9ca3a284f4c55ee8 100644 --- a/services/TS29222_CAPIF_Events_API/requirements.txt +++ b/services/TS29222_CAPIF_Events_API/requirements.txt @@ -21,3 +21,7 @@ rfc3987 redis flask_executor Flask-APScheduler +aiohttp==3.9.5 +async-timeout==4.0.3 +gunicorn==22.0.0 +packaging==24.0 \ No newline at end of file diff --git a/services/TS29222_CAPIF_Logging_API_Invocation_API/api_invocation_logs/core/invocationlogs.py b/services/TS29222_CAPIF_Logging_API_Invocation_API/api_invocation_logs/core/invocationlogs.py index fe4fc9f262167775dc38de1a77ac260fd6e733ff..752b214056aea69d4410dc92a75b7f4cc3bc4858 100644 --- a/services/TS29222_CAPIF_Logging_API_Invocation_API/api_invocation_logs/core/invocationlogs.py +++ b/services/TS29222_CAPIF_Logging_API_Invocation_API/api_invocation_logs/core/invocationlogs.py @@ -6,8 +6,13 @@ from flask import current_app, Flask, Response from pymongo import ReturnDocument from ..util import dict_to_camel_case, clean_empty +from ..encoder import JSONEncoder from .resources import Resource + from .responses import internal_server_error, not_found_error, unauthorized_error, make_response +from .redis_event import RedisEvent +import json + class LoggingInvocationOperations(Resource): @@ -56,7 +61,7 @@ class LoggingInvocationOperations(Resource): return None def add_invocationlog(self, aef_id, invocationlog): - + mycol = self.db.get_col_by_name(self.db.invocation_logs) try: @@ -74,11 +79,29 @@ class LoggingInvocationOperations(Resource): return result current_app.logger.debug("Check service apis") + event=None + invocation_log_base=json.loads(json.dumps(invocationlog, cls=JSONEncoder)) + for log in invocationlog.logs: result = self.__check_service_apis(log.api_id, log.api_name) + current_app.logger.debug("Inside for loop.") if result is not None: return result + + if log.result: + current_app.logger.debug(log) + if int(log.result) >= 200 and int(log.result) < 300: + event="SERVICE_API_INVOCATION_SUCCESS" + else: + event="SERVICE_API_INVOCATION_FAILURE" + + current_app.logger.info(event) + invocation_log_base['logs']=[log] + invocationLogs=[invocation_log_base] + RedisEvent(event,"invocationLogs",invocationLogs).send_event() + + current_app.logger.debug("After log check") current_app.logger.debug("Check existing logs") my_query = {'aef_id': aef_id, 'api_invoker_id': invocationlog.api_invoker_id} diff --git a/services/TS29222_CAPIF_Logging_API_Invocation_API/api_invocation_logs/core/publisher.py b/services/TS29222_CAPIF_Logging_API_Invocation_API/api_invocation_logs/core/publisher.py new file mode 100644 index 0000000000000000000000000000000000000000..a15c0d90e8d00233317a6c1ab748b8ff150be80e --- /dev/null +++ b/services/TS29222_CAPIF_Logging_API_Invocation_API/api_invocation_logs/core/publisher.py @@ -0,0 +1,11 @@ +import redis +import sys +from flask import current_app + +class Publisher(): + + def __init__(self): + self.r = redis.Redis(host='redis', port=6379, db=0) + + def publish_message(self, channel, message): + self.r.publish(channel, message) diff --git a/services/TS29222_CAPIF_Logging_API_Invocation_API/api_invocation_logs/core/redis_event.py b/services/TS29222_CAPIF_Logging_API_Invocation_API/api_invocation_logs/core/redis_event.py new file mode 100644 index 0000000000000000000000000000000000000000..aadbdbb6b3116cf288648de41effdb12b9ca9143 --- /dev/null +++ b/services/TS29222_CAPIF_Logging_API_Invocation_API/api_invocation_logs/core/redis_event.py @@ -0,0 +1,41 @@ +from ..encoder import JSONEncoder +from .publisher import Publisher +import json + +publisher_ops = Publisher() + + +class RedisEvent(): + def __init__(self, event, event_detail_key=None, information=None) -> None: + self.EVENTS_ENUM = [ + 'SERVICE_API_AVAILABLE', + 'SERVICE_API_UNAVAILABLE', + 'SERVICE_API_UPDATE', + 'API_INVOKER_ONBOARDED', + 'API_INVOKER_OFFBOARDED', + 'SERVICE_API_INVOCATION_SUCCESS', + 'SERVICE_API_INVOCATION_FAILURE', + 'ACCESS_CONTROL_POLICY_UPDATE', + 'ACCESS_CONTROL_POLICY_UNAVAILABLE', + 'API_INVOKER_AUTHORIZATION_REVOKED', + 'API_INVOKER_UPDATED', + 'API_TOPOLOGY_HIDING_CREATED', + 'API_TOPOLOGY_HIDING_REVOKED'] + if event not in self.EVENTS_ENUM: + raise Exception( + "Event (" + event + ") is not on event enum (" + ','.join(self.EVENTS_ENUM) + ")") + self.redis_event = { + "event": event + } + if event_detail_key != None and information != None: + self.redis_event['key'] = event_detail_key + self.redis_event['information'] = information + + def to_string(self): + return json.dumps(self.redis_event, cls=JSONEncoder) + + def send_event(self): + publisher_ops.publish_message("events", self.to_string()) + + def __call__(self): + return self.redis_event diff --git a/services/TS29222_CAPIF_Logging_API_Invocation_API/requirements.txt b/services/TS29222_CAPIF_Logging_API_Invocation_API/requirements.txt index cb1e43905a79b1fdcbe01282175cd884eb958efa..b08bd828f28be3597ba68c0748cbc4ac7ca6c1ce 100644 --- a/services/TS29222_CAPIF_Logging_API_Invocation_API/requirements.txt +++ b/services/TS29222_CAPIF_Logging_API_Invocation_API/requirements.txt @@ -6,6 +6,7 @@ Flask == 2.0.3 pymongo == 4.0.1 elasticsearch == 8.4.3 flask_jwt_extended == 4.4.4 +redis == 4.5.4 opentelemetry-instrumentation == 0.38b0 opentelemetry-instrumentation-flask == 0.38b0 opentelemetry-instrumentation-redis == 0.38b0 diff --git a/services/TS29222_CAPIF_Publish_Service_API/published_apis/controllers/default_controller.py b/services/TS29222_CAPIF_Publish_Service_API/published_apis/controllers/default_controller.py index ab80164959e10a8339bfa8395f80e7c1e534d8e7..62e506a6be747d94efa513917913f03a75f20ca3 100644 --- a/services/TS29222_CAPIF_Publish_Service_API/published_apis/controllers/default_controller.py +++ b/services/TS29222_CAPIF_Publish_Service_API/published_apis/controllers/default_controller.py @@ -1,7 +1,6 @@ import connexion from ..models.service_api_description import ServiceAPIDescription # noqa: E501 from ..core.serviceapidescriptions import PublishServiceOperations -from ..core.publisher import Publisher from flask import Response, request, current_app @@ -12,10 +11,10 @@ from functools import wraps service_operations = PublishServiceOperations() -publisher_ops = Publisher() valid_user = ControlAccess() + def cert_validation(): def _cert_validation(f): @wraps(f) @@ -25,13 +24,16 @@ def cert_validation(): cert_tmp = request.headers['X-Ssl-Client-Cert'] cert_raw = cert_tmp.replace('\t', '') - cert = x509.load_pem_x509_certificate(str.encode(cert_raw), default_backend()) + cert = x509.load_pem_x509_certificate( + str.encode(cert_raw), default_backend()) - cn = cert.subject.get_attributes_for_oid(x509.OID_COMMON_NAME)[0].value.strip() + cn = cert.subject.get_attributes_for_oid( + x509.OID_COMMON_NAME)[0].value.strip() if cn != "superadmin": cert_signature = cert.signature.hex() - result = valid_user.validate_user_cert(args["apfId"], args["serviceApiId"], cert_signature) + result = valid_user.validate_user_cert( + args["apfId"], args["serviceApiId"], cert_signature) if result is not None: return result @@ -41,6 +43,7 @@ def cert_validation(): return __cert_validation return _cert_validation + def apf_id_service_apis_get(apf_id): # noqa: E501 """apf_id_service_apis_get @@ -76,12 +79,9 @@ def apf_id_service_apis_post(apf_id, body): # noqa: E501 res = service_operations.add_serviceapidescription(apf_id, body) - if res.status_code == 201: - current_app.logger.info("Service published") - publisher_ops.publish_message("events", "SERVICE_API_AVAILABLE") - return res + @cert_validation() def apf_id_service_apis_service_api_id_delete(service_api_id, apf_id): # noqa: E501 """apf_id_service_apis_service_api_id_delete @@ -97,15 +97,12 @@ def apf_id_service_apis_service_api_id_delete(service_api_id, apf_id): # noqa: """ current_app.logger.info("Removing service published") - res = service_operations.delete_serviceapidescription(service_api_id, apf_id) - - if res.status_code == 204: - current_app.logger.info("Removed service published") - publisher_ops.publish_message("events", "SERVICE_API_UNAVAILABLE") - publisher_ops.publish_message("internal-messages", f"service-removed:{service_api_id}") + res = service_operations.delete_serviceapidescription( + service_api_id, apf_id) return res + @cert_validation() def apf_id_service_apis_service_api_id_get(service_api_id, apf_id): # noqa: E501 """apf_id_service_apis_service_api_id_get @@ -125,6 +122,7 @@ def apf_id_service_apis_service_api_id_get(service_api_id, apf_id): # noqa: E50 return res + @cert_validation() def apf_id_service_apis_service_api_id_put(service_api_id, apf_id, body): # noqa: E501 """apf_id_service_apis_service_api_id_put @@ -141,14 +139,13 @@ def apf_id_service_apis_service_api_id_put(service_api_id, apf_id, body): # noq :rtype: ServiceAPIDescription """ - current_app.logger.info("Updating service api id with id: " + service_api_id) + current_app.logger.info( + "Updating service api id with id: " + service_api_id) if connexion.request.is_json: body = ServiceAPIDescription.from_dict(connexion.request.get_json()) # noqa: E501 - response = service_operations.update_serviceapidescription(service_api_id, apf_id, body) - - if response.status_code == 200: - publisher_ops.publish_message("events", "SERVICE_API_UPDATE") + response = service_operations.update_serviceapidescription( + service_api_id, apf_id, body) return response diff --git a/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/consumer_messager.py b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/consumer_messager.py index f0b31598fcbc8c4f9b08fc38ca7d0dd8f4b623eb..f781ead9e3beb684bf6face38ef9c0e4fd5cc5bc 100644 --- a/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/consumer_messager.py +++ b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/consumer_messager.py @@ -16,7 +16,7 @@ class Subscriber(): for raw_message in self.p.listen(): if raw_message["type"] == "message" and raw_message["channel"].decode('utf-8') == "internal-messages": message, *ids = raw_message["data"].decode('utf-8').split(":") - if message == "provider-removed" and len(ids)==2: + if message == "provider-removed" and len(ids) > 0: self.security_ops.delete_intern_service(ids[1]) diff --git a/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/redis_event.py b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/redis_event.py new file mode 100644 index 0000000000000000000000000000000000000000..aadbdbb6b3116cf288648de41effdb12b9ca9143 --- /dev/null +++ b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/redis_event.py @@ -0,0 +1,41 @@ +from ..encoder import JSONEncoder +from .publisher import Publisher +import json + +publisher_ops = Publisher() + + +class RedisEvent(): + def __init__(self, event, event_detail_key=None, information=None) -> None: + self.EVENTS_ENUM = [ + 'SERVICE_API_AVAILABLE', + 'SERVICE_API_UNAVAILABLE', + 'SERVICE_API_UPDATE', + 'API_INVOKER_ONBOARDED', + 'API_INVOKER_OFFBOARDED', + 'SERVICE_API_INVOCATION_SUCCESS', + 'SERVICE_API_INVOCATION_FAILURE', + 'ACCESS_CONTROL_POLICY_UPDATE', + 'ACCESS_CONTROL_POLICY_UNAVAILABLE', + 'API_INVOKER_AUTHORIZATION_REVOKED', + 'API_INVOKER_UPDATED', + 'API_TOPOLOGY_HIDING_CREATED', + 'API_TOPOLOGY_HIDING_REVOKED'] + if event not in self.EVENTS_ENUM: + raise Exception( + "Event (" + event + ") is not on event enum (" + ','.join(self.EVENTS_ENUM) + ")") + self.redis_event = { + "event": event + } + if event_detail_key != None and information != None: + self.redis_event['key'] = event_detail_key + self.redis_event['information'] = information + + def to_string(self): + return json.dumps(self.redis_event, cls=JSONEncoder) + + def send_event(self): + publisher_ops.publish_message("events", self.to_string()) + + def __call__(self): + return self.redis_event diff --git a/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py index 9dd9beb1b5af8dbbbec9b675356f9ed3a03ea13c..41ce03de2243a40442ec3cddd04b11eebf5018bf 100644 --- a/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py +++ b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py @@ -7,26 +7,33 @@ from datetime import datetime from ..util import dict_to_camel_case, clean_empty from .responses import internal_server_error, forbidden_error, not_found_error, unauthorized_error, make_response from .auth_manager import AuthManager +from .redis_event import RedisEvent +from .publisher import Publisher + +publisher_ops = Publisher() service_api_not_found_message = "Service API not found" + class PublishServiceOperations(Resource): def __check_apf(self, apf_id): providers_col = self.db.get_col_by_name(self.db.capif_provider_col) current_app.logger.debug("Checking apf id") - provider = providers_col.find_one({"api_prov_funcs.api_prov_func_id": apf_id}) + provider = providers_col.find_one( + {"api_prov_funcs.api_prov_func_id": apf_id}) if provider is None: current_app.logger.error("Publisher not exist") - return unauthorized_error(detail = "Publisher not existing", cause = "Publisher id not found") + return unauthorized_error(detail="Publisher not existing", cause="Publisher id not found") - list_apf_ids = [func["api_prov_func_id"] for func in provider["api_prov_funcs"] if func["api_prov_func_role"] == "APF"] + list_apf_ids = [func["api_prov_func_id"] + for func in provider["api_prov_funcs"] if func["api_prov_func_role"] == "APF"] if apf_id not in list_apf_ids: current_app.logger.debug("This id not belongs to APF") - return unauthorized_error(detail ="You are not a publisher", cause ="This API is only available for publishers") + return unauthorized_error(detail="You are not a publisher", cause="This API is only available for publishers") return None @@ -47,7 +54,8 @@ class PublishServiceOperations(Resource): if result != None: return result - service = mycol.find({"apf_id": apf_id}, {"_id":0, "api_name":1, "api_id":1, "aef_profiles":1, "description":1, "supported_features":1, "shareable_info":1, "service_api_category":1, "api_supp_feats":1, "pub_api_path":1, "ccf_id":1}) + service = mycol.find({"apf_id": apf_id}, {"_id": 0, "api_name": 1, "api_id": 1, "aef_profiles": 1, "description": 1, + "supported_features": 1, "shareable_info": 1, "service_api_category": 1, "api_supp_feats": 1, "pub_api_path": 1, "ccf_id": 1}) current_app.logger.debug(service) if service is None: current_app.logger.error("Not found services for this apf id") @@ -82,9 +90,11 @@ class PublishServiceOperations(Resource): if result != None: return result - service = mycol.find_one({"api_name": serviceapidescription.api_name}) + service = mycol.find_one( + {"api_name": serviceapidescription.api_name}) if service is not None: - current_app.logger.error("Service already registered with same api name") + current_app.logger.error( + "Service already registered with same api name") return forbidden_error(detail="Already registered service with same api name", cause="Found service with same api name") api_id = secrets.token_hex(15) @@ -99,9 +109,15 @@ class PublishServiceOperations(Resource): self.auth_manager.add_auth_service(api_id, apf_id) current_app.logger.debug("Service inserted in database") + res = make_response(object=dict_to_camel_case(clean_empty(serviceapidescription.to_dict())), status=201) - res.headers['Location'] = "http://localhost:8080/published-apis/v1/" + str(apf_id) + "/service-apis/" + str(api_id) + res.headers['Location'] = "http://localhost:8080/published-apis/v1/" + \ + str(apf_id) + "/service-apis/" + str(api_id) + if res.status_code == 201: + current_app.logger.info("Service published") + RedisEvent("SERVICE_API_AVAILABLE", "apiIds", + [str(api_id)]).send_event() return res except Exception as e: @@ -109,26 +125,25 @@ class PublishServiceOperations(Resource): current_app.logger.error(exception + "::" + str(e)) return internal_server_error(detail=exception, cause=str(e)) - - def get_one_serviceapi(self, service_api_id, apf_id): mycol = self.db.get_col_by_name(self.db.service_api_descriptions) try: - current_app.logger.debug("Geting service api with id: " + service_api_id) + current_app.logger.debug( + "Geting service api with id: " + service_api_id) result = self.__check_apf(apf_id) if result != None: return result my_query = {'apf_id': apf_id, 'api_id': service_api_id} - service_api = mycol.find_one(my_query, {"_id":0, "api_name":1, "api_id":1, "aef_profiles":1, "description":1, "supported_features":1, "shareable_info":1, "service_api_category":1, "api_supp_feats":1, "pub_api_path":1, "ccf_id":1}) + service_api = mycol.find_one(my_query, {"_id": 0, "api_name": 1, "api_id": 1, "aef_profiles": 1, "description": 1, + "supported_features": 1, "shareable_info": 1, "service_api_category": 1, "api_supp_feats": 1, "pub_api_path": 1, "ccf_id": 1}) if service_api is None: current_app.logger.error(service_api_not_found_message) return not_found_error(detail=service_api_not_found_message, cause="No Service with specific credentials exists") - my_service_api = dict_to_camel_case(service_api) my_service_api = clean_empty(my_service_api) @@ -147,7 +162,8 @@ class PublishServiceOperations(Resource): try: - current_app.logger.debug("Removing api service with id: " + service_api_id) + current_app.logger.debug( + "Removing api service with id: " + service_api_id) result = self.__check_apf(apf_id) if result != None: @@ -165,22 +181,29 @@ class PublishServiceOperations(Resource): self.auth_manager.remove_auth_service(service_api_id, apf_id) current_app.logger.debug("Removed service from database") - out = "The service matching api_id " + service_api_id + " was deleted." - return make_response(out, status=204) + out = "The service matching api_id " + service_api_id + " was deleted." + res = make_response(out, status=204) + if res.status_code == 204: + current_app.logger.info("Removed service published") + RedisEvent("SERVICE_API_UNAVAILABLE", "apiIds", + [service_api_id]).send_event() + publisher_ops.publish_message( + "internal-messages", f"service-removed:{service_api_id}") + return res except Exception as e: exception = "An exception occurred in delete service" current_app.logger.error(exception + "::" + str(e)) return internal_server_error(detail=exception, cause=str(e)) - def update_serviceapidescription(self, service_api_id, apf_id, service_api_description): mycol = self.db.get_col_by_name(self.db.service_api_descriptions) try: - current_app.logger.debug("Updating service api with id: " + service_api_id) + current_app.logger.debug( + "Updating service api with id: " + service_api_id) result = self.__check_apf(apf_id) @@ -197,18 +220,22 @@ class PublishServiceOperations(Resource): service_api_description = service_api_description.to_dict() service_api_description = clean_empty(service_api_description) - result = mycol.find_one_and_update(serviceapidescription, {"$set":service_api_description}, projection={"_id":0, "api_name":1, "api_id":1, "aef_profiles":1, "description":1, "supported_features":1, "shareable_info":1, "service_api_category":1, "api_supp_feats":1, "pub_api_path":1, "ccf_id":1},return_document=ReturnDocument.AFTER ,upsert=False) + result = mycol.find_one_and_update(serviceapidescription, {"$set": service_api_description}, projection={"_id": 0, "api_name": 1, "api_id": 1, "aef_profiles": 1, "description": 1, + "supported_features": 1, "shareable_info": 1, "service_api_category": 1, "api_supp_feats": 1, "pub_api_path": 1, "ccf_id": 1}, return_document=ReturnDocument.AFTER, upsert=False) result = clean_empty(result) current_app.logger.debug("Updated service api") - - response = make_response(object=dict_to_camel_case(result), status=200) + service_api_description_updated = dict_to_camel_case(result) + response = make_response( + object=service_api_description_updated, status=200) + if response.status_code == 200: + RedisEvent("SERVICE_API_UPDATE", "serviceAPIDescriptions", [ + service_api_description_updated]).send_event() return response except Exception as e: exception = "An exception occurred in update service" current_app.logger.error(exception + "::" + str(e)) return internal_server_error(detail=exception, cause=str(e)) - diff --git a/services/TS29222_CAPIF_Routing_Info_API/Dockerfile b/services/TS29222_CAPIF_Routing_Info_API/Dockerfile index 9911d06b662dc1890945b8ec73c862a5ae5a2fab..cfa4b5dfe4888a1efa8fb9551f707f21c93dd73c 100644 --- a/services/TS29222_CAPIF_Routing_Info_API/Dockerfile +++ b/services/TS29222_CAPIF_Routing_Info_API/Dockerfile @@ -11,6 +11,6 @@ COPY . /usr/src/app EXPOSE 8080 -ENTRYPOINT ["python3"] +ENTRYPOINT ["gunicorn"] -CMD ["-m", "capif_routing_info"] \ No newline at end of file +CMD ["--bind", "0.0.0.0:8080", "--chdir", "/usr/src/app/capif_routing_info", "wsgi:app"] \ No newline at end of file diff --git a/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/__main__.py b/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/__main__.py deleted file mode 100644 index e595fa02106ed80d620b8eb00f1d2a421bb1ee5f..0000000000000000000000000000000000000000 --- a/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/__main__.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python3 - -import connexion - -from capif_routing_info import encoder - - -def main(): - app = connexion.App(__name__, specification_dir='./openapi/') - app.app.json_encoder = encoder.JSONEncoder - app.add_api('openapi.yaml', - arguments={'title': 'CAPIF_Routing_Info_API'}, - pythonic_params=True) - - app.run(port=8080) - - -if __name__ == '__main__': - main() diff --git a/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/app.py b/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/app.py new file mode 100644 index 0000000000000000000000000000000000000000..e669f46a4ab85ec541d4e0a6d2586c362468090d --- /dev/null +++ b/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/app.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +import connexion +import encoder + + +# def main(): +app = connexion.App(__name__, specification_dir='./openapi/') +app.app.json_encoder = encoder.JSONEncoder +app.add_api('openapi.yaml', + arguments={'title': 'CAPIF_Routing_Info_API'}, + pythonic_params=True) + +# app.run(port=8080) + + +# if __name__ == '__main__': +# main() diff --git a/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/controllers/default_controller.py b/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/controllers/default_controller.py index 7cd07dd74ff2634a9487083283cad4ef354c61ae..442fa809298424c273d7ec228855e9b3fa8adcf5 100644 --- a/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/controllers/default_controller.py +++ b/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/controllers/default_controller.py @@ -1,9 +1,9 @@ import connexion import six -from capif_routing_info.models.problem_details import ProblemDetails # noqa: E501 -from capif_routing_info.models.routing_info import RoutingInfo # noqa: E501 -from capif_routing_info import util +from ..models.problem_details import ProblemDetails # noqa: E501 +from ..models.routing_info import RoutingInfo # noqa: E501 +# from capif_routing_info import util def service_apis_service_api_id_get(service_api_id, aef_id, supp_feat=None): # noqa: E501 diff --git a/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/encoder.py b/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/encoder.py index b53647004a14457d83693f5909b69425d927d3c8..80bad8fa9220ab873e044b7adc0a849746088ad5 100644 --- a/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/encoder.py +++ b/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/encoder.py @@ -1,7 +1,7 @@ from connexion.apps.flask_app import FlaskJSONEncoder import six -from capif_routing_info.models.base_model_ import Model +from models.base_model_ import Model class JSONEncoder(FlaskJSONEncoder): diff --git a/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/util.py b/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/util.py index 227076550fea5acb4f1f167a777f366215563261..910388bab6fa518b467b13476102000d2b0a0321 100644 --- a/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/util.py +++ b/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/util.py @@ -2,7 +2,7 @@ import datetime import six import typing -from capif_routing_info import typing_utils +import typing_utils def _deserialize(data, klass): diff --git a/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/wsgi.py b/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/wsgi.py new file mode 100644 index 0000000000000000000000000000000000000000..6026b0fa96078634d3455ab93d71dcdc78774276 --- /dev/null +++ b/services/TS29222_CAPIF_Routing_Info_API/capif_routing_info/wsgi.py @@ -0,0 +1,4 @@ +from app import app + +if __name__ == "__main__": + app.run() diff --git a/services/TS29222_CAPIF_Routing_Info_API/requirements.txt b/services/TS29222_CAPIF_Routing_Info_API/requirements.txt index 1be0346f149043abdcafbd540c98c73eca75275e..e4be1ef6b91acd656f04127c542692abdb9b7b6b 100644 --- a/services/TS29222_CAPIF_Routing_Info_API/requirements.txt +++ b/services/TS29222_CAPIF_Routing_Info_API/requirements.txt @@ -3,3 +3,5 @@ swagger-ui-bundle >= 0.0.2 python_dateutil >= 2.6.0 setuptools >= 21.0.0 Flask == 2.0.3 +gunicorn==22.0.0 +packaging==24.0 \ No newline at end of file diff --git a/services/TS29222_CAPIF_Security_API/capif_security/controllers/default_controller.py b/services/TS29222_CAPIF_Security_API/capif_security/controllers/default_controller.py index 6713db133306fd689b329c380467f4f926bf8b08..7e04d4df353bd4227d5b7bda72e3cb7236cf1dab 100644 --- a/services/TS29222_CAPIF_Security_API/capif_security/controllers/default_controller.py +++ b/services/TS29222_CAPIF_Security_API/capif_security/controllers/default_controller.py @@ -111,10 +111,7 @@ def trusted_invokers_api_invoker_id_delete_post(api_invoker_id, body): # noqa: current_app.logger.info("Revoking permissions") res = service_security_ops.revoke_api_authorization(api_invoker_id, body) - if res.status_code == 204: - current_app.logger.info("Permissions revoked") - publish_ops.publish_message("events", "API_INVOKER_AUTHORIZATION_REVOKED") - + return res @cert_validation() diff --git a/services/TS29222_CAPIF_Security_API/capif_security/core/consumer_messager.py b/services/TS29222_CAPIF_Security_API/capif_security/core/consumer_messager.py index 749f57ed7914b5cb1954b9ee8ef259ee930a0a93..43445583236a5743ea1475079a3c329bb0788787 100644 --- a/services/TS29222_CAPIF_Security_API/capif_security/core/consumer_messager.py +++ b/services/TS29222_CAPIF_Security_API/capif_security/core/consumer_messager.py @@ -3,6 +3,7 @@ import redis from .internal_security_ops import InternalSecurityOps from flask import current_app + class Subscriber(): def __init__(self): @@ -16,12 +17,7 @@ class Subscriber(): for raw_message in self.p.listen(): if raw_message["type"] == "message" and raw_message["channel"].decode('utf-8') == "internal-messages": message, *ids = raw_message["data"].decode('utf-8').split(":") - if message == "invoker-removed" and len(ids)>0: + if message == "invoker-removed" and len(ids) > 0: self.security_ops.delete_intern_servicesecurity(ids[0]) if message == "provider-removed" or message == "service-removed" and len(ids) > 0: self.security_ops.update_intern_servicesecurity(ids[0]) - - - - - diff --git a/services/TS29222_CAPIF_Security_API/capif_security/core/redis_event.py b/services/TS29222_CAPIF_Security_API/capif_security/core/redis_event.py new file mode 100644 index 0000000000000000000000000000000000000000..aadbdbb6b3116cf288648de41effdb12b9ca9143 --- /dev/null +++ b/services/TS29222_CAPIF_Security_API/capif_security/core/redis_event.py @@ -0,0 +1,41 @@ +from ..encoder import JSONEncoder +from .publisher import Publisher +import json + +publisher_ops = Publisher() + + +class RedisEvent(): + def __init__(self, event, event_detail_key=None, information=None) -> None: + self.EVENTS_ENUM = [ + 'SERVICE_API_AVAILABLE', + 'SERVICE_API_UNAVAILABLE', + 'SERVICE_API_UPDATE', + 'API_INVOKER_ONBOARDED', + 'API_INVOKER_OFFBOARDED', + 'SERVICE_API_INVOCATION_SUCCESS', + 'SERVICE_API_INVOCATION_FAILURE', + 'ACCESS_CONTROL_POLICY_UPDATE', + 'ACCESS_CONTROL_POLICY_UNAVAILABLE', + 'API_INVOKER_AUTHORIZATION_REVOKED', + 'API_INVOKER_UPDATED', + 'API_TOPOLOGY_HIDING_CREATED', + 'API_TOPOLOGY_HIDING_REVOKED'] + if event not in self.EVENTS_ENUM: + raise Exception( + "Event (" + event + ") is not on event enum (" + ','.join(self.EVENTS_ENUM) + ")") + self.redis_event = { + "event": event + } + if event_detail_key != None and information != None: + self.redis_event['key'] = event_detail_key + self.redis_event['information'] = information + + def to_string(self): + return json.dumps(self.redis_event, cls=JSONEncoder) + + def send_event(self): + publisher_ops.publish_message("events", self.to_string()) + + def __call__(self): + return self.redis_event diff --git a/services/TS29222_CAPIF_Security_API/capif_security/core/servicesecurity.py b/services/TS29222_CAPIF_Security_API/capif_security/core/servicesecurity.py index f553b4d6112aa50af7563edf79018ed87b270664..9fe7838a47479a17b0233dd7698c3bb08958c670 100644 --- a/services/TS29222_CAPIF_Security_API/capif_security/core/servicesecurity.py +++ b/services/TS29222_CAPIF_Security_API/capif_security/core/servicesecurity.py @@ -7,14 +7,15 @@ from flask_jwt_extended import create_access_token from datetime import datetime, timedelta import json -from ..models.access_token_rsp import AccessTokenRsp -from ..models.access_token_claims import AccessTokenClaims from bson import json_util from ..core.publisher import Publisher from ..models.access_token_err import AccessTokenErr +from ..models.access_token_rsp import AccessTokenRsp +from ..models.access_token_claims import AccessTokenClaims from ..util import dict_to_camel_case, clean_empty from .responses import not_found_error, make_response, bad_request_error, internal_server_error, forbidden_error from .resources import Resource +from .redis_event import RedisEvent import os publish_ops = Publisher() @@ -22,13 +23,15 @@ publish_ops = Publisher() security_context_not_found_detail = "Security context not found" api_invoker_no_context_cause = "API Invoker has no security context" + class SecurityOperations(Resource): def __check_invoker(self, api_invoker_id): invokers_col = self.db.get_col_by_name(self.db.capif_invokers) - current_app.logger.debug("Checking api invoker with id: " + api_invoker_id) - invoker = invokers_col.find_one({"api_invoker_id": api_invoker_id}) + current_app.logger.debug( + "Checking api invoker with id: " + api_invoker_id) + invoker = invokers_col.find_one({"api_invoker_id": api_invoker_id}) if invoker is None: current_app.logger.error("Invoker not found") return not_found_error(detail="Invoker not found", cause="API Invoker not exists or invalid ID") @@ -44,12 +47,12 @@ class SecurityOperations(Resource): if header != "3gpp": current_app.logger.error("Bad format scope") token_error = AccessTokenErr(error="invalid_scope", error_description="The first characters must be '3gpp'") - # return make_response(object=dict_to_camel_case(clean_empty(token_error.to_dict())), status=400) return make_response(object=clean_empty(token_error.to_dict()), status=400) _, body = scope.split("#") - capif_service_col = self.db.get_col_by_name(self.db.capif_service_col) + capif_service_col = self.db.get_col_by_name( + self.db.capif_service_col) security_info = security_context["security_info"] aef_security_context = [info["aef_id"] for info in security_info] @@ -59,15 +62,17 @@ class SecurityOperations(Resource): if aef_id not in aef_security_context: current_app.logger.error("Bad format Scope, not valid aef id ") token_error = AccessTokenErr(error="invalid_scope", error_description="One of aef_id not belongs of your security context") - # return make_response(object=dict_to_camel_case(clean_empty(token_error.to_dict())), status=400) return make_response(object=clean_empty(token_error.to_dict()), status=400) + api_names = api_names.split(",") for api_name in api_names: - service = capif_service_col.find_one({"$and": [{"api_name":api_name},{self.filter_aef_id:aef_id}]}) + service = capif_service_col.find_one( + {"$and": [{"api_name": api_name}, {self.filter_aef_id: aef_id}]}) if service is None: current_app.logger.error("Bad format Scope, not valid api name") - token_error = AccessTokenErr(error="invalid_scope", error_description="One of the api names does not exist or is not associated with the aef id provided") - # return make_response(object=dict_to_camel_case(clean_empty(token_error.to_dict())), status=400) + token_error = AccessTokenErr( + error="invalid_scope", + error_description="One of the api names does not exist or is not associated with the aef id provided") return make_response(object=clean_empty(token_error.to_dict()), status=400) return None @@ -75,7 +80,6 @@ class SecurityOperations(Resource): except Exception as e: current_app.logger.error("Bad format Scope: " + e) token_error = AccessTokenErr(error="invalid_scope", error_description="malformed scope") - # return make_response(object=dict_to_camel_case(clean_empty(token_error.to_dict())), status=400) return make_response(object=clean_empty(token_error.to_dict()), status=400) def __init__(self): @@ -88,16 +92,18 @@ class SecurityOperations(Resource): try: - current_app.logger.debug("Obtainig security context with id: " + api_invoker_id) + current_app.logger.debug( + "Obtainig security context with id: " + api_invoker_id) result = self.__check_invoker(api_invoker_id) if result != None: return result else: - services_security_object = mycol.find_one({"api_invoker_id": api_invoker_id}, {"_id":0, "api_invoker_id":0}) + services_security_object = mycol.find_one({"api_invoker_id": api_invoker_id}, { + "_id": 0, "api_invoker_id": 0}) if services_security_object is None: current_app.logger.error("Not found security context") - return not_found_error(detail= security_context_not_found_detail, cause=api_invoker_no_context_cause) + return not_found_error(detail=security_context_not_found_detail, cause=api_invoker_no_context_cause) if not authentication_info: for security_info_obj in services_security_object['security_info']: @@ -106,12 +112,15 @@ class SecurityOperations(Resource): for security_info_obj in services_security_object['security_info']: del security_info_obj['authorization_info'] - properyly_json= json.dumps(services_security_object, default=json_util.default) - my_service_security = dict_to_camel_case(json.loads(properyly_json)) + properyly_json = json.dumps( + services_security_object, default=json_util.default) + my_service_security = dict_to_camel_case( + json.loads(properyly_json)) my_service_security = clean_empty(my_service_security) - current_app.logger.debug("Obtained security context from database") - + current_app.logger.debug( + "Obtained security context from database") + res = make_response(object=my_service_security, status=200) return res @@ -120,7 +129,6 @@ class SecurityOperations(Resource): current_app.logger.error(exception + "::" + str(e)) return internal_server_error(detail=exception, cause=str(e)) - def create_servicesecurity(self, api_invoker_id, service_security): mycol = self.db.get_col_by_name(self.db.security_info) @@ -134,43 +142,54 @@ class SecurityOperations(Resource): if rfc3987.match(service_security.notification_destination, rule="URI") is None: current_app.logger.error("Bad url format") - return bad_request_error(detail="Bad Param", cause = "Detected Bad format of param", invalid_params=[{"param": "notificationDestination", "reason": "Not valid URL format"}]) + return bad_request_error(detail="Bad Param", cause="Detected Bad format of param", invalid_params=[{"param": "notificationDestination", "reason": "Not valid URL format"}]) - services_security_object = mycol.find_one({"api_invoker_id": api_invoker_id}) + services_security_object = mycol.find_one( + {"api_invoker_id": api_invoker_id}) if services_security_object is not None: - current_app.logger.error("Already security context defined with same api invoker id") + current_app.logger.error( + "Already security context defined with same api invoker id") return forbidden_error(detail="Security method already defined", cause="Identical AEF Profile IDs") - for service_instance in service_security.security_info: if service_instance.interface_details is not None: security_methods = service_instance.interface_details.security_methods pref_security_methods = service_instance.pref_security_methods - valid_security_method = set(security_methods) & set(pref_security_methods) + valid_security_method = set( + security_methods) & set(pref_security_methods) else: - capif_service_col = self.db.get_col_by_name(self.db.capif_service_col) - services_security_object = capif_service_col.find_one({"api_id":service_instance.api_id, self.filter_aef_id: service_instance.aef_id}, {"aef_profiles.security_methods.$":1}) + capif_service_col = self.db.get_col_by_name( + self.db.capif_service_col) + services_security_object = capif_service_col.find_one( + {"api_id": service_instance.api_id, self.filter_aef_id: service_instance.aef_id}, {"aef_profiles.security_methods.$": 1}) if services_security_object is None: - current_app.logger.error("Not found service with this aef id: " + service_instance.aef_id) + current_app.logger.error( + "Not found service with this aef id: " + service_instance.aef_id) return not_found_error(detail="Service with this aefId not found", cause="Not found Service") pref_security_methods = service_instance.pref_security_methods - valid_security_methods = [security_method for array_methods in services_security_object["aef_profiles"] for security_method in array_methods["security_methods"]] - valid_security_method = set(valid_security_methods) & set(pref_security_methods) + valid_security_methods = [security_method for array_methods in services_security_object["aef_profiles"] + for security_method in array_methods["security_methods"]] + valid_security_method = set( + valid_security_methods) & set(pref_security_methods) if len(list(valid_security_method)) == 0: - current_app.logger.error("Not found comptaible security method with pref security method") + current_app.logger.error( + "Not found comptaible security method with pref security method") return bad_request_error(detail="Not found compatible security method with pref security method", cause="Error pref security method", invalid_params=[{"param": "prefSecurityMethods", "reason": "pref security method not compatible with security method available"}]) - service_instance.sel_security_method = list(valid_security_method)[0] + service_instance.sel_security_method = list( + valid_security_method)[0] # Send service instance to ACL current_app.logger.debug("Sending message to create ACL") - publish_ops.publish_message("acls-messages", "create-acl:"+str(api_invoker_id)+":"+str(service_instance.api_id)+":"+str(service_instance.aef_id)) - current_app.logger.debug("Inserted security context in database") + publish_ops.publish_message("acls-messages", "create-acl:"+str( + api_invoker_id)+":"+str(service_instance.api_id)+":"+str(service_instance.aef_id)) + current_app.logger.debug( + "Inserted security context in database") rec = dict() rec['api_invoker_id'] = api_invoker_id @@ -178,7 +197,9 @@ class SecurityOperations(Resource): mycol.insert_one(rec) res = make_response(object=dict_to_camel_case(clean_empty(service_security.to_dict())), status=201) - res.headers['Location'] = "https://{}/capif-security/v1/trustedInvokers/{}".format(os.getenv('CAPIF_HOSTNAME'),str(api_invoker_id)) + res.headers['Location'] = "https://{}/capif-security/v1/trustedInvokers/{}".format( + os.getenv('CAPIF_HOSTNAME'), str(api_invoker_id)) + return res except Exception as e: @@ -186,7 +207,6 @@ class SecurityOperations(Resource): current_app.logger.error(exception + "::" + str(e)) return internal_server_error(detail=exception, cause=str(e)) - def delete_servicesecurity(self, api_invoker_id): mycol = self.db.get_col_by_name(self.db.security_info) @@ -205,19 +225,22 @@ class SecurityOperations(Resource): if services_security_count == 0: current_app.logger.error(security_context_not_found_detail) return not_found_error(detail=security_context_not_found_detail, cause=api_invoker_no_context_cause) - + mycol.delete_many(my_query) - publish_ops.publish_message("acls-messages", "remove-acl:"+api_invoker_id) + publish_ops.publish_message( + "acls-messages", "remove-acl:"+api_invoker_id) - current_app.logger.debug("Removed security context from database") - out= "The security info of Netapp with Netapp ID " + api_invoker_id + " were deleted.", 204 + current_app.logger.debug( + "Removed security context from database") + out = "The security info of Netapp with Netapp ID " + \ + api_invoker_id + " were deleted.", 204 return make_response(out, status=204) except Exception as e: exception = "An exception occurred in create security info" current_app.logger.error(exception + "::" + str(e)) - return internal_server_error(detail=exception, cause = str(e)) + return internal_server_error(detail=exception, cause=str(e)) def delete_intern_servicesecurity(self, api_invoker_id): @@ -235,18 +258,19 @@ class SecurityOperations(Resource): invokers_col = self.db.get_col_by_name(self.db.capif_invokers) - current_app.logger.debug("Checking api invoker with id: " + access_token_req["client_id"]) - invoker = invokers_col.find_one({"api_invoker_id": access_token_req["client_id"]}) + current_app.logger.debug( + "Checking api invoker with id: " + access_token_req["client_id"]) + invoker = invokers_col.find_one( + {"api_invoker_id": access_token_req["client_id"]}) if invoker is None: client_id_error = AccessTokenErr(error="invalid_client", error_description="Client Id not found") - # return make_response(object=dict_to_camel_case(clean_empty(client_id_error.to_dict())), status=400) return make_response(object=clean_empty(client_id_error.to_dict()), status=400) if access_token_req["grant_type"] != "client_credentials": - client_id_error = AccessTokenErr(error="unsupported_grant_type", error_description="Invalid value for `grant_type` ({0}), must be one of ['client_credentials'] - 'grant_type'" - .format(access_token_req["grant_type"])) - # return make_response(object=dict_to_camel_case(clean_empty(client_id_error.to_dict())), status=400) + client_id_error = AccessTokenErr(error="unsupported_grant_type", + error_description="Invalid value for `grant_type` ({0}), must be one of ['client_credentials'] - 'grant_type'" + .format(access_token_req["grant_type"])) return make_response(object=clean_empty(client_id_error.to_dict()), status=400) service_security = mycol.find_one({"api_invoker_id": security_id}) @@ -254,17 +278,21 @@ class SecurityOperations(Resource): current_app.logger.error("Not found security context with id: " + security_id) return not_found_error(detail= security_context_not_found_detail, cause=api_invoker_no_context_cause) - result = self.__check_scope(access_token_req["scope"], service_security) + result = self.__check_scope( + access_token_req["scope"], service_security) if result != None: return result expire_time = timedelta(minutes=10) - now=datetime.now() + now = datetime.now() - claims = AccessTokenClaims(iss = access_token_req["client_id"], scope=access_token_req["scope"], exp=int((now+expire_time).timestamp())) - access_token = create_access_token(identity = access_token_req["client_id"] , additional_claims=claims.to_dict()) - access_token_resp = AccessTokenRsp(access_token=access_token, token_type="Bearer", expires_in=int(expire_time.total_seconds()), scope=access_token_req["scope"]) + claims = AccessTokenClaims(iss=access_token_req["client_id"], scope=access_token_req["scope"], exp=int( + (now+expire_time).timestamp())) + access_token = create_access_token( + identity=access_token_req["client_id"], additional_claims=claims.to_dict()) + access_token_resp = AccessTokenRsp(access_token=access_token, token_type="Bearer", expires_in=int( + expire_time.total_seconds()), scope=access_token_req["scope"]) current_app.logger.debug("Created access token") @@ -276,7 +304,6 @@ class SecurityOperations(Resource): current_app.logger.error(exception + "::" + str(e)) return internal_server_error(detail=exception, cause=str(e)) - def update_servicesecurity(self, api_invoker_id, service_security): mycol = self.db.get_col_by_name(self.db.security_info) try: @@ -289,32 +316,42 @@ class SecurityOperations(Resource): old_object = mycol.find_one({"api_invoker_id": api_invoker_id}) if old_object is None: - current_app.logger.error("Service api not found with id: " + api_invoker_id) + current_app.logger.error( + "Service api not found with id: " + api_invoker_id) return not_found_error(detail="Service API not existing", cause="Not exist securiy information for this invoker") for service_instance in service_security.security_info: if service_instance.interface_details is not None: security_methods = service_instance.interface_details.security_methods pref_security_methods = service_instance.pref_security_methods - valid_security_method = set(security_methods) & set(pref_security_methods) - service_instance.sel_security_method = list(valid_security_method)[0] + valid_security_method = set( + security_methods) & set(pref_security_methods) + service_instance.sel_security_method = list( + valid_security_method)[0] else: - capif_service_col = self.db.get_col_by_name(self.db.capif_service_col) - services_security_object = capif_service_col.find_one({self.filter_aef_id: service_instance.aef_id}, {"aef_profiles.security_methods.$":1}) + capif_service_col = self.db.get_col_by_name( + self.db.capif_service_col) + services_security_object = capif_service_col.find_one( + {self.filter_aef_id: service_instance.aef_id}, {"aef_profiles.security_methods.$": 1}) if services_security_object is None: - current_app.logger.error("Service api with this aefId not found: " + service_instance.aef_id) + current_app.logger.error( + "Service api with this aefId not found: " + service_instance.aef_id) return not_found_error(detail="Service with this aefId not found", cause="Not found Service") pref_security_methods = service_instance.pref_security_methods - valid_security_methods = [security_method for array_methods in services_security_object["aef_profiles"] for security_method in array_methods["security_methods"]] - valid_security_method = set(valid_security_methods) & set(pref_security_methods) - service_instance.sel_security_method = list(valid_security_method)[0] + valid_security_methods = [security_method for array_methods in services_security_object["aef_profiles"] + for security_method in array_methods["security_methods"]] + valid_security_method = set( + valid_security_methods) & set(pref_security_methods) + service_instance.sel_security_method = list( + valid_security_method)[0] service_security = service_security.to_dict() service_security = clean_empty(service_security) - result = mycol.find_one_and_update(old_object, {"$set":service_security}, projection={'_id': 0, "api_invoker_id":0},return_document=ReturnDocument.AFTER ,upsert=False) + result = mycol.find_one_and_update(old_object, {"$set": service_security}, projection={ + '_id': 0, "api_invoker_id": 0}, return_document=ReturnDocument.AFTER, upsert=False) # result = clean_empty(result) @@ -329,7 +366,6 @@ class SecurityOperations(Resource): current_app.logger.error(exception + "::" + str(e)) return internal_server_error(detail=exception, cause=str(e)) - def revoke_api_authorization(self, api_invoker_id, security_notification): mycol = self.db.get_col_by_name(self.db.security_info) @@ -350,10 +386,12 @@ class SecurityOperations(Resource): updated_security_context = services_security_context.copy() for context in services_security_context["security_info"]: - index = services_security_context["security_info"].index(context) + index = services_security_context["security_info"].index( + context) if security_notification.aef_id == context["aef_id"] or context["api_id"] in security_notification.api_ids: current_app.logger.debug("Sending message.") - publish_ops.publish_message("acls-messages", "remove-acl:"+str(api_invoker_id)+":"+str(context["api_id"])+":"+str(security_notification.aef_id)) + publish_ops.publish_message("acls-messages", "remove-acl:"+str( + api_invoker_id)+":"+str(context["api_id"])+":"+str(security_notification.aef_id)) current_app.logger.debug("message sended.") updated_security_context["security_info"].pop(index) @@ -362,13 +400,16 @@ class SecurityOperations(Resource): if len(updated_security_context["security_info"]) == 0: mycol.delete_many(my_query) - #self.notification.send_notification(services_security_context["notification_destination"], security_notification) - current_app.logger.debug("Revoked security context") - out= "Netapp with ID " + api_invoker_id + " was revoked by some APIs.", 204 - return make_response(out, status=204) + out = "Netapp with ID " + api_invoker_id + " was revoked by some APIs.", 204 + res = make_response(out, status=204) + if res.status_code == 204: + current_app.logger.info("Permissions revoked") + RedisEvent("API_INVOKER_AUTHORIZATION_REVOKED").send_event() + + return res except Exception as e: exception = "An exception occurred in revoke security auth" current_app.logger.error(exception + "::" + str(e)) - return internal_server_error(detail=exception, cause=str(e)) \ No newline at end of file + return internal_server_error(detail=exception, cause=str(e)) diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/Dockerfile b/services/capif-client/CAPIFInvokerGUI/invoker_gui/Dockerfile deleted file mode 100644 index 8357bf96b3ffb21ac8b942395a6df5b54b708ccb..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM python:3.8 -ENV PYTHONUNBUFFERED 1 - -RUN apt-get update && apt-get install -y jq && apt-get clean -RUN apt-get install -y iputils-ping - -RUN mkdir -p /usr/src/app -WORKDIR /usr/src/app -ADD requirements.txt /usr/src/app/ -RUN pip install -r requirements.txt -ADD . /usr/src/app/ - -CMD ["sh", "prepare.sh"] \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/capif_registration.json b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/capif_registration.json deleted file mode 100644 index 498f2844f3b1090f489960a6469bdf6d5fa6064f..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/capif_registration.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "folder_to_store_certificates": "/usr/src/app/capif_onboarding", - "capif_host": "capifcore", - "capif_http_port": "8080", - "capif_https_port": "443", - "capif_netapp_username": "test_netapp_23", - "capif_netapp_password": "test_netapp_password", - "capif_callback_url": "http://192.168.1.11:5000", - "description": ",test_app_description", - "csr_common_name": "test_app_common_name", - "csr_organizational_unit": "test_app_ou", - "csr_organization": "test_app_o", - "crs_locality": "Madrid", - "csr_state_or_province_name": "Madrid", - "csr_country_name": "ES", - "csr_email_address": "test@example.com" -} \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/credentials.properties b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/credentials.properties deleted file mode 100755 index 0c39e2c313412f75ba0a252e17da79ef884cf5be..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/credentials.properties +++ /dev/null @@ -1,14 +0,0 @@ -[credentials] -invoker_username = customnetapp -invoker_password = pass123 -invoker_role = invoker -invoker_description = Dummy NetApp -invoker_cn = invoker -#capif_ip = capicore -#capif_port = 8080 -capif_callback_ip = host.docker.internal -capif_callback_port = 8086 -nef_ip = host.docker.internal -nef_port = 8888 -nef_callback_ip = host.docker.internal -nef_callback_port = 8085 \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/demo_values.json b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/demo_values.json deleted file mode 100644 index 3a8a2ab4fa101b15b95b45565765dbd00a8a5aab..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/demo_values.json +++ /dev/null @@ -1 +0,0 @@ -{"netappID": "93caff2e486955", "ccf_onboarding_url": "api-invoker-management/v1/onboardedInvokers", "ccf_discover_url": "service-apis/v1/allServiceAPIs?api-invoker-id=", "capif_access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY4OTU4NzA1OCwianRpIjoiMDViOGMwYWMtYmU4OC00OWViLWFhNWItZTA0ZDM4NWJlYWFkIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImN1c3RvbW5ldGFwcCBpbnZva2VyIiwibmJmIjoxNjg5NTg3MDU4LCJleHAiOjE2ODk1ODc5NTh9.k8ZXlgS0CJS-aDJCHgUv0oA4B6CLBjYpp5z3qIrzsvgr20wflpKXiO03c6U3G87T33ocEPR6BWG-ZhpQ1bfml2CKU16gef4nIDIgOKh17yBF0M1eW-gULBZL9exJQIpDWJXQK9oZOrkyHjgN89ieXlVYW9hKaGQfRl_B_HZL0hllWq6E9uE7kHG-VJTEmLJTEyP6uqmfIPLz2znHeTk8eP7IB_vxeIh-7Fr6LcyziDoxMskPDqxzg_6oLyd7biH9qZyWQYvtrEPsh_kJdK5Yc7vK1Kuh01uY9JRk9E97sXU8x0yBjejHgDn9K7_kH1gugPo7eP6nEPvm3BROWHFUmw", "api_id_0": "1d3c0c7803e650264ba30963b96549", "api_name_0": "/nef/api/v1/3gpp-as-session-with-qos/", "aef_id_0": "8cadd2bf35c6adf24bda897b4e8e99", "demo_ipv4_addr_0": "3gppnef", "demo_port_0": 8090, "demo_url_0": "/nef/api/v1/3gpp-as-session-with-qos/v1/{scsAsId}/subscriptions", "netapp_service_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY4OTU4ODEwOCwianRpIjoiY2NiNDNkNzktNTRjYS00NDI4LTg1OGMtN2MyNDI1MzhiZjU5IiwidHlwZSI6ImFjY2VzcyIsInN1YiI6IjIwZjAxMTY0OTRkNTA0MjhmZTI2ZDdhNjNkYjNlNSIsIm5iZiI6MTY4OTU4ODEwOCwiZXhwIjoxNjg5NTg4NzA4LCJpc3MiOiIyMGYwMTE2NDk0ZDUwNDI4ZmUyNmQ3YTYzZGIzZTUiLCJzY29wZSI6IjNncHAjOGNhZGQyYmYzNWM2YWRmMjRiZGE4OTdiNGU4ZTk5Oi9uZWYvYXBpL3YxLzNncHAtYXMtc2Vzc2lvbi13aXRoLXFvcy8ifQ.PTdAWSGCqEdxroxd1qF_cNA_JRohvNlgw_A49CyaUVqEKrky2_LnVtRll_KOHPGgGcIa8g_vqdM6We71CGx7w_KQPuSccb_wUkPQ5U2kfDT2-dkBZX8le_M1aQ9346tl3iQHqPsoMv3KdiD6mNSbO8f7vlRbQ1o7HQLtLaULB_0xbFr1iJAWdwO6Dm0KOKP_rM6kC5gKyVaLzUPUQBHGQwncQWlKp1Cey3G2cW5Aw_O6kF8mt1R1wgNCedU77JUmW3-ptc1kWmWlSo3UypYNm-XRAMWh44yYnGok5gE1tf451cRc5s9Hfl6Ya2fYYBI1by9x1S_zGlxi6f_OV9z4zg", "api_id_1": "cbc42102826d69a35de147c790a983", "api_name_1": "/nef/api/v1/3gpp-as-session-with-qos/", "aef_id_1": "1f1883a784c43f47aa4fae1dc4821f", "demo_ipv4_addr_1": "127.0.0.1", "demo_port_1": 8090, "demo_url_1": "/{scsAsId}/subscriptions", "demo_resource_id": ""} \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/events.json b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/events.json deleted file mode 100644 index 3d109204f59cee2b45773ea6d6844a15762a86d8..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/events.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "events": [ - "SERVICE_API_AVAILABLE" - ], - "notificationDestination": "http://192.168.1.11:8080/capifcallbacks" - } \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/invoker_details.json b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/invoker_details.json deleted file mode 100755 index d94aa7fe5ac73c53d7b55664f0b9e36a4a1c8c24..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/invoker_details.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "notificationDestination" : "http://X:Y/netapp_callback", - "supportedFeatures" : "fffffff", - "apiInvokerInformation" : "dummy", - "websockNotifConfig" : { - "requestWebsocketUri" : true, - "websocketUri" : "websocketUri" - }, - "onboardingInformation" : { - "apiInvokerPublicKey" : "" - }, - "requestTestNotification" : true -} \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/security_info.json b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/security_info.json deleted file mode 100755 index c1b08672b8c08e0c8775b6c8a689fbba48fb51c1..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/security_info.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "securityInfo": [ - { - "prefSecurityMethods": [ - "OAUTH" - ], - "authenticationInfo": "string", - "authorizationInfo": "string" - } - ], - "notificationDestination": "https://mynotificationdest.com", - "requestTestNotification": true, - "websockNotifConfig": { - "websocketUri": "string", - "requestWebsocketUri": true - }, - "supportedFeatures": "fff" - } \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/service_request_body.json b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/service_request_body.json deleted file mode 100644 index 189c49e4d67d9bd1bab5790f7bc6fa50c78d4169..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/service_request_body.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "ipv4Addr": "10.0.0.3", - "notificationDestination": "http://invoker_gui:9091/nefcallbacks", - "snssai": { - "sst": 1, - "sd": "000001" - }, -"dnn": "province1.mnc01.mcc202.gprs", -"qosReference": 82, - "altQoSReferences": [ -0 - ], -"usageThreshold": { -"duration": 0, - "totalVolume": 0, -"downlinkVolume": 0, - "uplinkVolume": 0 - }, -"qosMonInfo": { -"reqQosMonParams": [ -"DOWNLINK" -], -"repFreqs": [ -"EVENT_TRIGGERED" -], - "latThreshDl": 0, -"latThreshUl": 0, - "latThreshRp": 0, -"waitTime": 0, - "repPeriod": 0 -} -} \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/token_request.json b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/token_request.json deleted file mode 100755 index 5408b745186bf0d0bd3f92a43229eef846c989c1..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/config_files/token_request.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "grant_type": "client_credentials", - "client_id": "", - "client_secret": "string", - "scope": "" -} \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_delete.py b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_delete.py deleted file mode 100644 index 297f88e559f8724a4a77ac5aa370bdb567e9c5ad..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_delete.py +++ /dev/null @@ -1,71 +0,0 @@ -from dis import dis -import requests -import json -import configparser -import os -from termcolor import colored - - -class RemoveInvoker(): - - - def __offboard_netapp_to_capif(self, capif_ip, invoker_id, log_level): - - print(colored("Removing netapp from CAPIF","yellow")) - url = 'https://{}/api-invoker-management/v1/onboardedInvokers/{}'.format(capif_ip, invoker_id) - - headers = { - 'Content-Type': 'application/json' - } - - try: - - if log_level == "debug": - print(colored("''''''''''REQUEST'''''''''''''''''","blue")) - print(colored(f"Request: to {url}","blue")) - print(colored(f"Request Headers: {headers}", "blue")) - print(colored(f"''''''''''REQUEST'''''''''''''''''", "blue")) - - response = requests.request("DELETE", url, headers=headers, cert=( - 'capif_ops/certs/dummy.crt', 'capif_ops/certs/invoker_private_key.key'), verify='capif_ops/certs/ca.crt') - response.raise_for_status() - - if log_level == "debug": - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - print(colored(f"Response to: {response.url}","green")) - print(colored(f"Response Headers: {response.headers}","green")) - print(colored(f"Response: {response.json()}","green")) - print(colored(f"Response Status code: {response.status_code}","green")) - print(colored("Success onboard invoker","green")) - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - - except requests.exceptions.HTTPError as err: - raise Exception(err.response.text, err.response.status_code) - - - - def execute_remove_invoker(self, log_level): - - - capif_ip = os.getenv('CAPIF_HOSTNAME') - - with open('capif_ops/config_files/demo_values.json', 'r') as demo_file: - demo_values = json.load(demo_file) - - try: - - self.__offboard_netapp_to_capif(capif_ip, demo_values["invokerID"], log_level) - - print("ApiInvokerID: {}\n".format(demo_values["invokerID"])) - demo_values.pop("invokerID") - demo_values.pop("pub_key") - with open('capif_ops/config_files/demo_values.json', 'w') as outfile: - json.dump(demo_values, outfile) - - except Exception as e: - status_code = e.args[0] - if status_code == 403: - print("Invoker already registered.") - print("Chanage invoker public key in invoker_details.json\n") - else: - print(e) diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_discover_service.py b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_discover_service.py deleted file mode 100644 index 7b608ac6fd23ba097dd4b7ae73661f4ab0e300dd..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_discover_service.py +++ /dev/null @@ -1,96 +0,0 @@ -from dis import dis -import requests -import json -import configparser -import redis -import os -from termcolor import colored - - -class DiscoverService(): - - def __discover_service_apis(self, capif_ip, api_invoker_id, jwt_token, ccf_url, log_level): - - print(colored("Discover Service", "yellow")) - url = "https://{}/{}{}".format(capif_ip, ccf_url, api_invoker_id) - - payload = {} - files = {} - headers = { - 'Content-Type': 'application/json' - } - - try: - - if log_level == "debug": - print(colored("''''''''''REQUEST'''''''''''''''''", "blue")) - print(colored(f"Request: to {url}", "blue")) - print(colored(f"Request Headers: {headers}", "blue")) - print(colored(f"''''''''''REQUEST'''''''''''''''''", "blue")) - - response = requests.request("GET", url, headers=headers, data=payload, files=files, cert=( - 'capif_ops/certs/dummy.crt', 'capif_ops/certs/invoker_private_key.key'), verify='capif_ops/certs/ca.crt') - response.raise_for_status() - response_payload = json.loads(response.text) - - if log_level == "debug": - print(colored("''''''''''RESPONSE'''''''''''''''''", "green")) - print(colored(f"Response to: {response.url}", "green")) - print(colored(f"Response Headers: {response.headers}", "green")) - print(colored(f"Response: {response.json()}", "green")) - print( - colored(f"Response Status code: {response.status_code}", "green")) - print(colored("''''''''''RESPONSE'''''''''''''''''", "green")) - - return response_payload - except requests.exceptions.HTTPError as err: - print(err.response.text) - message = json.loads(err.response.text) - status = err.response.status_code - raise Exception(message, status) - - def execute_discover_service(self, log_level): - - - with open('capif_ops/config_files/demo_values.json', 'r') as demo_file: - demo_values = json.load(demo_file) - - capif_ip = os.getenv('CAPIF_HOSTNAME') - - try: - if 'invokerID' in demo_values: - invokerID = demo_values['invokerID'] - capif_access_token = demo_values['capif_access_token'] - ccf_discover_url = demo_values['ccf_discover_url'] - discovered_apis = self.__discover_service_apis( - capif_ip, invokerID, capif_access_token, ccf_discover_url, log_level) - print(colored(json.dumps(discovered_apis, indent=2), "yellow")) - - count = 0 - api_list = discovered_apis["serviceAPIDescriptions"] - for api in api_list: - getAEF_profiles = api["aefProfiles"][0] - getAEF_interfaces = getAEF_profiles["interfaceDescriptions"][0] - getAEF_versions = getAEF_profiles["versions"][0] - getAEF_resources = getAEF_versions["resources"][0] - demo_values[f'api_id_{count}'] = api["apiId"] - demo_values[f'api_name_{count}'] = api["apiName"] - demo_values[f'aef_id_{count}'] = getAEF_profiles["aefId"] - demo_values[f'demo_ipv4_addr_{count}'] = getAEF_interfaces["ipv4Addr"] - demo_values[f'demo_port_{count}'] = getAEF_interfaces["port"] - demo_values[f'demo_url_{count}'] = api["apiName"] + getAEF_versions["apiVersion"]+ getAEF_resources['uri'] - count += 1 - - print(colored("Discovered APIs", "yellow")) - with open('capif_ops/config_files/demo_values.json', 'w') as outfile: - json.dump(demo_values, outfile) - - except Exception as e: - status_code = e.args[0] - if status_code == 401: - print("API Invoker is not authorized") - elif status_code == 403: - print("API Invoker does not exist. API Invoker id not found") - else: - print(e) - diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_get_auth.py b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_get_auth.py deleted file mode 100644 index dde36942bb6b493791a3142ccae1e6a891d8d1c4..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_get_auth.py +++ /dev/null @@ -1,71 +0,0 @@ -import requests -import json -import configparser -import os -from termcolor import colored - -class PreviousAuth(): - - def __get_capif_auth(self, capif_ip, capif_port, username, password): - - #print("Geting Auth to exposer") - #url = "http://{}:{}/getauth".format(capif_ip, capif_port) - url = "https://register:8084/getauth".format(capif_port) - #url = "http://{}:{}/getauth".format(capif_ip, capif_port) - - payload = dict() - payload['username'] = username - payload['password'] = password - - headers = { - 'Content-Type': 'application/json' - } - - try: - response = requests.request("POST", url, headers=headers, data=json.dumps(payload), verify=False) - - response.raise_for_status() - response_payload = json.loads(response.text) - - return response_payload['access_token'] - - except requests.exceptions.HTTPError as err: - raise Exception(err.response.text, err.response.status_code) - - - def execute_get_auth(self, log_level): - - config = configparser.ConfigParser() - config.read('capif_ops/config_files/credentials.properties') - - username = config.get("credentials", "invoker_username") - password = config.get("credentials", "invoker_password") - - capif_ip = os.getenv('CAPIF_HOSTNAME') - capif_port = os.getenv('CAPIF_PORT') - - if os.path.exists("capif_ops/config_files/demo_values.json"): - #os.remove("capif_ops/config_files/demo_values.json") - with open('capif_ops/config_files/demo_values.json', 'r') as demo_file: - demo_values = json.load(demo_file) - else: - demo_values = {} - - #First we need register exposer in CAPIF - try: - if 'netappID' in demo_values: - access_token = self.__get_capif_auth(capif_ip, capif_port, username, password) - demo_values['capif_access_token'] = access_token - - with open('capif_ops/config_files/demo_values.json', 'w') as outfile: - json.dump(demo_values, outfile) - - print("Invoker auth Success!") - except Exception as e: - status_code = e.args[0] - if status_code == 409: - print("User already registed. Continue with token request\n") - else: - print(e) - - return True diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_get_security_auth.py b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_get_security_auth.py deleted file mode 100644 index 74b026fa9e4e5859b4143ec21e91169d2619dad7..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_get_security_auth.py +++ /dev/null @@ -1,90 +0,0 @@ -from dis import dis -from email import charset -import requests -import json -import configparser -import redis -import os -from termcolor import colored - - -class InvokerGetSecurityAuth(): - - def __get_security_token(self, capif_ip, api_invoker_id, jwt_token, ccf_url, aef_id, api_name, log_level): - - - url = "https://{}/capif-security/v1/securities/{}/token".format(capif_ip, api_invoker_id) - - with open('capif_ops/config_files/token_request.json', "rb") as f: - payload = json.load(f) - - payload["client_id"] = api_invoker_id - payload["scope"] = "3gpp#"+aef_id+":"+api_name - - headers = { - 'Content-Type': 'application/x-www-form-urlencoded', - } - - - payload_dict = json.dumps(payload, indent=2) - - print(colored(f"Request Body: {payload_dict}", "yellow")) - - try: - - if log_level == "debug": - print(colored("''''''''''REQUEST'''''''''''''''''","blue")) - print(colored(f"Request: to {url}","blue")) - print(colored(f"Request Headers: {headers}", "blue")) - print(colored(f"''''''''''REQUEST'''''''''''''''''", "blue")) - - response = requests.post(url, headers=headers, data=payload, cert=('capif_ops/certs/dummy.crt', 'capif_ops/certs/invoker_private_key.key'), verify='capif_ops/certs/ca.crt') - print(response.request.body) - response.raise_for_status() - response_payload = json.loads(response.text) - - if log_level == "debug": - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - print(colored(f"Response to: {response.url}","green")) - print(colored(f"Response Headers: {response.headers}","green")) - print(colored(f"Response: {response.json()}","green")) - print(colored(f"Response Status code: {response.status_code}","green")) - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - - return response_payload - except requests.exceptions.HTTPError as err: - print(err.response.text) - message = json.loads(err.response.text) - status = err.response.status_code - raise Exception(message, status) - - def execute_get_security_auth(self, log_level): - - with open('capif_ops/config_files/demo_values.json', 'r') as demo_file: - demo_values = json.load(demo_file) - - config = configparser.ConfigParser() - config.read('credentials.properties') - - capif_ip = os.getenv('CAPIF_HOSTNAME') - invokerID = demo_values['invokerID'] - capif_access_token = demo_values['capif_access_token'] - ccf_discover_url = demo_values['ccf_discover_url'] - - try: - if 'aef_id_0' in demo_values and 'api_name_0' in demo_values: - token = self.__get_security_token(capif_ip, invokerID, capif_access_token, ccf_discover_url, demo_values['aef_id_0'], demo_values['api_name_0'],log_level) - print(colored(json.dumps(token, indent=2),"yellow")) - demo_values["netapp_service_token"] = token["access_token"] - print(colored("Obtained Security Token","yellow")) - - with open('capif_ops/config_files/demo_values.json', 'w') as outfile: - json.dump(demo_values, outfile) - except Exception as e: - status_code = e.args[0] - if status_code == 401: - print("API Invoker is not authorized") - elif status_code == 403: - print("API Invoker does not exist. API Invoker id not found") - else: - print(e) diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_previous_register.py b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_previous_register.py deleted file mode 100644 index 6a772648c1c862e6a3401941138bc91299d7ae41..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_previous_register.py +++ /dev/null @@ -1,134 +0,0 @@ -import requests -import json -import configparser -import os -from termcolor import colored - -class PreviousRegister(): - - def __register_invoker_to_capif(self, capif_ip, capif_port, username, password, role, description, cn): - - #print(colored("Registering exposer to CAPIF","yellow")) - url = "https://register:8084/register".format(capif_port) - #url = "http://{}:{}/register".format(capif_ip, capif_port) - - payload = dict() - payload['username'] = username - payload['password'] = password - payload['role'] = role - payload['description'] = description - payload['cn'] = cn - - headers = { - 'Content-Type': 'application/json' - } - - try: - # print(colored("''''''''''REQUEST'''''''''''''''''","blue")) - # print(colored(f"Request: to {url}","blue")) - # print(colored(f"Request Headers: {headers}", "blue")) - # print(colored(f"Request Body: {json.dumps(payload)}", "blue")) - # print(colored(f"''''''''''REQUEST'''''''''''''''''", "blue")) - - response = requests.request("POST", url, headers=headers, data=json.dumps(payload), verify=False) - response.raise_for_status() - response_payload = json.loads(response.text) - - # print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - # print(colored(f"Response to: {response.url}","green")) - # print(colored(f"Response Headers: {response.headers}","green")) - # print(colored(f"Response: {response.json()}","green")) - # print(colored(f"Response Status code: {response.status_code}","green")) - # print(colored("Success to register new exposer","green")) - # print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - return response_payload['id'], response_payload['ccf_onboarding_url'], response_payload['ccf_discover_url'], - except requests.exceptions.HTTPError as err: - raise Exception(err.response.status_code) - - - def __get_capif_auth(self, capif_ip, capif_port, username, password): - - #print("Geting Auth to exposer") - url = "https://register:8084/getauth".format(capif_port) - #url = "http://{}:{}/getauth".format(capif_ip, capif_port) - - payload = dict() - payload['username'] = username - payload['password'] = password - - headers = { - 'Content-Type': 'application/json' - } - - try: - # print("''''''''''REQUEST'''''''''''''''''") - # print("Request: to ",url) - # print("Request Headers: ", headers) - # print("Request Body: ", json.dumps(payload)) - # print("''''''''''REQUEST'''''''''''''''''") - - response = requests.request("POST", url, headers=headers, data=json.dumps(payload), verify = False) - - response.raise_for_status() - response_payload = json.loads(response.text) - - # print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - # print(colored(f"Response to: {response.url}","green")) - # print(colored(f"Response Headers: {response.headers}","green")) - # print(colored(f"Response: {response.json()}","green")) - # print(colored(f"Response Status code: {response.status_code}","green")) - # print(colored("Get AUTH Success. Received access token", "green")) - # print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - return response_payload['access_token'] - - except requests.exceptions.HTTPError as err: - raise Exception(err.response.text, err.response.status_code) - - - def execute_previous_register_invoker(self): - - config = configparser.ConfigParser() - config.read('capif_ops/config_files/credentials.properties') - - username = config.get("credentials", "invoker_username") - password = config.get("credentials", "invoker_password") - role = config.get("credentials", "invoker_role") - description = config.get("credentials", "invoker_description") - cn = config.get("credentials", "invoker_cn") - - capif_ip = os.getenv('CAPIF_HOSTNAME') - capif_port = os.getenv('CAPIF_PORT') - - if os.path.exists("capif_ops/config_files/demo_values.json"): - #os.remove("capif_ops/config_files/demo_values.json") - with open('capif_ops/config_files/demo_values.json', 'r') as demo_file: - demo_values = json.load(demo_file) - else: - demo_values = {} - - #First we need register exposer in CAPIF - try: - netappID, ccf_onboarding_url, ccf_discover_url = self.__register_invoker_to_capif(capif_ip, capif_port, username, password, role, description, cn) - demo_values['netappID'] = netappID - demo_values['ccf_onboarding_url'] = ccf_onboarding_url - demo_values['ccf_discover_url'] = ccf_discover_url - #print(colored(f"NetAppID: {netappID}\n","yellow")) - #print("provider ID: {}".format(providerID)) - - with open('capif_ops/config_files/demo_values.json', 'w') as outfile: - json.dump(demo_values, outfile) - - if 'netappID' in demo_values: - access_token = self.__get_capif_auth(capif_ip, capif_port, username, password) - demo_values['capif_access_token'] = access_token - - with open('capif_ops/config_files/demo_values.json', 'w') as outfile: - json.dump(demo_values, outfile) - except Exception as e: - status_code = e.args[0] - if status_code == 409: - print() - else: - print(e) - - return True diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_register_to_capif.py b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_register_to_capif.py deleted file mode 100644 index 49be7316a33fdc85a6150fe2bbc00471cfe87f77..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_register_to_capif.py +++ /dev/null @@ -1,134 +0,0 @@ -from dis import dis -import requests -import json -import configparser -import redis -import os -from termcolor import colored - - -from OpenSSL.SSL import FILETYPE_PEM -from OpenSSL.crypto import (dump_certificate_request, dump_privatekey, load_publickey, PKey, TYPE_RSA, X509Req, dump_publickey) - - -class RegisterInvoker(): - def __create_csr(self, name): - - # create public/private key - key = PKey() - key.generate_key(TYPE_RSA, 2048) - - # Generate CSR - req = X509Req() - req.get_subject().CN = name - req.get_subject().O = 'Telefonica I+D' - req.get_subject().OU = 'Innovation' - req.get_subject().L = 'Madrid' - req.get_subject().ST = 'Madrid' - req.get_subject().C = 'ES' - req.get_subject().emailAddress = 'inno@tid.es' - req.set_pubkey(key) - req.sign(key, 'sha256') - - csr_request = dump_certificate_request(FILETYPE_PEM, req) - - private_key = dump_privatekey(FILETYPE_PEM, key) - - return csr_request, private_key - - - - - def __onboard_netapp_to_capif(self, capif_ip, capif_callback_ip, capif_callback_port, jwt_token, ccf_url, log_level): - - print(colored("Onboarding netapp to CAPIF","yellow")) - url = 'https://{}/{}'.format(capif_ip, ccf_url) - - with open('capif_ops/config_files/demo_values.json', 'r') as demo_file: - demo_values = json.load(demo_file) - - csr_request, private_key = self.__create_csr("invoker") - - if 'pub_key' not in demo_values: - private_key_file = open("capif_ops/certs/invoker_private_key.key", 'wb+') - private_key_file.write(bytes(private_key)) - - json_file = open('capif_ops/config_files/invoker_details.json', 'rb') - payload_dict = json.load(json_file) - if 'pub_key' not in demo_values: - payload_dict['onboardingInformation']['apiInvokerPublicKey'] = csr_request.decode("utf-8") - else: - payload_dict['onboardingInformation']['apiInvokerPublicKey'] = demo_values['pub_key'] - payload_dict['notificationDestination'] = payload_dict['notificationDestination'].replace("X", capif_callback_ip) - payload_dict['notificationDestination'] = payload_dict['notificationDestination'].replace("Y", capif_callback_port) - payload = json.dumps(payload_dict, indent=2) - - print(colored(f"Request Body: {payload}", "yellow")) - - headers = { - 'Authorization': 'Bearer {}'.format(jwt_token), - 'Content-Type': 'application/json' - } - - try: - - if log_level == "debug": - print(colored("''''''''''REQUEST'''''''''''''''''","blue")) - print(colored(f"Request: to {url}","blue")) - print(colored(f"Request Headers: {headers}", "blue")) - print(colored(f"Request Body: {json.dumps(payload)}", "blue")) - print(colored(f"''''''''''REQUEST'''''''''''''''''", "blue")) - - response = requests.request("POST", url, headers=headers, data=payload, verify='capif_ops/certs/ca.crt') - response.raise_for_status() - response_payload = json.loads(response.text) - certification_file = open('capif_ops/certs/dummy.crt', 'wb') - certification_file.write(bytes(response_payload['onboardingInformation']['apiInvokerCertificate'], 'utf-8')) - certification_file.close() - - if log_level == "debug": - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - print(colored(f"Response to: {response.url}","green")) - print(colored(f"Response Headers: {response.headers}","green")) - print(colored(f"Response: {response.json()}","green")) - print(colored(f"Response Status code: {response.status_code}","green")) - print(colored("Success onboard invoker","green")) - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - return response_payload['apiInvokerId'], payload_dict['onboardingInformation']['apiInvokerPublicKey'] - except requests.exceptions.HTTPError as err: - raise Exception(err.response.text, err.response.status_code) - - - - - - def execute_register_invoker(self, log_level): - - config = configparser.ConfigParser() - config.read('capif_ops/config_files/credentials.properties') - - capif_ip = os.getenv('CAPIF_HOSTNAME') - - capif_callback_ip = config.get("credentials", "capif_callback_ip") - capif_callback_port = config.get("credentials", "capif_callback_port") - - with open('capif_ops/config_files/demo_values.json', 'r') as demo_file: - demo_values = json.load(demo_file) - - try: - capif_access_token = demo_values['capif_access_token'] - ccf_onboarding_url = demo_values['ccf_onboarding_url'] - invokerID, pub_key = self.__onboard_netapp_to_capif(capif_ip, capif_callback_ip, capif_callback_port, capif_access_token, ccf_onboarding_url, log_level) - demo_values['invokerID'] = invokerID - demo_values['pub_key'] = pub_key - print("ApiInvokerID: {}\n".format(invokerID)) - with open('capif_ops/config_files/demo_values.json', 'w') as outfile: - json.dump(demo_values, outfile) - - except Exception as e: - status_code = e.args[0] - if status_code == 403: - print("Invoker already registered.") - print("Chanage invoker public key in invoker_details.json\n") - else: - print(e) diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_remove_security_context.py b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_remove_security_context.py deleted file mode 100644 index 18bb3f35966b07492cbe71f2f6cfb1120689d59e..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_remove_security_context.py +++ /dev/null @@ -1,82 +0,0 @@ -from dis import dis -from email import charset -import requests -import json -import configparser -import redis -import os -from termcolor import colored - - -class InvokerRemoveSecurityContext(): - - def __remove_security_service(self, capif_ip, api_invoker_id, jwt_token, ccf_url, demo_values, log_level): - - - url = "https://{}/capif-security/v1/trustedInvokers/{}".format(capif_ip, api_invoker_id) - - headers = { - 'Content-Type': 'application/json' - } - - try: - - if log_level == "debug": - print(colored("''''''''''REQUEST'''''''''''''''''","blue")) - print(colored(f"Request: to {url}","blue")) - print(colored(f"Request Headers: {headers}", "blue")) - print(colored(f"''''''''''REQUEST'''''''''''''''''", "blue")) - - response = requests.delete(url, cert=('capif_ops/certs/dummy.crt', 'capif_ops/certs/invoker_private_key.key'), verify='capif_ops/certs/ca.crt') - response.raise_for_status() - - if log_level == "debug": - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - print(colored(f"Response to: {response.url}","green")) - print(colored(f"Response Headers: {response.headers}","green")) - print(colored(f"Response: {response.json()}","green")) - print(colored(f"Response Status code: {response.status_code}","green")) - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - - return - except requests.exceptions.HTTPError as err: - print(err.response.text) - message = json.loads(err.response.text) - status = err.response.status_code - raise Exception(message, status) - - def execute_remove_security_context(self, log_level): - - with open('capif_ops/config_files/demo_values.json', 'r') as demo_file: - demo_values = json.load(demo_file) - - config = configparser.ConfigParser() - config.read('credentials.properties') - - capif_ip = os.getenv('CAPIF_HOSTNAME') - invokerID = "" - capif_access_token = "" - ccf_discover_url = "" - - try: - - invokerID = demo_values['invokerID'] - capif_access_token = demo_values['capif_access_token'] - ccf_discover_url = demo_values['ccf_discover_url'] - security_information = self.__remove_security_service(capif_ip, invokerID, capif_access_token, ccf_discover_url, demo_values,log_level) - print(colored(json.dumps(security_information, indent=2),"yellow")) - print(colored("Register Security context","yellow")) - - with open('capif_ops/config_files/demo_values.json', 'w') as outfile: - json.dump(demo_values, outfile) - - - except Exception as e: - status_code = e.args[0] - if status_code == 401: - print("API Invoker is not authorized") - elif status_code == 403: - print("API Invoker does not exist. API Invoker id not found") - else: - print(e) - diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_secutiry_context.py b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_secutiry_context.py deleted file mode 100644 index a26988f1c499697d219876e7ee16c2c95b8eef3b..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_secutiry_context.py +++ /dev/null @@ -1,96 +0,0 @@ -from dis import dis -from email import charset -import requests -import json -import configparser -import redis -import os -from termcolor import colored - - -class InvokerSecurityContext(): - - def __register_security_service(self, capif_ip, api_invoker_id, jwt_token, ccf_url, demo_values, log_level): - - - url = "https://{}/capif-security/v1/trustedInvokers/{}".format(capif_ip, api_invoker_id) - - with open('capif_ops/config_files/security_info.json', "rb") as f: - payload = json.load(f) - - count = 0 - for profile in payload["securityInfo"]: - profile["aefId"] = demo_values[f"aef_id_{count}"] - profile["apiId"] = demo_values[f"api_id_{count}"] - count += 1 - - headers = { - 'Content-Type': 'application/json' - } - - # payload_dict = json.dumps(payload, indent=2) - - # print(colored(f"Request Body: {payload_dict}", "yellow")) - - try: - - if log_level == "debug": - print(colored("''''''''''REQUEST'''''''''''''''''","blue")) - print(colored(f"Request: to {url}","blue")) - print(colored(f"Request Headers: {headers}", "blue")) - print(colored(f"''''''''''REQUEST'''''''''''''''''", "blue")) - - response = requests.put(url, json=payload, cert=('capif_ops/certs/dummy.crt', 'capif_ops/certs/invoker_private_key.key'), verify='capif_ops/certs/ca.crt') - response.raise_for_status() - response_payload = response.json() - - if log_level == "debug": - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - print(colored(f"Response to: {response.url}","green")) - print(colored(f"Response Headers: {response.headers}","green")) - print(colored(f"Response: {response.json()}","green")) - print(colored(f"Response Status code: {response.status_code}","green")) - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - - return response_payload - except requests.exceptions.HTTPError as err: - print(err.response.text) - message = json.loads(err.response.text) - status = err.response.status_code - raise Exception(message, status) - - def execute_register_security_context(self, log_level): - - with open('capif_ops/config_files/demo_values.json', 'r') as demo_file: - demo_values = json.load(demo_file) - - config = configparser.ConfigParser() - config.read('credentials.properties') - - capif_ip = os.getenv('CAPIF_HOSTNAME') - invokerID = "" - capif_access_token = "" - ccf_discover_url = "" - - try: - - invokerID = demo_values['invokerID'] - capif_access_token = demo_values['capif_access_token'] - ccf_discover_url = demo_values['ccf_discover_url'] - security_information = self.__register_security_service(capif_ip, invokerID, capif_access_token, ccf_discover_url, demo_values,log_level) - print(colored(json.dumps(security_information, indent=2),"yellow")) - print(colored("Register Security context","yellow")) - - with open('capif_ops/config_files/demo_values.json', 'w') as outfile: - json.dump(demo_values, outfile) - - - except Exception as e: - status_code = e.args[0] - if status_code == 401: - print("API Invoker is not authorized") - elif status_code == 403: - print("API Invoker does not exist. API Invoker id not found") - else: - print(e) - diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_to_service.py b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_to_service.py deleted file mode 100644 index bc54ad7077759696396525433075b004416b97d9..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/invoker_to_service.py +++ /dev/null @@ -1,168 +0,0 @@ -from dis import dis -import requests -import json -import configparser -import redis -import os -import argparse -import re -from termcolor import colored - -# Get environment variables - - -class InvokerToService(): - def __demo_to_aef(self, operation, demo_ip, demo_port, demo_url, jwt_token, name, log_level): - - #def register_netapp_to_nef(nef_ip, nef_port): - access_token_url = "https://{}:{}/api/v1/login/access-token".format(demo_ip, demo_port) - - access_payload = { - "username": "admin@my-email.com", - "password": "pass" - } - - response = requests.request('POST', access_token_url, data=access_payload, verify=False) - parsed = json.loads(response.text) - - access_token = parsed['access_token'] - - print(colored("Using AEF Service API","yellow")) - url = "https://{}:{}{}".format(demo_ip, demo_port, demo_url) - #url = "http://python_aef:8086/hello" - - - - json_file = open('capif_ops/config_files/service_request_body.json', 'rb') - payload_dict = json.load(json_file) - payload = json.dumps(payload_dict, indent=2) - - files = {} - headers = { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer '+ access_token + "," +jwt_token - } - - if operation == "create": - print(colored(f"Request Body: {payload}", "yellow")) - try: - if log_level == "debug": - print(colored("''''''''''REQUEST'''''''''''''''''","blue")) - print(colored(f"Request: to {url}","blue")) - print(colored(f"Request Headers: {headers}", "blue")) - print(colored(f"Request Body: {json.dumps(payload, indent=2)}", "blue")) - print(colored(f"''''''''''REQUEST'''''''''''''''''", "blue")) - response = requests.request("POST", url, headers=headers, data=payload, files=files, verify=False) - response.raise_for_status() - response_payload = json.loads(response.text) - - if log_level == "debug": - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - print(colored(f"Response to: {response.url}","green")) - print(colored(f"Response Headers: {response.headers}","green")) - print(colored(f"Response: {response.json()}","green")) - print(colored(f"Response Status code: {response.status_code}","green")) - print(colored("Success to invoke service","green")) - print(colored(response_payload,"green")) - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - - link_created_resource = response_payload["link"] - resource_id = link_created_resource.rsplit('/', 1)[-1] - if resource_id: - with open('capif_ops/config_files/demo_values.json', 'r') as demo_file: - demo_values = json.load(demo_file) - demo_values["demo_resource_id"] = resource_id - with open('capif_ops/config_files/demo_values.json', 'w') as outfile: - json.dump(demo_values, outfile) - - return response_payload - except requests.exceptions.HTTPError as err: - print(err.response.text) - message = json.loads(err.response.text) - status = err.response.status_code - raise Exception(message, status) - - elif operation == "delete": - with open('capif_ops/config_files/demo_values.json', 'r') as demo_file: - demo_values = json.load(demo_file) - resource_id = demo_values["demo_resource_id"] - if resource_id == "": - print("Not found resource to delete") - return - - url = url + "/" + resource_id - try: - if log_level == "debug": - print(colored("''''''''''REQUEST'''''''''''''''''","blue")) - print(colored(f"Request: to {url}","blue")) - print(colored(f"Request Headers: {headers}", "blue")) - print(colored(f"''''''''''REQUEST'''''''''''''''''", "blue")) - response = requests.request("DELETE", url, headers=headers, verify=False) - response.raise_for_status() - response_payload = json.loads(response.text) - - if log_level == "debug": - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - print(colored(f"Response to: {response.url}","green")) - print(colored(f"Response Headers: {response.headers}","green")) - print(colored(f"Response: {response.json()}","green")) - print(colored(f"Response Status code: {response.status_code}","green")) - print(colored("Success to invoke service","green")) - print(colored(response_payload,"green")) - print(colored("''''''''''RESPONSE'''''''''''''''''","green")) - - demo_values["demo_resource_id"] = "" - with open('capif_ops/config_files/demo_values.json', 'w') as outfile: - json.dump(demo_values, outfile) - - return response_payload - except requests.exceptions.HTTPError as err: - print(err.response.text) - message = json.loads(err.response.text) - status = err.response.status_code - raise Exception(message, status) - - else: - print("You must spicify if you want create or delete resource") - - - def execute_invoker_to_service(self, input): - - # parser = argparse.ArgumentParser() - # parser.add_argument('--name', metavar= "name", type=str, default="Evolve5G", help="Name to send to the aef service") - # args = parser.parse_args() - input_name = "prueba" - operation = "" - log_level = "" - params = input.split() - if len(params) > 0: - operation = params[0] - if len(params) > 1: - log_level = params[1] - - with open('capif_ops/config_files/demo_values.json', 'r') as demo_file: - demo_values = json.load(demo_file) - - try: - if 'netapp_service_token' in demo_values: - - print(colored("Doing test","yellow")) - jwt_token = demo_values['netapp_service_token'] - invokerID = demo_values['invokerID'] - demo_ip = demo_values['demo_ipv4_addr_0'] - #demo_port = demo_values['demo_port_0'] - demo_port = 4443 - demo_url = demo_values['demo_url_0'] - demo_url = re.sub(r'\{scsAsId\}', 'myNetapp', demo_url) - - result = self.__demo_to_aef(operation, demo_ip, demo_port, demo_url, jwt_token, input_name, log_level) - print(colored(f"Response: {json.dumps(result, indent=2)}", "yellow")) - print(colored("Success","yellow")) - except Exception as e: - status_code = e.args[0] - if status_code == 401: - print("API Invoker is not authorized") - elif status_code == 403: - print("API Invoker does not exist. API Invoker id not found") - else: - print(e) \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/nef_calback_server/callback.py b/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/nef_calback_server/callback.py deleted file mode 100644 index 682d35893dd8b8798fea225098779e0f65b07044..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/nef_calback_server/callback.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python3 - -from flask import Flask, jsonify, request -# import redis -# from redis.commands.json.path import Path -import secrets -from werkzeug import serving -import os - - - -app = Flask(__name__) - -COUNTER = 0 - -@app.route("/nefcallbacks", methods=["POST"]) -def nefcallback(): - global COUNTER - COUNTER += 1 - print(f"Notification received from NEF: {COUNTER}") - - return jsonify(message="Receive message"), 200 - - -if __name__ == '__main__': - serving.run_simple('0.0.0.0', 8080, app) \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/main.py b/services/capif-client/CAPIFInvokerGUI/invoker_gui/main.py deleted file mode 100644 index bed7c8bcd6b02ac4ba0ffda5d42552040f7b505d..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/main.py +++ /dev/null @@ -1,98 +0,0 @@ - -from cmd import Cmd -from capif_ops.invoker_previous_register import PreviousRegister -from capif_ops.invoker_register_to_capif import RegisterInvoker -from capif_ops.invoker_discover_service import DiscoverService -from capif_ops.invoker_secutiry_context import InvokerSecurityContext -from capif_ops.invoker_get_security_auth import InvokerGetSecurityAuth -from capif_ops.invoker_delete import RemoveInvoker -from capif_ops.invoker_get_auth import PreviousAuth -from capif_ops.invoker_remove_security_context import InvokerRemoveSecurityContext -from capif_ops.invoker_to_service import InvokerToService -import shlex -import subprocess -from art import * - -prev_register = PreviousRegister() -regiter_capif = RegisterInvoker() -discover_service = DiscoverService() -register_security_context = InvokerSecurityContext() -security_context_auth = InvokerGetSecurityAuth() -remove_invoker = RemoveInvoker() -invoker_auth = PreviousAuth() -remove_security_service = InvokerRemoveSecurityContext() -invoker_service = InvokerToService() - - - -class CAPIFProvider(Cmd): - - def __init__(self): - Cmd.__init__(self) - self.prompt = "> " - self.intro = tprint("Welcome to Invoker Console") - - def emptyline(self): - """Do nothing on empty input line""" - pass - - def preloop(self): - state = prev_register.execute_previous_register_invoker() - self.previous_register_state = state - - def precmd(self, line): - - line = line.lower() - args = shlex.split(line) - - if len(args) >= 1 and args[0] in ["goodbye"]: - print("The first argument is username") - return "" - - elif len(args) >= 1 and args[0] not in ["->", "wall", "follows", "exit", "help"]: - pass - - return line - - def do_register_invoker(self, input): - 'Register invoker to CAPIF' - regiter_capif.execute_register_invoker(input) - - def do_discover_service(self, input): - 'Discover all services published in CAPIF' - discover_service.execute_discover_service(input) - - def do_register_security_context(self, input): - 'Create security context to use services' - register_security_context.execute_register_security_context(input) - - def do_get_security_auth(self, input): - "If you select Oauth as security method use this command to obtain jwt token to access service" - security_context_auth.execute_get_security_auth(input) - - def do_get_auth(self, input): - 'Get jwt token to register invoker in CAPIF (Optional, only if token expires)' - invoker_auth.execute_get_auth(input) - - def do_remove_security_context(self, input): - print("Not implemented yet") - #remove_security_service.execute_remove_security_context(input) - - def do_remove_invoker(self, input): - "Remove invoker from CAPIF" - remove_invoker.execute_remove_invoker(input) - - def do_call_service(self, input): - "Test invocation os service API" - invoker_service.execute_invoker_to_service(input) - - def do_exit(self, input): - print('\nExiting...') - return True - - -if __name__ == '__main__': - try: - CAPIFProvider().cmdloop() - except KeyboardInterrupt: - print('\nExiting...') \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/prepare.sh b/services/capif-client/CAPIFInvokerGUI/invoker_gui/prepare.sh deleted file mode 100755 index ca0305a044b9c7aa9f6d60a88bac3c4f13707fc0..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/prepare.sh +++ /dev/null @@ -1,11 +0,0 @@ -VAULT_ADDR="http://$VAULT_HOSTNAME:$VAULT_PORT" -VAULT_TOKEN=$VAULT_ACCESS_TOKEN - -curl -k -retry 30 \ - --retry-all-errors \ - --connect-timeout 5 \ - --max-time 10 \ - --retry-delay 10 \ - --retry-max-time 300 \ - --header "X-Vault-Token: $VAULT_TOKEN" \ - --request GET "$VAULT_ADDR/v1/secret/data/ca" 2>/dev/null | jq -r '.data.data.ca' -j > ./capif_ops/certs/ca.crt \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/requirements.txt b/services/capif-client/CAPIFInvokerGUI/invoker_gui/requirements.txt deleted file mode 100755 index a9e6ededb6f821c6f5a8c6b991c68a4032dc5fad..0000000000000000000000000000000000000000 --- a/services/capif-client/CAPIFInvokerGUI/invoker_gui/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -requests == 2.26.0 -evolved5g >= 0.8.9 -python_dateutil >= 2.6.0 -setuptools >= 21.0.0 -Flask -watchdog -redis -configparser -redis -pyopenssl -pyjwt -art -termcolor \ No newline at end of file diff --git a/services/capif-client/Dockerfile b/services/capif-client/Dockerfile deleted file mode 100644 index ad7e50339036e5012ef65b99bf89c66a747be050..0000000000000000000000000000000000000000 --- a/services/capif-client/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM public.ecr.aws/o2v4a8t6/opencapif/client:3.1.3 - -COPY ./CAPIFInvokerGUI . -RUN pip3 install -r ./invoker_gui/requirements.txt -CMD ["sleep", "infinity"] \ No newline at end of file diff --git a/services/check_services_are_running.sh b/services/check_services_are_running.sh index 3bad399d33d55e04256e6faf58cd00293ecbbc4e..16de7046452f3faa4ab94b04e340def03d89d9d4 100755 --- a/services/check_services_are_running.sh +++ b/services/check_services_are_running.sh @@ -1,12 +1,39 @@ #!/bin/bash +export CAPIF_PRIV_KEY= +export CAPIF_PRIV_KEY_BASE_64= +export MONITORING= + +running="$(docker compose -f docker-compose-vault.yml ps --services --all --filter "status=running")" +services="$(docker compose -f docker-compose-vault.yml ps --services --all)" +if [ "$running" != "$services" ]; then + echo "Following Vault services are not running:" + # Bash specific + comm -13 <(sort <<<"$running") <(sort <<<"$services") + exit 1 +else + echo "All Vault services are running" +fi + running="$(docker compose -f docker-compose-capif.yml ps --services --all --filter "status=running")" services="$(docker compose -f docker-compose-capif.yml ps --services --all)" if [ "$running" != "$services" ]; then - echo "Following services are not running:" + echo "Following CCF services are not running:" + # Bash specific + comm -13 <(sort <<<"$running") <(sort <<<"$services") + exit 1 +else + echo "All CCF services are running" +fi + +running="$(docker compose -f docker-compose-register.yml ps --services --all --filter "status=running")" +services="$(docker compose -f docker-compose-register.yml ps --services --all)" +if [ "$running" != "$services" ]; then + echo "Following Register services are not running:" # Bash specific comm -13 <(sort <<<"$running") <(sort <<<"$services") exit 1 else - echo "All services are running" - exit 0 + echo "All Register services are running" fi + +exit 0 diff --git a/services/clean_capif_docker_services.sh b/services/clean_capif_docker_services.sh index fb8949784d4eb81d5a0b958c197765f82118d60d..cfc5b716dacf2f058c8e868b2fdef97efb96b3ce 100755 --- a/services/clean_capif_docker_services.sh +++ b/services/clean_capif_docker_services.sh @@ -6,6 +6,7 @@ help() { echo " -v : Clean vault service" echo " -r : Clean register service" echo " -m : Clean monitoring service" + echo " -s : Clean Robot Mock service" echo " -a : Clean all services" echo " -h : show this help" exit 1 @@ -21,7 +22,7 @@ FILES=() echo "${FILES[@]}" # Read params -while getopts "cvrahm" opt; do +while getopts "cvrahms" opt; do case $opt in c) echo "Remove Capif services" @@ -39,9 +40,13 @@ while getopts "cvrahm" opt; do echo "Remove monitoring service" FILES+=("../monitoring/docker-compose.yml") ;; + s) + echo "Robot Mock Server" + FILES+=("docker-compose-mock-server.yml") + ;; a) echo "Remove all services" - FILES=("docker-compose-capif.yml" "docker-compose-vault.yml" "docker-compose-register.yml" "../monitoring/docker-compose.yml") + FILES=("docker-compose-capif.yml" "docker-compose-vault.yml" "docker-compose-register.yml" "docker-compose-mock-server.yml" "../monitoring/docker-compose.yml") ;; h) help @@ -74,4 +79,6 @@ done docker network rm capif-network +docker volume prune --all --force + echo "Clean complete." diff --git a/services/clean_mock_server.sh b/services/clean_mock_server.sh new file mode 100755 index 0000000000000000000000000000000000000000..5ea886cdee5476d39a20440d1f2a27f3e49a0857 --- /dev/null +++ b/services/clean_mock_server.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +FILE="docker-compose-mock-server.yml" + +echo "Executing 'docker compose down' for file $FILE" +docker compose -f "$FILE" down --rmi all +status=$? + if [ $status -eq 0 ]; then + echo "*** Removed Service from $FILE ***" + else + echo "*** Some services of $FILE failed on clean ***" + fi + +docker volume prune --all --force + +echo "Clean complete." diff --git a/services/docker-compose-capif.yml b/services/docker-compose-capif.yml index f667d8c2999ac6ca304cafd621dc3ba2df32a074..779df2185c851ae0ef1fa2ce35fa73510f97bc2d 100644 --- a/services/docker-compose-capif.yml +++ b/services/docker-compose-capif.yml @@ -1,5 +1,3 @@ -version: '3.7' - services: redis: image: "redis:alpine" @@ -11,6 +9,30 @@ services: - $PWD/redis.conf:/usr/local/etc/redis/redis.conf environment: - REDIS_REPLICATION_MODE=master + + + helper: + build: + context: ./helper + expose: + - "8080" + container_name: helper + restart: unless-stopped + volumes: + - ./helper:/usr/src/app + extra_hosts: + - host.docker.internal:host-gateway + - fluent-bit:host-gateway + - otel-collector:host-gateway + - vault:host-gateway + environment: + - CAPIF_HOSTNAME=${CAPIF_HOSTNAME} + - CONTAINER_NAME=helper + - VAULT_HOSTNAME=vault + - VAULT_ACCESS_TOKEN=dev-only-token + - VAULT_PORT=8200 + depends_on: + - nginx access-control-policy: build: TS29222_CAPIF_Access_Control_Policy_API @@ -30,7 +52,6 @@ services: depends_on: - redis - nginx - api-invoker-management: build: diff --git a/services/docker-compose-mock-server.yml b/services/docker-compose-mock-server.yml new file mode 100644 index 0000000000000000000000000000000000000000..1415b439bb1eec214b6adbd5af74812432544bf1 --- /dev/null +++ b/services/docker-compose-mock-server.yml @@ -0,0 +1,20 @@ +services: + mock-server: + build: + context: ./mock_server + ports: + - 9090:9090 + volumes: + - ./mock_server:/usr/src/app + extra_hosts: + - host.docker.internal:host-gateway + restart: unless-stopped + image: public.ecr.aws/o2v4a8t6/opencapif/mock_server:latest + +networks: + default: + name: capif-network + external: true + + + diff --git a/services/docker-compose-register.yml b/services/docker-compose-register.yml index 5363e0174270c24cdc9ea383f731945c117f4489..b0e55710ca7f1bec7f229dc32d55ee823a80cec2 100644 --- a/services/docker-compose-register.yml +++ b/services/docker-compose-register.yml @@ -1,5 +1,3 @@ -version: '3.7' - services: register: build: diff --git a/services/docker-compose-vault.yml b/services/docker-compose-vault.yml index e0bb7bce196fde18748da2967cb1b94485b2ef59..82a38d0a8dd9d728c74a44ffe131ae272c34f211 100644 --- a/services/docker-compose-vault.yml +++ b/services/docker-compose-vault.yml @@ -1,4 +1,3 @@ -version: '3.7' services: vault: build: diff --git a/services/helper/Dockerfile b/services/helper/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..2c521b77a2f232b87dd82c95f7f8fba652d083a7 --- /dev/null +++ b/services/helper/Dockerfile @@ -0,0 +1,18 @@ +FROM public.ecr.aws/o2v4a8t6/opencapif/python:3-alpine + +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app + +COPY requirements.txt /usr/src/app/requirements.txt + +RUN apk add -U --no-cache gcc build-base linux-headers ca-certificates libffi-dev libressl-dev libxslt-dev +RUN pip3 install --no-cache-dir -r requirements.txt +RUN apk add openssl curl redis + +COPY . /usr/src/app + +EXPOSE 8080 + +ENTRYPOINT ["gunicorn"] + +CMD ["--bind", "0.0.0.0:8080", "--chdir", "/usr/src/app/helper_service", "wsgi:app"] diff --git a/services/helper/config.yaml b/services/helper/config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..46e5801bef78265a4d572d2a59f9628814dfafb5 --- /dev/null +++ b/services/helper/config.yaml @@ -0,0 +1,18 @@ +mongo: { + 'user': 'root', + 'password': 'example', + 'db': 'capif', + 'invoker_col': 'invokerdetails', + 'provider_col': 'providerenrolmentdetails', + 'col_services': "serviceapidescriptions", + 'col_security': "security", + 'col_event': "eventsdetails", + 'host': 'mongo', + 'port': "27017" +} + +ca_factory: { + "url": "vault", + "port": "8200", + "token": "dev-only-token" +} diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/__init__.py b/services/helper/helper_service/__init__.py similarity index 100% rename from services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/__init__.py rename to services/helper/helper_service/__init__.py diff --git a/services/helper/helper_service/app.py b/services/helper/helper_service/app.py new file mode 100644 index 0000000000000000000000000000000000000000..2534195ec8b2bea31ea627474360a97814488905 --- /dev/null +++ b/services/helper/helper_service/app.py @@ -0,0 +1,67 @@ +from flask import Flask +import logging +from controllers.helper_controller import helper_routes +from OpenSSL.crypto import PKey, TYPE_RSA, X509Req, dump_certificate_request, FILETYPE_PEM, dump_privatekey +from config import Config +import json +import requests + +app = Flask(__name__) +config = Config().get_config() + +# Create a superadmin CSR and keys +key = PKey() +key.generate_key(TYPE_RSA, 2048) +req = X509Req() +req.get_subject().O = 'OCF helper' +req.get_subject().OU = 'helper' +req.get_subject().L = 'Madrid' +req.get_subject().ST = 'Madrid' +req.get_subject().C = 'ES' +req.get_subject().emailAddress = 'helper@tid.es' +req.set_pubkey(key) +req.sign(key, 'sha256') + +csr_request = dump_certificate_request(FILETYPE_PEM, req) +private_key = dump_privatekey(FILETYPE_PEM, key) + +# Save superadmin private key +key_file = open("certs/superadmin.key", 'wb+') +key_file.write(bytes(private_key)) +key_file.close() + +# Request superadmin certificate +url = 'http://{}:{}/v1/pki_int/sign/my-ca'.format(config["ca_factory"]["url"], config["ca_factory"]["port"]) +headers = {'X-Vault-Token': f"{config["ca_factory"]["token"]}"} +data = { + 'format':'pem_bundle', + 'ttl': '43000h', + 'csr': csr_request, + 'common_name': "superadmin" +} + +response = requests.request("POST", url, headers=headers, data=data, verify = False) +superadmin_cert = json.loads(response.text)['data']['certificate'] + +# Save the superadmin certificate +cert_file = open("certs/superadmin.crt", 'wb') +cert_file.write(bytes(superadmin_cert, 'utf-8')) +cert_file.close() + +url = f"http://{config['ca_factory']['url']}:{config['ca_factory']['port']}/v1/secret/data/ca" +headers = { + + 'X-Vault-Token': config['ca_factory']['token'] +} +response = requests.request("GET", url, headers=headers, verify = False) + +ca_root = json.loads(response.text)['data']['data']['ca'] +cert_file = open("certs/ca_root.crt", 'wb') +cert_file.write(bytes(ca_root, 'utf-8')) +cert_file.close() + +app.register_blueprint(helper_routes) +app.logger.setLevel(logging.DEBUG) + +# if __name__ == '__main__': +# app.run(host='0.0.0.0', port=8080, debug=True) \ No newline at end of file diff --git a/services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/nef_calback_server/__init__.py b/services/helper/helper_service/certs/.gitkeep similarity index 100% rename from services/capif-client/CAPIFInvokerGUI/invoker_gui/capif_ops/nef_calback_server/__init__.py rename to services/helper/helper_service/certs/.gitkeep diff --git a/services/helper/helper_service/config.py b/services/helper/helper_service/config.py new file mode 100644 index 0000000000000000000000000000000000000000..f9574a678498483f4e138d4890fc1fa079cebce4 --- /dev/null +++ b/services/helper/helper_service/config.py @@ -0,0 +1,20 @@ +import yaml +import os + +#Config class to get config +class Config: + def __init__(self): + self.cached = 0 + self.file="../config.yaml" + self.my_config = {} + + stamp = os.stat(self.file).st_mtime + if stamp != self.cached: + self.cached = stamp + f = open(self.file) + self.my_config = yaml.safe_load(f) + f.close() + + def get_config(self): + return self.my_config + diff --git a/services/helper/helper_service/controllers/helper_controller.py b/services/helper/helper_service/controllers/helper_controller.py new file mode 100644 index 0000000000000000000000000000000000000000..f645a564ca88eb238ced5784cdc5ff2450910075 --- /dev/null +++ b/services/helper/helper_service/controllers/helper_controller.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 + +from flask import Blueprint, request, current_app, jsonify +from core.helper_operations import HelperOperations +from config import Config + +config = Config().get_config() + +helper_routes = Blueprint("helper_routes", __name__) +helper_operation = HelperOperations() + +@helper_routes.route("/helper/getInvokers", methods=["GET"]) +def getInvokers(): + uuid = request.args.get('uuid') + invoker_id = request.args.get('api_invoker_id') + page_size = request.args.get('page_size') + page = request.args.get('page') + sort_order = request.args.get('sort_order') + if page_size: + page_size = int(page_size) + if page_size < 0: + return jsonify(message="The value of the page_size parameter must be greater than 0"), 400 + if page: + page = int(page) + if page < 0: + return jsonify(message="The value of the page parameter must be greater than 0"), 400 + + current_app.logger.debug(f"uuid: {uuid}, invoker_id: {invoker_id}, page: {page}, page_size: {page_size}, sort_order: {sort_order}") + + return helper_operation.get_invokers(uuid, invoker_id, page, page_size, sort_order) + +@helper_routes.route("/helper/getProviders", methods=["GET"]) +def getProviders(): + uuid = request.args.get('uuid') + provider_id = request.args.get('api_prov_dom_id') + page_size = request.args.get('page_size') + page = request.args.get('page') + sort_order = request.args.get('sort_order') + + if page_size: + page_size = int(page_size) + if page_size < 0: + return jsonify(message="The value of the page_size parameter must be greater than 0"), 400 + if page: + page = int(page) + if page < 0: + return jsonify(message="The value of the page parameter must be greater than 0"), 400 + + current_app.logger.debug(f"uuid: {uuid}, provider_id: {provider_id}, page: {page}, page_size: {page_size}, sort_order: {sort_order}") + + return helper_operation.get_providers(uuid, provider_id, page, page_size, sort_order) + + +@helper_routes.route("/helper/getServices", methods=["GET"]) +def getServices(): + service_id = request.args.get('service_id') + apf_id = request.args.get('apf_id') + api_name = request.args.get('api_name') + page_size = request.args.get('page_size') + page = request.args.get('page') + sort_order = request.args.get('sort_order') + if page_size: + page_size = int(page_size) + if page_size < 0: + return jsonify(message="The value of the page_size parameter must be greater than 0"), 400 + if page: + page = int(page) + if page < 0: + return jsonify(message="The value of the page parameter must be greater than 0"), 400 + + current_app.logger.debug(f"service_id: {service_id}, apf_id: {apf_id}, api_name: {api_name}, page: {page}, page_size: {page_size}, sort_order: {sort_order}") + + return helper_operation.get_services(service_id, apf_id, api_name, page, page_size, sort_order) + +@helper_routes.route("/helper/getSecurity", methods=["GET"]) +def getSecurity(): + invoker_id = request.args.get('invoker_id') + page_size = request.args.get('page_size') + page = request.args.get('page') + if page_size: + page_size = int(page_size) + if page_size < 0: + return jsonify(message="The value of the page_size parameter must be greater than 0"), 400 + if page: + page = int(page) + if page < 0: + return jsonify(message="The value of the page parameter must be greater than 0"), 400 + + current_app.logger.debug(f"invoker_id: {invoker_id}, page: {page}, page_size: {page_size} ") + + return helper_operation.get_security(invoker_id, page, page_size) + +@helper_routes.route("/helper/getEvents", methods=["GET"]) +def getEvents(): + subscriber_id = request.args.get('subscriber_id') + subscription_id = request.args.get('subscription_id') + page_size = request.args.get('page_size') + page = request.args.get('page') + if page_size: + page_size = int(page_size) + if page_size < 0: + return jsonify(message="The value of the page_size parameter must be greater than 0"), 400 + if page: + page = int(page) + if page < 0: + return jsonify(message="The value of the page parameter must be greater than 0"), 400 + + current_app.logger.debug(f"subscriber_id: {subscriber_id}, subscription_id: {subscription_id}, page: {page}, page_size: {page_size} ") + + return helper_operation.get_events(subscriber_id, subscription_id, page, page_size) + + +@helper_routes.route("/helper/deleteEntities/<uuid>", methods=["DELETE"]) +def deleteUserEntities(uuid): + return helper_operation.remove_entities(uuid) diff --git a/services/helper/helper_service/core/helper_operations.py b/services/helper/helper_service/core/helper_operations.py new file mode 100644 index 0000000000000000000000000000000000000000..9e6354675ae348efbd5b9f47c940f6145f371103 --- /dev/null +++ b/services/helper/helper_service/core/helper_operations.py @@ -0,0 +1,204 @@ +from flask import jsonify, current_app +import pymongo +from db.db import MongoDatabse +from config import Config +import requests +import os + +class HelperOperations: + + def __init__(self): + self.db = MongoDatabse() + self.mimetype = 'application/json' + self.config = Config().get_config() + + def get_invokers(self, uuid, invoker_id, page, page_size, sort_order): + current_app.logger.debug(f"Getting the invokers") + invoker_col = self.db.get_col_by_name(self.db.invoker_col) + + total_invokers = invoker_col.count_documents({}) + + filter = {} + if uuid: + filter["uuid"]=uuid + if invoker_id: + filter["api_invoker_id"]=invoker_id + + sort_direction = pymongo.DESCENDING if sort_order == "desc" else pymongo.ASCENDING + + if page_size and page: + index = (page - 1) * page_size + documents = invoker_col.find(filter,{"_id":0}).sort("onboarding_date", sort_direction).skip(index).limit(page_size) + pages = (total_invokers + page_size - 1) // page_size + else: + documents = invoker_col.find(filter,{"_id":0}).sort("onboarding_date", sort_direction) + pages = 1 + + list_invokers= list(documents) + long = len(list_invokers) + + return jsonify(message="Invokers returned successfully", + invokers=list_invokers, + total = total_invokers, + long = long, + totalPages = pages, + sortOrder = sort_order), 200 + + + def get_providers(self, uuid, provider_id, page, page_size, sort_order): + current_app.logger.debug(f"Getting the providers") + provider_col = self.db.get_col_by_name(self.db.provider_col) + + total_providers = provider_col.count_documents({}) + + filter = {} + if uuid: + filter["uuid"]=uuid + if provider_id: + filter["api_prov_dom_id"]=provider_id + + sort_direction = pymongo.DESCENDING if sort_order == "desc" else pymongo.ASCENDING + + if page_size and page: + index = (page - 1) * page_size + documents = provider_col.find(filter,{"_id":0}).sort("onboarding_date", sort_direction).skip(index).limit(page_size) + pages = (total_providers + page_size - 1) // page_size + else: + documents = provider_col.find(filter,{"_id":0}).sort("onboarding_date", sort_direction) + pages = 1 + + list_providers = list(documents) + long = len(list_providers) + + return jsonify(message="Providers returned successfully", + providers=list_providers, + total = total_providers, + long = long, + totalPages = pages, + sortOrder = sort_order), 200 + + def get_services(self, service_id, apf_id, api_name, page, page_size, sort_order): + current_app.logger.debug(f"Getting the services") + service_col = self.db.get_col_by_name(self.db.services_col) + + total_services = service_col.count_documents({}) + + filter = {} + if service_id: + filter["api_id"]=service_id + if apf_id: + filter["apf_id"]=apf_id + if api_name: + filter["api_name"]=api_name + + sort_direction = pymongo.DESCENDING if sort_order == "desc" else pymongo.ASCENDING + + if page_size and page: + index = (page - 1) * page_size + documents = service_col.find(filter,{"_id":0}).sort("onboarding_date", sort_direction).skip(index).limit(page_size) + pages = (total_services + page_size - 1) // page_size + else: + documents = service_col.find(filter,{"_id":0}).sort("onboarding_date", sort_direction) + pages = 1 + + list_services= list(documents) + long = len(list_services) + + return jsonify(message="Services returned successfully", + services=list_services, + total = total_services, + long = long, + totalPages = pages, + sortOrder = sort_order), 200 + + def get_security(self, invoker_id, page, page_size): + current_app.logger.debug(f"Getting the security context") + security_col = self.db.get_col_by_name(self.db.security_context_col) + + total_security = security_col.count_documents({}) + + filter = {} + + if invoker_id: + filter["api_invoker_id"]=invoker_id + + if page_size and page: + index = (page - 1) * page_size + documents = security_col.find(filter,{"_id":0}).skip(index).limit(page_size) + pages = (total_security + page_size - 1) // page_size + else: + documents = security_col.find(filter,{"_id":0}) + pages = 1 + + list_security= list(documents) + long = len(list_security) + + return jsonify(message="Security context returned successfully", + security=list_security, + total = total_security, + long = long, + totalPages = pages), 200 + + def get_events(self, subscriber_id, subscription_id, page, page_size): + current_app.logger.debug(f"Getting the events") + events_col = self.db.get_col_by_name(self.db.events) + + total_events = events_col.count_documents({}) + + filter = {} + + if subscriber_id: + filter["subscriber_id"]=subscriber_id + if subscription_id: + filter["subscription_id"]=subscription_id + + if page_size and page: + index = (page - 1) * page_size + documents = events_col.find(filter,{"_id":0}).skip(index).limit(page_size) + pages = (total_events + page_size - 1) // page_size + else: + documents = events_col.find(filter,{"_id":0}) + pages = 1 + + list_events= list(documents) + long = len(list_events) + + return jsonify(message="Events returned successfully", + events=list_events, + total = total_events, + long = long, + totalPages = pages), 200 + + def remove_entities(self, uuid): + + current_app.logger.debug(f"Removing entities for uuid: {uuid}") + invoker_col = self.db.get_col_by_name(self.db.invoker_col) + provider_col = self.db.get_col_by_name(self.db.provider_col) + + try: + if invoker_col.count_documents({'uuid':uuid}) == 0 and provider_col.count_documents({'uuid':uuid}) == 0: + current_app.logger.debug(f"No entities found for uuid: {uuid}") + return jsonify(message=f"No entities found for uuid: {uuid}"), 204 + + for invoker in invoker_col.find({'uuid':uuid}, {"_id":0}): + current_app.logger.debug(f"Removing Invoker: {invoker["api_invoker_id"]}") + url = 'https://{}/api-invoker-management/v1/onboardedInvokers/{}'.format(os.getenv('CAPIF_HOSTNAME'), invoker["api_invoker_id"]) + requests.request("DELETE", url, cert=( + '/usr/src/app/helper_service/certs/superadmin.crt', '/usr/src/app/helper_service/certs/superadmin.key'), verify='/usr/src/app/helper_service/certs/ca_root.crt') + + for provider in provider_col.find({'uuid':uuid}, {"_id":0}): + current_app.logger.debug(f"Removing Provider: {provider["api_prov_dom_id"]}") + url = 'https://{}/api-provider-management/v1/registrations/{}'.format(os.getenv('CAPIF_HOSTNAME'), provider["api_prov_dom_id"]) + + requests.request("DELETE", url, cert=( + '/usr/src/app/helper_service/certs/superadmin.crt', '/usr/src/app/helper_service/certs/superadmin.key'), verify='/usr/src/app/helper_service/certs/ca_root.crt') + except Exception as e: + current_app.logger.debug(f"Error deleting user entities: {e}") + jsonify(message=f"Error deleting user entities: {e}"), 500 + + current_app.logger.debug(f"User entities removed successfully") + return jsonify(message="User entities removed successfully"), 200 + + + + diff --git a/services/helper/helper_service/db/db.py b/services/helper/helper_service/db/db.py new file mode 100644 index 0000000000000000000000000000000000000000..bb2853ef6638b8d2294edbd8d7d1c2c225c8a061 --- /dev/null +++ b/services/helper/helper_service/db/db.py @@ -0,0 +1,45 @@ +import time +from pymongo import MongoClient +from pymongo.errors import AutoReconnect +from config import Config +from bson.codec_options import CodecOptions + +class MongoDatabse(): + + def __init__(self): + self.config = Config().get_config() + self.db = self.__connect() + self.invoker_col = self.config['mongo']['invoker_col'] + self.provider_col = self.config['mongo']['provider_col'] + self.services_col = self.config['mongo']['col_services'] + self.security_context_col = self.config['mongo']['col_security'] + self.events = self.config['mongo']['col_event'] + + + def get_col_by_name(self, name): + return self.db[name].with_options(codec_options=CodecOptions(tz_aware=True)) + + def __connect(self, max_retries=3, retry_delay=1): + + retries = 0 + + while retries < max_retries: + try: + uri = f"mongodb://{self.config['mongo']['user']}:{self.config['mongo']['password']}@" \ + f"{self.config['mongo']['host']}:{self.config['mongo']['port']}" + + + client = MongoClient(uri) + mydb = client[self.config['mongo']['db']] + mydb.command("ping") + return mydb + except AutoReconnect: + retries += 1 + print(f"Reconnecting... Retry {retries} of {max_retries}") + time.sleep(retry_delay) + return None + + def close_connection(self): + if self.db.client: + self.db.client.close() + diff --git a/services/helper/helper_service/wsgi.py b/services/helper/helper_service/wsgi.py new file mode 100644 index 0000000000000000000000000000000000000000..6026b0fa96078634d3455ab93d71dcdc78774276 --- /dev/null +++ b/services/helper/helper_service/wsgi.py @@ -0,0 +1,4 @@ +from app import app + +if __name__ == "__main__": + app.run() diff --git a/services/helper/requirements.txt b/services/helper/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..0e444da92643ea1dacd4f451aef1ccd8c1a7beb4 --- /dev/null +++ b/services/helper/requirements.txt @@ -0,0 +1,10 @@ +python_dateutil == 2.9.0.post0 +setuptools == 68.2.2 +Flask == 3.0.3 +pymongo == 4.0.1 +flask_jwt_extended == 4.6.0 +pyopenssl == 24.1.0 +pyyaml == 6.0.1 +requests == 2.32.2 +gunicorn==22.0.0 +packaging==24.0 diff --git a/services/mock_server/Dockerfile b/services/mock_server/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6d72bf569899e6512254be59539231c0fb49b338 --- /dev/null +++ b/services/mock_server/Dockerfile @@ -0,0 +1,21 @@ +# start by pulling the python image +FROM python:3.10.0-alpine + +# copy the requirements file into the image +COPY ./requirements.txt /app/requirements.txt + +# switch working directory +WORKDIR /app + +# install the dependencies and packages in the requirements file +RUN pip install -r requirements.txt + +# copy every content from the local file to the image +COPY . /app + +EXPOSE 9090 + +# configure the container to run in an executed manner +ENTRYPOINT [ "python" ] + +CMD ["mock_server.py" ] \ No newline at end of file diff --git a/services/mock_server/mock_server.py b/services/mock_server/mock_server.py new file mode 100644 index 0000000000000000000000000000000000000000..e45fae0f241f1801e7ddc9749399f4fa0bc5d796 --- /dev/null +++ b/services/mock_server/mock_server.py @@ -0,0 +1,56 @@ +from flask import Flask, request +import logging +from logging.handlers import RotatingFileHandler +import os + +app = Flask(__name__) + +# Lista para almacenar las solicitudes recibidas +requests_received = [] + +def verbose_formatter(): + return logging.Formatter( + '{"timestamp": "%(asctime)s", "level": "%(levelname)s", "logger": "%(name)s", "function": "%(funcName)s", "line": %(lineno)d, "message": %(message)s}', + datefmt='%d/%m/%Y %H:%M:%S' + ) + + +def configure_logging(app): + del app.logger.handlers[:] + loggers = [app.logger, ] + handlers = [] + console_handler = logging.StreamHandler() + console_handler.setLevel(logging.DEBUG) + console_handler.setFormatter(verbose_formatter()) + file_handler = RotatingFileHandler(filename="mock_server.log", maxBytes=1024 * 1024 * 100, backupCount=20) + file_handler.setLevel(logging.DEBUG) + file_handler.setFormatter(verbose_formatter()) + handlers.append(console_handler) + handlers.append(file_handler) + + + for l in loggers: + for handler in handlers: + l.addHandler(handler) + l.propagate = False + l.setLevel(logging.DEBUG) + +@app.route('/testing', methods=['POST', 'GET']) +def index(): + if request.method == 'POST': + app.logger.debug(request.json) + app.logger.debug(request.headers) + requests_received.append(request.json) + return 'Mock Server is running' + +@app.route('/requests_list', methods=['GET','DELETE']) +def requests_list(): + if request.method == 'DELETE': + requests_received.clear() + return requests_received + + +configure_logging(app) + +if __name__ == '__main__': + app.run(host=os.environ.get("IP",'0.0.0.0'),port=os.environ.get("PORT",9090),debug=True) diff --git a/services/mock_server/requirements.txt b/services/mock_server/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..abf28620ee482efbabd6ccf8a2b8d8b554563b03 --- /dev/null +++ b/services/mock_server/requirements.txt @@ -0,0 +1 @@ +flask==3.0.3 \ No newline at end of file diff --git a/services/nginx/nginx.conf b/services/nginx/nginx.conf index d30ded7b2eb1863cc072284e1f6c5727bf849ef1..ec70d576977be2d61f633f8888ff392d60f1b322 100644 --- a/services/nginx/nginx.conf +++ b/services/nginx/nginx.conf @@ -12,6 +12,10 @@ http { default ""; ~(^|,)CN=(?<CN>[^,]+) $CN; } + map "$request_method:$uri:$ssl_client_s_dn_cn" $helper_error_message { + default 'SUCCESS'; + "~*(GET|DELETE):.*:(?!(superadmin))(.*)" '{"status":401, "title":"Unauthorized" ,"detail":"Role not authorized for this API route", "cause":"User role must be superadmin"}'; + } map "$request_method:$uri:$ssl_client_s_dn_cn" $invoker_error_message { default 'SUCCESS'; "~*(PUT|DELETE):.*:(?!(INV|superadmin))(.*)" '{"status":401, "title":"Unauthorized" ,"detail":"Role not authorized for this API route", "cause":"User role must be invoker"}'; @@ -163,6 +167,15 @@ http { proxy_pass http://access-control-policy:8080; } + location /helper { + if ( $helper_error_message != SUCCESS ) { + add_header Content-Type 'application/problem+json'; + return 401 $helper_error_message; + } + proxy_set_header X-SSL-Client-Cert $ssl_client_cert; + proxy_pass http://helper:8080; + } + } } diff --git a/services/register/config.yaml b/services/register/config.yaml index bc1370fd86805cf64bba5f7063b502ce2aa0432c..f63df9fe95452c0443dce72e8934dc2d4998d8fc 100644 --- a/services/register/config.yaml +++ b/services/register/config.yaml @@ -3,6 +3,7 @@ mongo: { 'password': 'example', 'db': 'capif_users', 'col': 'user', + 'admins': 'admins', 'host': 'mongo_register', 'port': '27017' } @@ -16,5 +17,6 @@ register: { register_uuid: '6ba7b810-9dad-11d1-80b4-00c04fd430c8', refresh_expiration: 30, #days token_expiration: 10, #mins - admin_users: {admin: "password123"} + admin_users: {admin_user: "admin", + admin_pass: "password123"} } \ No newline at end of file diff --git a/services/register/pytest.ini b/services/register/pytest.ini new file mode 100644 index 0000000000000000000000000000000000000000..62fc4e2aa1169ef0638ebceab8d77e0cabe9d67a --- /dev/null +++ b/services/register/pytest.ini @@ -0,0 +1,4 @@ +# content of pytest.ini +[pytest] +python_files = register_services/config.py + diff --git a/services/register/register_service/app.py b/services/register/register_service/app.py index cca3cfb3c61614a3b2ac66d4c8edef66f3e0f793..a4b0a50014609efc85addf328b8f685dfb885c99 100644 --- a/services/register/register_service/app.py +++ b/services/register/register_service/app.py @@ -6,6 +6,8 @@ from OpenSSL.crypto import PKey, TYPE_RSA, X509Req, dump_certificate_request, FI import requests import json from config import Config +from db.db import MongoDatabse + app = Flask(__name__) @@ -71,6 +73,11 @@ response = requests.request("GET", url, headers=headers, verify = False) key_data = json.loads(response.text)["data"]["data"]["key"] +# Create an Admin in the Admin Collection +client = MongoDatabse() +if not client.get_col_by_name(client.capif_admins).find_one({"admin_name": config["register"]["admin_users"]["admin_user"], "admin_pass": config["register"]["admin_users"]["admin_pass"]}): + client.get_col_by_name(client.capif_admins).insert_one({"admin_name": config["register"]["admin_users"]["admin_user"], "admin_pass": config["register"]["admin_users"]["admin_pass"]}) + app.config['JWT_ALGORITHM'] = 'RS256' app.config['JWT_PRIVATE_KEY'] = key_data diff --git a/services/register/register_service/controllers/register_controller.py b/services/register/register_service/controllers/register_controller.py index 58620baf6950f0ff245c58e6b863dfe431e03d7a..da354690d65706959068276a7e6b81f0cffc3a9d 100644 --- a/services/register/register_service/controllers/register_controller.py +++ b/services/register/register_service/controllers/register_controller.py @@ -7,6 +7,7 @@ from config import Config from functools import wraps from datetime import datetime, timedelta +from db.db import MongoDatabse from flask_httpauth import HTTPBasicAuth import jwt @@ -36,7 +37,9 @@ def generate_tokens(username): @auth.verify_password def verify_password(username, password): users = register_operation.get_users()[0].json["users"] - if username in config["register"]["admin_users"] and password == config["register"]["admin_users"][username]: + client = MongoDatabse() + admin = client.get_col_by_name(client.capif_admins).find_one({"admin_name": username, "admin_pass": password}) + if admin: return username, "admin" for user in users: if user["username"] == username and user["password"]==password: @@ -86,12 +89,40 @@ def refresh_token(username): @register_routes.route("/createUser", methods=["POST"]) @admin_required() def register(username): - username = request.json["username"] - password = request.json["password"] - description = request.json["description"] - email = request.json["email"] + required_fields = { + "username": str, + "password": str, + "enterprise": str, + "country": str, + "email": str, + "purpose": str + } + + optional_fields = { + "phone_number": str, + "company_web": str, + "description": str + } + + user_info = request.get_json() + + missing_fields = [] + for field, field_type in required_fields.items(): + if field not in user_info: + missing_fields.append(field) + elif not isinstance(user_info[field], field_type): + return jsonify({"error": f"Field '{field}' must be of type {field_type.__name__}"}), 400 + + for field, field_type in optional_fields.items(): + if field in user_info and not isinstance(user_info[field], field_type): + return jsonify({"error": f"Optional field '{field}' must be of type {field_type.__name__}"}), 400 + if field not in user_info: + user_info[field] = None + + if missing_fields: + return jsonify({"error": "Missing required fields", "fields": missing_fields}), 400 - return register_operation.register_user(username, password, description, email) + return register_operation.register_user(user_info) @register_routes.route("/getauth", methods=["GET"]) @auth.login_required diff --git a/services/register/register_service/core/register_operations.py b/services/register/register_service/core/register_operations.py index cf5219d759e955551fcd260899a2c65be7a497cc..a420fb88f45ddfc0e377c8dc59bf56b696dc51f9 100644 --- a/services/register/register_service/core/register_operations.py +++ b/services/register/register_service/core/register_operations.py @@ -3,8 +3,10 @@ from flask_jwt_extended import create_access_token from db.db import MongoDatabse from datetime import datetime from config import Config +import requests import uuid + class RegisterOperations: def __init__(self): @@ -12,17 +14,18 @@ class RegisterOperations: self.mimetype = 'application/json' self.config = Config().get_config() - def register_user(self, username, password, description, email): + def register_user(self, user_info): mycol = self.db.get_col_by_name(self.db.capif_users) - exist_user = mycol.find_one({"username": username}) + exist_user = mycol.find_one({"username": user_info["username"]}) if exist_user: return jsonify("user already exists"), 409 name_space = uuid.UUID(self.config["register"]["register_uuid"]) - user_uuid = str(uuid.uuid5(name_space, username)) + user_uuid = str(uuid.uuid5(name_space,user_info["username"])) - user_info = dict(uuid=user_uuid, username=username, password=password, description=description, email=email, onboarding_date=datetime.now()) + user_info["uuid"] = user_uuid + user_info["onboarding_date"]=datetime.now() obj = mycol.insert_one(user_info) return jsonify(message="User registered successfully", uuid=user_uuid), 201 @@ -60,11 +63,11 @@ class RegisterOperations: mycol = self.db.get_col_by_name(self.db.capif_users) try: - mycol.delete_one({"uuid": uuid}) - - # Request to the helper to delete invokers and providers - + url = f"https://capifcore/helper/deleteEntities/{uuid}" + requests.delete(url, cert=("register_service/certs/superadmin.crt", "register_service/certs/superadmin.key"), verify="register_service/certs/ca_root.crt") + + mycol.delete_one({"uuid": uuid}) return jsonify(message="User removed successfully"), 204 except Exception as e: diff --git a/services/register/register_service/db/db.py b/services/register/register_service/db/db.py index 9e64103b191e766c5ddcbd5fd474ca1e233ed3c0..fdf448e5be3b675d585bccf141b8e2a6ffe67754 100644 --- a/services/register/register_service/db/db.py +++ b/services/register/register_service/db/db.py @@ -10,6 +10,7 @@ class MongoDatabse(): self.config = Config().get_config() self.db = self.__connect() self.capif_users = self.config['mongo']['col'] + self.capif_admins = self.config['mongo']['admins'] def get_col_by_name(self, name): diff --git a/services/run.sh b/services/run.sh index 83011c34414af3725c56b86640b6e50591298276..4465aaa49909052f767c9d0bd1e6aeacfe3dfad2 100755 --- a/services/run.sh +++ b/services/run.sh @@ -3,6 +3,7 @@ help() { echo "Usage: $1 <options>" echo " -c : Setup different hostname for capif" + echo " -s : Run Mock server" echo " -m : Clean monitoring service" echo " -h : show this help" exit 1 @@ -12,10 +13,14 @@ HOSTNAME=capifcore MONITORING_STATE=false DEPLOY=all -#Needed to avoid write permissions on bind volumes with prometheus and grafana +# Needed to avoid write permissions on bind volumes with prometheus and grafana DUID=$(id -u) DGID=$(id -g) +# Mock Server configuration +IP=0.0.0.0 +PORT=9090 + # Get docker compose version docker_version=$(docker compose version --short | cut -d',' -f1) IFS='.' read -ra version_components <<< "$docker_version" @@ -28,7 +33,7 @@ else fi # Read params -while getopts ":c:mh" opt; do +while getopts ":c:msh" opt; do case $opt in c) HOSTNAME="$OPTARG" @@ -36,6 +41,9 @@ while getopts ":c:mh" opt; do m) MONITORING_STATE=true ;; + s) + ROBOT_MOCK_SERVER=true + ;; h) help ;; @@ -97,6 +105,21 @@ if [ $status -eq 0 ]; then echo "*** Register Service are running ***" else echo "*** Register Service failed to start ***" + exit $status +fi + +if [ "$ROBOT_MOCK_SERVER" == "true" ] ; then + echo '***Robot Mock Server set as true***' + echo '***Creating Robot Mock Server stack***' + + IP=$IP PORT=$PORT docker compose -f "docker-compose-mock-server.yml" up --detach + status=$? + if [ $status -eq 0 ]; then + echo "*** Monitoring Stack Runing ***" + else + echo "*** Monitoring Stack failed to start ***" + exit $status + fi fi exit $status diff --git a/services/run_capif_tests.sh b/services/run_capif_tests.sh index b73893a45ca40cacecb4dca088999461a9de55de..7762fcd8f016a5ca227599b310cef5523ce577e5 100755 --- a/services/run_capif_tests.sh +++ b/services/run_capif_tests.sh @@ -9,13 +9,34 @@ RESULT_FOLDER=$REPOSITORY_BASE_FOLDER/results ROBOT_DOCKER_FILE_FOLDER=$REPOSITORY_BASE_FOLDER/tools/robot # nginx Hostname and http port (80 by default) to reach for tests +# CAPIF_REGISTER=registercapif.mobilesandbox.cloud +CAPIF_REGISTER=capifcore +# CAPIF_REGISTER_PORT=37211 +CAPIF_REGISTER_PORT=8084 +# CAPIF_HOSTNAME=capif.mobilesandbox.cloud CAPIF_HOSTNAME=capifcore CAPIF_HTTP_PORT=8080 CAPIF_HTTPS_PORT=443 -echo "HOSTNAME = $CAPIF_HOSTNAME" +# VAULT access configuration +# CAPIF_VAULT=vault.5gnacar.int +CAPIF_VAULT=vault +CAPIF_VAULT_PORT=8200 +# CAPIF_VAULT_TOKEN=dev-only-token +CAPIF_VAULT_TOKEN=read-ca-token + + +MOCK_SERVER_URL=http://mock-server:9090 + + +echo "CAPIF_HOSTNAME = $CAPIF_HOSTNAME" +echo "CAPIF_REGISTER = $CAPIF_REGISTER" echo "CAPIF_HTTP_PORT = $CAPIF_HTTP_PORT" echo "CAPIF_HTTPS_PORT = $CAPIF_HTTPS_PORT" +echo "CAPIF_VAULT = $CAPIF_VAULT" +echo "CAPIF_VAULT_PORT = $CAPIF_VAULT_PORT" +echo "CAPIF_VAULT_TOKEN = $CAPIF_VAULT_TOKEN" +echo "MOCK_SERVER_URL = $MOCK_SERVER_URL" docker >/dev/null 2>/dev/null if [[ $? -ne 0 ]] @@ -45,8 +66,15 @@ docker run -ti --rm --network="host" \ --add-host host.docker.internal:host-gateway \ --add-host vault:host-gateway \ --add-host register:host-gateway \ + --add-host mock-server:host-gateway \ -v $TEST_FOLDER:/opt/robot-tests/tests \ -v $RESULT_FOLDER:/opt/robot-tests/results ${DOCKER_ROBOT_IMAGE}:${DOCKER_ROBOT_IMAGE_VERSION} \ --variable CAPIF_HOSTNAME:$CAPIF_HOSTNAME \ --variable CAPIF_HTTP_PORT:$CAPIF_HTTP_PORT \ - --variable CAPIF_HTTPS_PORT:$CAPIF_HTTPS_PORT $@ + --variable CAPIF_HTTPS_PORT:$CAPIF_HTTPS_PORT \ + --variable CAPIF_REGISTER:$CAPIF_REGISTER \ + --variable CAPIF_REGISTER_PORT:$CAPIF_REGISTER_PORT \ + --variable CAPIF_VAULT:$CAPIF_VAULT \ + --variable CAPIF_VAULT_PORT:$CAPIF_VAULT_PORT \ + --variable CAPIF_VAULT_TOKEN:$CAPIF_VAULT_TOKEN \ + --variable MOCK_SERVER_URL:$MOCK_SERVER_URL $@ diff --git a/services/run_mock_server.sh b/services/run_mock_server.sh new file mode 100755 index 0000000000000000000000000000000000000000..5a194c469371de592d4f63ec5e8bbfc59598489c --- /dev/null +++ b/services/run_mock_server.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +help() { + echo "Usage: $1 <options>" + echo " -i : Setup different host ip for mock server (default 0.0.0.0)" + echo " -p : Setup different port for mock server (default 9090)" + echo " -h : show this help" + exit 1 +} + +IP=0.0.0.0 +PORT=9090 + +# Read params +while getopts ":i:p:h" opt; do + case $opt in + i) + IP="$OPTARG" + ;; + p) + PORT=$OPTARG + ;; + h) + help + ;; + \?) + echo "Not valid option: -$OPTARG" >&2 + help + ;; + :) + echo "The -$OPTARG option requires an argument." >&2 + help + ;; + esac +done + +echo Robot Framework Mock Server will listen on $IP:$PORT + +IP=$IP PORT=$PORT docker compose -f "docker-compose-mock-server.yml" up --detach --build + +status=$? +if [ $status -eq 0 ]; then + echo "*** All Capif services are running ***" +else + echo "*** Some Capif services failed to start ***" + exit $status +fi diff --git a/services/show_logs.sh b/services/show_logs.sh index b53641dd216e3cb43d9cd4ca0eaca85567ca60e4..c2bcf52d90607d3da9364c11c44c99d4bf209071 100755 --- a/services/show_logs.sh +++ b/services/show_logs.sh @@ -5,6 +5,7 @@ help() { echo " -c : Show capif services" echo " -v : Show vault service" echo " -r : Show register service" + echo " -s : Show Robot Mock Server service" echo " -m : Show monitoring service" echo " -a : Show all services" echo " -f : Follow log output" @@ -23,7 +24,7 @@ echo "${FILES[@]}" FOLLOW="" # Read params -while getopts "cvrahmf" opt; do +while getopts "cvrahmfs" opt; do case $opt in c) echo "Show Capif services" @@ -37,13 +38,17 @@ while getopts "cvrahmf" opt; do echo "Show register service" FILES+=("-f docker-compose-register.yml") ;; + s) + echo "Show Mock Server service" + FILES+=("-f docker-compose-mock-server.yml") + ;; m) echo "Show monitoring service" FILES+=("-f ../monitoring/docker-compose.yml") ;; a) echo "Show all services" - FILES=("-f docker-compose-capif.yml" -f "docker-compose-vault.yml" -f "docker-compose-register.yml" -f "../monitoring/docker-compose.yml") + FILES=("-f docker-compose-capif.yml" -f "docker-compose-vault.yml" -f "docker-compose-register.yml" -f "docker-compose-mock-server.yml" -f "../monitoring/docker-compose.yml") ;; f) echo "Setup follow logs" diff --git a/tests/features/CAPIF Api Access Control Policy/capif_api_access_control_policy.robot b/tests/features/CAPIF Api Access Control Policy/capif_api_access_control_policy.robot index 04d789839c2fb6ed6212ede4fd1abf5228b09a55..ec0f0821b4ae9eff2896b2e6dcd18dc9874137eb 100644 --- a/tests/features/CAPIF Api Access Control Policy/capif_api_access_control_policy.robot +++ b/tests/features/CAPIF Api Access Control Policy/capif_api_access_control_policy.robot @@ -238,7 +238,6 @@ Retrieve ACL with security context created by two different Invokers ... username=${AEF_PROVIDER_USERNAME} Check Response Variable Type And Values ${resp} 200 AccessControlPolicyList - # Check returned values Should Not Be Empty ${resp.json()['apiInvokerPolicies']} Length Should Be ${resp.json()['apiInvokerPolicies']} 2 diff --git a/tests/features/CAPIF Api Events/capif_events_api.robot b/tests/features/CAPIF Api Events/capif_events_api.robot index f2a596689598ea7761ca25304e4e8f0e469f6c8e..d5e02d26d1b9d7592d5cc84e489bfecf2bc1dd0e 100644 --- a/tests/features/CAPIF Api Events/capif_events_api.robot +++ b/tests/features/CAPIF Api Events/capif_events_api.robot @@ -2,6 +2,7 @@ Resource /opt/robot-tests/tests/resources/common.resource Library /opt/robot-tests/tests/libraries/bodyRequests.py Library XML +Library String Resource /opt/robot-tests/tests/resources/common/basicRequests.robot Resource ../../resources/common.resource @@ -137,3 +138,665 @@ Deletes an individual CAPIF Event Subscription with invalid SubscriptionId ... title=Unauthorized ... detail=User not authorized ... cause=You are not the owner of this resource + +Invoker receives Service API Invocation events + [Tags] capif_api_events-6 mockserver + + # Initialize Mock server + Init Mock Server + + # Register APF + ${register_user_info}= Provider Default Registration + + # Publish one api + Publish Service Api ${register_user_info} + + # Register INVOKER + ${register_user_info_invoker} ${url} ${request_body}= Invoker Default Onboarding + + ${discover_response}= Get Request Capif + ... ${DISCOVER_URL}${register_user_info_invoker['api_invoker_id']} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + ${api_ids} ${api_names}= Get Api Ids And Names From Discover Response ${discover_response} + + # Subscribe to events + ${events_list}= Create List SERVICE_API_INVOCATION_SUCCESS SERVICE_API_INVOCATION_FAILURE + ${aef_ids}= Create List ${register_user_info['aef_id']} + ${event_filter}= Create Capif Event Filter aefIds=${aef_ids} + ${event_filters}= Create List ${event_filter} + + ${request_body}= Create Events Subscription + ... events=@{events_list} + ... notificationDestination=${MOCK_SERVER_URL}/testing + ... eventFilters=${event_filters} + ${resp}= Post Request Capif + ... /capif-events/v1/${register_user_info_invoker['api_invoker_id']}/subscriptions + ... json=${request_body} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + # Check Results + Check Response Variable Type And Values ${resp} 201 EventSubscription + ${subscriber_id} ${subscription_id}= Check Event Location Header ${resp} + + # Create Log Entry, emulate success and failure api invocation + ${results}= Create List 200 400 + ${request_body}= Create Log Entry + ... ${register_user_info['aef_id']} + ... ${register_user_info_invoker['api_invoker_id']} + ... ${api_ids} + ... ${api_names} + ... results=${results} + ${resp}= Post Request Capif + ... /api-invocation-logs/v1/${register_user_info['aef_id']}/logs + ... json=${request_body} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${AEF_PROVIDER_USERNAME} + + # Check Results + Check Response Variable Type And Values ${resp} 201 InvocationLog + ${resource_url}= Check Location Header ${resp} ${LOCATION_LOGGING_RESOURCE_REGEX} + + # Check Event Notifications + ## Create check Events to ensure all notifications were received + ${events_expected}= Create Events From InvocationLogs + ... ${subscription_id} + ... ${request_body} + ## Check Events Expected towards received notifications at mock server + Check Mock Server Notification Events ${events_expected} + +Invoker subscribe to Service API Available and Unavailable events + [Tags] capif_api_events-7 mockserver + + # Initialize Mock server + Init Mock Server + + # Register APF + ${register_user_info_provider}= Provider Default Registration + + # Publish one api + ${service_api_description_published_1} ${resource_url_1} ${request_body}= Publish Service Api + ... ${register_user_info_provider} + + # Register INVOKER + ${register_user_info_invoker} ${url} ${request_body}= Invoker Default Onboarding + + ${discover_response}= Get Request Capif + ... ${DISCOVER_URL}${register_user_info_invoker['api_invoker_id']} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + ${api_ids} ${api_names}= Get Api Ids And Names From Discover Response ${discover_response} + + # Subscribe to events + ${events_list}= Create List SERVICE_API_AVAILABLE SERVICE_API_UNAVAILABLE + ${aef_ids}= Create List ${register_user_info_provider['aef_id']} + ${event_filter}= Create Capif Event Filter aefIds=${aef_ids} + ${event_filters}= Create List ${event_filter} + + ${request_body}= Create Events Subscription + ... events=@{events_list} + ... notificationDestination=${MOCK_SERVER_URL}/testing + ... eventFilters=${event_filters} + ${resp}= Post Request Capif + ... /capif-events/v1/${register_user_info_invoker['api_invoker_id']}/subscriptions + ... json=${request_body} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + # Check Results + Check Response Variable Type And Values ${resp} 201 EventSubscription + ${subscriber_id} ${subscription_id}= Check Event Location Header ${resp} + + # Provider publish new API + ${service_api_description_published_2} ${resource_url_2} ${request_body}= Publish Service Api + ... ${register_user_info_provider} + ... service_2 + + # Provider Remove service_1 published API + ${resp}= Delete Request Capif + ... ${resource_url_1.path} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${APF_PROVIDER_USERNAME} + + Status Should Be 204 ${resp} + + # Check Event Notifications + ## Create check Events to ensure all notifications were received + ${service_api_available_resources}= Create List ${resource_url_2} + ${service_api_unavailable_resources}= Create List ${resource_url_1} + ${events_expected}= Create Expected Events For Service API Notifications + ... subscription_id=${subscription_id} + ... service_api_available_resources=${service_api_available_resources} + ... service_api_unavailable_resources=${service_api_unavailable_resources} + ## Check Events Expected towards received notifications at mock server + Check Mock Server Notification Events ${events_expected} + +Invoker subscribe to Service API Update + [Tags] capif_api_events-8 mockserver + + # Initialize Mock server + Init Mock Server + + # Register APF + ${register_user_info_provider}= Provider Default Registration + + # Publish one api + ${service_api_description_published} ${resource_url} ${request_body}= Publish Service Api + ... ${register_user_info_provider} + + # Register INVOKER + ${register_user_info_invoker} ${url} ${request_body}= Invoker Default Onboarding + + ${discover_response}= Get Request Capif + ... ${DISCOVER_URL}${register_user_info_invoker['api_invoker_id']} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + ${api_ids} ${api_names}= Get Api Ids And Names From Discover Response ${discover_response} + + # Subscribe to events + ${events_list}= Create List SERVICE_API_UPDATE + ${aef_ids}= Create List ${register_user_info_provider['aef_id']} + ${event_filter}= Create Capif Event Filter aefIds=${aef_ids} + ${event_filters}= Create List ${event_filter} + + ${request_body}= Create Events Subscription + ... events=@{events_list} + ... notificationDestination=${MOCK_SERVER_URL}/testing + ... eventFilters=${event_filters} + ${resp}= Post Request Capif + ... /capif-events/v1/${register_user_info_invoker['api_invoker_id']}/subscriptions + ... json=${request_body} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + # Check Results + Check Response Variable Type And Values ${resp} 201 EventSubscription + ${subscriber_id} ${subscription_id}= Check Event Location Header ${resp} + + # Update Service API + ${service_api_description_modified}= Create Service Api Description service_1_modified + ${resp}= Put Request Capif + ... ${resource_url.path} + ... json=${service_api_description_modified} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${APF_PROVIDER_USERNAME} + + Check Response Variable Type And Values ${resp} 200 ServiceAPIDescription + ... apiName=service_1_modified + + # Check Event Notifications + ## Create check Events to ensure all notifications were received + ${events_expected}= Create Expected Service Update Event ${subscription_id} ${resource_url} ${service_api_description_modified} + ## Check Events Expected towards received notifications at mock server + Check Mock Server Notification Events ${events_expected} + +Provider subscribe to API Invoker events + [Tags] capif_api_events-9 mockserver + + # Initialize Mock server + Init Mock Server + + # Register APF + ${register_user_info_provider}= Provider Default Registration + + # Subscribe to events + ${events_list}= Create List API_INVOKER_ONBOARDED API_INVOKER_UPDATED API_INVOKER_OFFBOARDED + ${request_body}= Create Events Subscription + ... events=@{events_list} + ... notificationDestination=${MOCK_SERVER_URL}/testing + ${resp}= Post Request Capif + ... /capif-events/v1/${register_user_info_provider['amf_id']}/subscriptions + ... json=${request_body} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${AMF_PROVIDER_USERNAME} + + # Check Results + Check Response Variable Type And Values ${resp} 201 EventSubscription + ${subscriber_id} ${subscription_id}= Check Event Location Header ${resp} + + # Register INVOKER + ${register_user_info_invoker} ${url} ${request_body}= Invoker Default Onboarding + + # Update Invoker onboarded information + ${new_notification_destination}= Set Variable + ... http://${CAPIF_CALLBACK_IP}:${CAPIF_CALLBACK_PORT}/netapp_new_callback + Set To Dictionary + ... ${request_body} + ... notificationDestination=${new_notification_destination} + ${resp}= Put Request Capif + ... ${url.path} + ... ${request_body} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + # Check Update + Check Response Variable Type And Values ${resp} 200 APIInvokerEnrolmentDetails + ... notificationDestination=${new_notification_destination} + + # Remove Invoker from CCF + ${resp}= Delete Request Capif + ... ${url.path} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + Call Method ${CAPIF_USERS} remove_capif_users_entry ${url.path} + + # Check Remove + Should Be Equal As Strings ${resp.status_code} 204 + + # Check Event Notifications + ## Create check Events to ensure all notifications were received + ${events_expected}= Create Expected Api Invoker Events + ... ${subscription_id} + ... ${register_user_info_invoker['api_invoker_id']} + ## Check Events Expected towards received notifications at mock server + Check Mock Server Notification Events ${events_expected} + +Invoker subscribed to ACL update event + [Tags] capif_api_events-10 mockserver + + # Initialize Mock server + Init Mock Server + + # Register APF + ${register_user_info_provider}= Provider Default Registration + + # Publish one api + ${service_api_description_published} ${resource_url} ${request_body}= Publish Service Api + ... ${register_user_info_provider} + + # Store apiId1 + ${service_api_id}= Set Variable ${service_api_description_published['apiId']} + + # Register INVOKER + ${register_user_info_invoker} ${url} ${request_body}= Invoker Default Onboarding + + # Subscribe to events + ${events_list}= Create List ACCESS_CONTROL_POLICY_UPDATE + ${request_body}= Create Events Subscription + ... events=@{events_list} + ... notificationDestination=${MOCK_SERVER_URL}/testing + ${resp}= Post Request Capif + ... /capif-events/v1/${register_user_info_provider['amf_id']}/subscriptions + ... json=${request_body} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${AMF_PROVIDER_USERNAME} + + # Check Results + Check Response Variable Type And Values ${resp} 201 EventSubscription + ${subscriber_id} ${subscription_id}= Check Event Location Header ${resp} + + # Test + ${discover_response}= Get Request Capif + ... ${DISCOVER_URL}${register_user_info_invoker['api_invoker_id']}&aef-id=${register_user_info_provider['aef_id']} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + Check Response Variable Type And Values ${discover_response} 200 DiscoveredAPIs + + # create Security Context + ${request_service_security_body}= Create Service Security From Discover Response + ... http://${CAPIF_HOSTNAME}:${CAPIF_HTTP_PORT}/test + ... ${discover_response} + ${resp}= Put Request Capif + ... /capif-security/v1/trustedInvokers/${register_user_info_invoker['api_invoker_id']} + ... json=${request_service_security_body} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + # Check Service Security + Check Response Variable Type And Values ${resp} 201 ServiceSecurity + ${resource_url}= Check Location Header ${resp} ${LOCATION_SECURITY_RESOURCE_REGEX} + + ${resp}= Get Request Capif + ... /access-control-policy/v1/accessControlPolicyList/${service_api_id}?aef-id=${register_user_info_provider['aef_id']} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${AEF_PROVIDER_USERNAME} + + Check Response Variable Type And Values ${resp} 200 AccessControlPolicyList + # Check returned values + Should Not Be Empty ${resp.json()['apiInvokerPolicies']} + Length Should Be ${resp.json()['apiInvokerPolicies']} 1 + Should Be Equal As Strings + ... ${resp.json()['apiInvokerPolicies'][0]['apiInvokerId']} + ... ${register_user_info_invoker['api_invoker_id']} + + ${api_invoker_policies}= Set Variable ${resp.json()['apiInvokerPolicies']} + + # Check Event Notifications + ## Create check Events to ensure all notifications were received + ${events_expected}= Create Expected Access Control Policy Update Event + ... ${subscription_id} + ... ${service_api_id} + ... ${api_invoker_policies} + ## Check Events Expected towards received notifications at mock server + Check Mock Server Notification Events ${events_expected} + +Provider receives an ACL unavailable event when invoker remove Security Context. + [Tags] capif_api_events-11 mockserver + + # Initialize Mock server + Init Mock Server + + # Register APF + ${register_user_info_provider}= Provider Default Registration + + # Publish one api + ${service_api_description_published} ${resource_url} ${request_body}= Publish Service Api + ... ${register_user_info_provider} + + # Store apiId1 + ${serviceApiId}= Set Variable ${service_api_description_published['apiId']} + + # Register INVOKER + ${register_user_info_invoker} ${url} ${request_body}= Invoker Default Onboarding + + # Subscribe to events + ${events_list}= Create List ACCESS_CONTROL_POLICY_UNAVAILABLE + ${request_body}= Create Events Subscription + ... events=@{events_list} + ... notificationDestination=${MOCK_SERVER_URL}/testing + ${resp}= Post Request Capif + ... /capif-events/v1/${register_user_info_provider['amf_id']}/subscriptions + ... json=${request_body} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + # Check Results + Check Response Variable Type And Values ${resp} 201 EventSubscription + ${subscriber_id} ${subscription_id}= Check Event Location Header ${resp} + + # Test + ${discover_response}= Get Request Capif + ... ${DISCOVER_URL}${register_user_info_invoker['api_invoker_id']}&aef-id=${register_user_info_provider['aef_id']} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + Check Response Variable Type And Values ${discover_response} 200 DiscoveredAPIs + + # create Security Context + ${request_service_security_body}= Create Service Security From Discover Response + ... http://${CAPIF_HOSTNAME}:${CAPIF_HTTP_PORT}/test + ... ${discover_response} + ${resp}= Put Request Capif + ... /capif-security/v1/trustedInvokers/${register_user_info_invoker['api_invoker_id']} + ... json=${request_service_security_body} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + # Check Service Security + Check Response Variable Type And Values ${resp} 201 ServiceSecurity + ${resource_url}= Check Location Header ${resp} ${LOCATION_SECURITY_RESOURCE_REGEX} + + # Remove Security Context by Provider + ${resp}= Delete Request Capif + ... /capif-security/v1/trustedInvokers/${register_user_info_invoker['api_invoker_id']} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${AEF_PROVIDER_USERNAME} + + Status Should Be 204 ${resp} + + # Check Event Notifications + ## Create check Events to ensure all notifications were received + ${events_expected}= Create Expected Access Control Policy Unavailable ${subscription_id} + ## Check Events Expected towards received notifications at mock server + Check Mock Server Notification Events ${events_expected} + +Invoker receives an Invoker Authorization Revoked and ACL unavailable event when Provider revoke Invoker Authorization. + [Tags] capif_api_events-12 mockserver + + # Initialize Mock server + Init Mock Server + + # Register APF + ${register_user_info_provider}= Provider Default Registration + + # Publish one api + ${service_api_description_published} ${resource_url} ${request_body}= Publish Service Api + ... ${register_user_info_provider} + + # Store apiId1 + ${serviceApiId}= Set Variable ${service_api_description_published['apiId']} + + # Register INVOKER + ${register_user_info_invoker} ${url} ${request_body}= Invoker Default Onboarding + + # Subscribe to events + ${events_list}= Create List ACCESS_CONTROL_POLICY_UNAVAILABLE API_INVOKER_AUTHORIZATION_REVOKED + ${request_body}= Create Events Subscription + ... events=@{events_list} + ... notificationDestination=${MOCK_SERVER_URL}/testing + ${resp}= Post Request Capif + ... /capif-events/v1/${register_user_info_provider['amf_id']}/subscriptions + ... json=${request_body} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + # Check Results + Check Response Variable Type And Values ${resp} 201 EventSubscription + ${subscriber_id} ${subscription_id}= Check Event Location Header ${resp} + + # Test + ${discover_response}= Get Request Capif + ... ${DISCOVER_URL}${register_user_info_invoker['api_invoker_id']}&aef-id=${register_user_info_provider['aef_id']} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + Check Response Variable Type And Values ${discover_response} 200 DiscoveredAPIs + + ${api_ids}= Get Api Ids From Discover Response ${discover_response} + + # create Security Context + ${request_service_security_body}= Create Service Security From Discover Response + ... http://${CAPIF_HOSTNAME}:${CAPIF_HTTP_PORT}/test + ... ${discover_response} + ${resp}= Put Request Capif + ... /capif-security/v1/trustedInvokers/${register_user_info_invoker['api_invoker_id']} + ... json=${request_service_security_body} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${INVOKER_USERNAME} + + # Check Service Security + Check Response Variable Type And Values ${resp} 201 ServiceSecurity + ${resource_url}= Check Location Header ${resp} ${LOCATION_SECURITY_RESOURCE_REGEX} + + # Revoke Security Context by Provider + ${request_body}= Create Security Notification Body + ... ${register_user_info_invoker['api_invoker_id']} + ... ${api_ids} + ${resp}= Post Request Capif + ... /capif-security/v1/trustedInvokers/${register_user_info_invoker['api_invoker_id']}/delete + ... json=${request_body} + ... server=${CAPIF_HTTPS_URL} + ... verify=ca.crt + ... username=${AEF_PROVIDER_USERNAME} + + # Check Results + Status Should Be 204 ${resp} + + # Check Event Notifications + ## Create check Events to ensure all notifications were received + ${events_expected}= Create Expected Access Control Policy Unavailable ${subscription_id} + ${events_expected}= Create Expected Api Invoker Authorization Revoked + ... ${subscription_id} + ... events_expected=${events_expected} + ## Check Events Expected towards received notifications at mock server + Check Mock Server Notification Events ${events_expected} + + +*** Keywords *** +Create Events From InvocationLogs + [Arguments] ${subscription_id} ${invocation_log} ${events_expected}=${NONE} + IF ${events_expected} == ${NONE} + ${events_expected}= Create List + END + + # Now we create the expected events received at notification server according to logs sent to loggin service in order to check if all are present. + ${invocation_log_base}= Copy Dictionary ${invocation_log} deepcopy=True + # Store log array because each log will be notified in one Event Notification + ${invocation_log_logs}= Copy List ${invocation_log_base['logs']} + # Remove logs array from invocationLog data + Remove From Dictionary ${invocation_log_base} logs + + FOR ${log} IN @{invocation_log_logs} + Log Dictionary ${log} + ${invocation_logs}= Copy Dictionary ${invocation_log_base} deepcopy=True + + # Get Event Enum for this result + ${event_enum}= Set Variable + IF ${log['result']} >= 200 and ${log['result']} < 300 + ${event_enum}= Set Variable SERVICE_API_INVOCATION_SUCCESS + ELSE + ${event_enum}= Set Variable SERVICE_API_INVOCATION_FAILURE + END + # Create a log array with only one component + ${log_list}= Create List ${log} + # Setup logs array with previously created list + Set To Dictionary ${invocation_logs} logs=${log_list} + ${event_expected}= Create Notification Event ${subscription_id} ${event_enum} invocationLogs=${invocation_logs} + Append To List ${events_expected} ${event_expected} + END + + RETURN ${events_expected} +Create Expected Events For Service API Notifications + [Arguments] + ... ${subscription_id} + ... ${service_api_available_resources}=${NONE} + ... ${service_api_unavailable_resources}=${NONE} + ... ${events_expected}=${NONE} + + IF ${events_expected} == ${NONE} + ${events_expected}= Create List + END + + FOR ${service_api_available_resource} IN @{service_api_available_resources} + Log ${service_api_available_resource} + ${api_id}= Fetch From Right ${service_api_available_resource.path} / + ${event_expected}= Create Notification Event + ... ${subscription_id} + ... SERVICE_API_AVAILABLE + ... apiIds=${api_id} + Append To List ${events_expected} ${event_expected} + END + + FOR ${service_api_unavailable_resource} IN @{service_api_unavailable_resources} + Log ${service_api_unavailable_resource} + ${api_id}= Fetch From Right ${service_api_unavailable_resource.path} / + ${event_expected}= Create Notification Event + ... ${subscription_id} + ... SERVICE_API_UNAVAILABLE + ... apiIds=${api_id} + Append To List ${events_expected} ${event_expected} + END + + RETURN ${events_expected} + +Create Expected Api Invoker Events + [Arguments] ${subscription_id} ${api_invoker_id} ${events_expected}=${NONE} + IF ${events_expected} == ${NONE} + ${events_expected}= Create List + END + ## Create events expected + # Create Notification Events expected to be received for Onboard event + ${event_expected}= Create Notification Event + ... ${subscription_id} + ... API_INVOKER_ONBOARDED + ... apiInvokerIds=${api_invoker_id} + Append To List ${events_expected} ${event_expected} + + # Create Notification Events expected to be received for Updated event + ${event_expected}= Create Notification Event + ... ${subscription_id} + ... API_INVOKER_UPDATED + ... apiInvokerIds=${api_invoker_id} + Append To List ${events_expected} ${event_expected} + + # Create Notification Events expected to be received for Offboard event + ${event_expected}= Create Notification Event + ... ${subscription_id} + ... API_INVOKER_OFFBOARDED + ... apiInvokerIds=${api_invoker_id} + Append To List ${events_expected} ${event_expected} + + RETURN ${events_expected} + +Create Expected Access Control Policy Update Event + [Arguments] ${subscription_id} ${service_api_id} ${api_invoker_policies} ${events_expected}=${NONE} + IF ${events_expected} == ${NONE} + ${events_expected}= Create List + END + ${acc_ctrl_pol_list}= Create Dictionary apiId=${service_api_id} apiInvokerPolicies=${api_invoker_policies} + Check Variable ${acc_ctrl_pol_list} AccessControlPolicyListExt + + ${event_expected}= Create Notification Event + ... ${subscription_id} + ... ACCESS_CONTROL_POLICY_UPDATE + ... accCtrlPolList=${acc_ctrl_pol_list} + Append To List ${events_expected} ${event_expected} + + RETURN ${events_expected} + +Create Expected Access Control Policy Unavailable + [Arguments] ${subscription_id} ${events_expected}=${NONE} + IF ${events_expected} == ${NONE} + ${events_expected}= Create List + END + ${event_expected}= Create Notification Event + ... ${subscription_id} + ... ACCESS_CONTROL_POLICY_UNAVAILABLE + Append To List ${events_expected} ${event_expected} + + RETURN ${events_expected} + +Create Expected Api Invoker Authorization Revoked + [Arguments] ${subscription_id} ${events_expected}=${NONE} + IF ${events_expected} == ${NONE} + ${events_expected}= Create List + END + ${event_expected}= Create Notification Event + ... ${subscription_id} + ... API_INVOKER_AUTHORIZATION_REVOKED + Append To List ${events_expected} ${event_expected} + RETURN ${events_expected} + +Create Expected Service Update Event + [Arguments] ${subscription_id} ${service_api_resource} ${service_api_descriptions} ${events_expected}=${NONE} + IF ${events_expected} == ${NONE} + ${events_expected}= Create List + END + ${api_id}= Fetch From Right ${service_api_resource.path} / + Set To Dictionary ${service_api_descriptions} apiId=${api_id} + ${events_expected}= Create List + ${event_expected}= Create Notification Event + ... ${subscription_id} + ... SERVICE_API_UPDATE + ... serviceAPIDescriptions=${service_api_descriptions} + Append To List ${events_expected} ${event_expected} + RETURN ${events_expected} \ No newline at end of file diff --git a/tests/libraries/api_events/bodyRequests.py b/tests/libraries/api_events/bodyRequests.py index ab6e4dae3d646bccbb9ac3f994c7f71112d9e2dc..17fd1b57f743adeecd6da459a8d0a3179b11af90 100644 --- a/tests/libraries/api_events/bodyRequests.py +++ b/tests/libraries/api_events/bodyRequests.py @@ -1,32 +1,97 @@ -def create_events_subscription(): +def create_events_subscription(events=["SERVICE_API_AVAILABLE", "API_INVOKER_ONBOARDED"], notificationDestination="http://robot.testing", eventFilters=None, eventReq=None, requestTestNotification=None, supportedFeatures=None, websockNotifConfig=None): + event_subscription = { + "events": events, + "notificationDestination": notificationDestination, + } + if eventFilters != None: + event_subscription['eventFilters'] = eventFilters + if eventReq != None: + event_subscription['eventReq'] = eventReq + if requestTestNotification != None: + event_subscription['requestTestNotification'] = requestTestNotification + if supportedFeatures != None: + event_subscription['supportedFeatures'] = supportedFeatures + if websockNotifConfig != None: + event_subscription['websockNotifConfig'] = websockNotifConfig + + return event_subscription + + +def create_capif_event_filter(aefIds=None, apiIds=None, apiInvokerIds=None): + if aefIds == None and apiIds == None and apiInvokerIds: + raise ("Error, no data present to create event filter") + capif_event_filter = dict() + if aefIds != None: + capif_event_filter['aefIds'] = aefIds + if apiIds != None: + capif_event_filter['apiIds'] = apiIds + if apiInvokerIds != None: + capif_event_filter['apiInvokerIds'] = apiInvokerIds + return capif_event_filter + + +def create_default_event_req(): + return { + "grpRepTime": 5, + "immRep": True, + "maxReportNbr": 0, + "monDur": "2000-01-23T04:56:07+00:00", + "partitionCriteria": ["TAC", "GEOAREA"], + "repPeriod": 6, + "sampRatio": 15 + } + + +def create_websock_notif_config_default(): return { - "eventFilters": [ - { - "aefIds": ["aefIds", "aefIds"], - "apiIds": ["apiIds", "apiIds"], - "apiInvokerIds": ["apiInvokerIds", "apiInvokerIds"] - }, - { - "aefIds": ["aefIds", "aefIds"], - "apiIds": ["apiIds", "apiIds"], - "apiInvokerIds": ["apiInvokerIds", "apiInvokerIds"] - } - ], - "eventReq": { - "grpRepTime": 5, - "immRep": True, - "maxReportNbr": 0, - "monDur": "2000-01-23T04:56:07+00:00", - "partitionCriteria": ["TAC", "GEOAREA"], - "repPeriod": 6, - "sampRatio": 15 - }, - "events": ["SERVICE_API_AVAILABLE", "API_INVOKER_ONBOARDED"], - "notificationDestination": "http://robot.testing", - "requestTestNotification": True, - "supportedFeatures": "aaa", - "websockNotifConfig": { - "requestWebsocketUri": True, - "websocketUri": "websocketUri" - } + "requestWebsocketUri": True, + "websocketUri": "websocketUri" + } + + +def create_notification_event(subscriptionId, event, serviceAPIDescriptions=None, apiIds=None, apiInvokerIds=None, accCtrlPolList=None, invocationLogs=None, apiTopoHide=None): + result = { + "subscriptionId": subscriptionId, + "events": event, + "eventDetail": dict() } + count = 0 + if serviceAPIDescriptions != None: + if isinstance(serviceAPIDescriptions, list): + result['eventDetail']['serviceAPIDescriptions'] = serviceAPIDescriptions + else: + result['eventDetail']['serviceAPIDescriptions'] = [ + serviceAPIDescriptions] + count = count+1 + if apiIds != None: + if isinstance(apiIds, list): + result['eventDetail']['apiIds'] = apiIds + else: + result['eventDetail']['apiIds'] = [apiIds] + count = count+1 + if apiInvokerIds != None: + if isinstance(apiInvokerIds, list): + result['eventDetail']['apiInvokerIds'] = apiInvokerIds + else: + result['eventDetail']['apiInvokerIds'] = [apiInvokerIds] + count = count+1 + if accCtrlPolList != None: + result['eventDetail']['accCtrlPolList'] = accCtrlPolList + count = count+1 + if invocationLogs != None: + if isinstance(invocationLogs, list): + result['eventDetail']['invocationLogs'] = invocationLogs + else: + result['eventDetail']['invocationLogs'] = [invocationLogs] + count = count+1 + if apiTopoHide != None: + if isinstance(apiTopoHide, list): + result['eventDetail']['apiTopoHide'] = apiTopoHide + else: + result['eventDetail']['apiTopoHide'] = [apiTopoHide] + count = count+1 + + if count == 0: + del result['eventDetail'] + + return result diff --git a/tests/libraries/api_logging_service/bodyRequests.py b/tests/libraries/api_logging_service/bodyRequests.py index fe3faf1dd9dbc1ccd0fb96ce96e5b73d0cd805f9..a4d2a18770dca3d2d43742010bf9d336aaf728d3 100644 --- a/tests/libraries/api_logging_service/bodyRequests.py +++ b/tests/libraries/api_logging_service/bodyRequests.py @@ -1,84 +1,21 @@ -def create_log_entry(aefId=None, apiInvokerId=None, apiId=None, apiName=None): +def create_log_entry(aefId, apiInvokerId, apiId, apiName, results=['200','500'],api_versions=['v1','v2']): data = { "aefId": aefId, "apiInvokerId": apiInvokerId, - "logs": [ - { - "apiId": apiId[0], - "apiName": apiName[0], - "apiVersion": "v1", - "resourceName": "string", - "uri": "http://resource/endpoint", - "protocol": "HTTP_1_1", - "operation": "GET", - "result": "string", - "invocationTime": "2023-03-30T10:30:21.408Z", - "invocationLatency": 0, - "inputParameters": "string", - "outputParameters": "string", - "srcInterface": { - "ipv4Addr": "192.168.1.1", - "fqdn": "string", - "port": 65535, - "apiPrefix": "string", - "securityMethods": [ - "PSK", - "string" - ] - }, - "destInterface": { - "ipv4Addr": "192.168.1.23", - "fqdn": "string", - "port": 65535, - "apiPrefix": "string", - "securityMethods": [ - "PSK", - "string" - ] - }, - "fwdInterface": "string" - }, - { - "apiId": apiId[0], - "apiName": apiName[0], - "apiVersion": "v2", - "resourceName": "string", - "uri": "http://resource/endpoint", - "protocol": "HTTP_1_1", - "operation": "GET", - "result": "string", - "invocationTime": "2023-03-30T10:30:21.408Z", - "invocationLatency": 0, - "inputParameters": "string", - "outputParameters": "string", - "srcInterface": { - "ipv4Addr": "192.168.1.1", - "fqdn": "string", - "port": 65535, - "apiPrefix": "string", - "securityMethods": [ - "PSK", - "string" - ] - }, - "destInterface": { - "ipv4Addr": "192.168.1.23", - "fqdn": "string", - "port": 65535, - "apiPrefix": "string", - "securityMethods": [ - "PSK", - "string" - ] - }, - "fwdInterface": "string" - } - ], + "logs": [], "supportedFeatures": "ffff" } + if len(results) > 0: + count=0 + for result in results: + data['logs'].append(create_log(apiId,apiName,result,api_versions[count])) + count=count+1 + if count == len(api_versions): + count=0 + return data -def create_log_entry_bad_service(aefId=None, apiInvokerId=None): +def create_log_entry_bad_service(aefId, apiInvokerId, result='500'): data = { "aefId": aefId, "apiInvokerId": apiInvokerId, @@ -91,29 +28,25 @@ def create_log_entry_bad_service(aefId=None, apiInvokerId=None): "uri": "string", "protocol": "HTTP_1_1", "operation": "GET", - "result": "string", - "invocationTime": "2023-03-30T10:30:21.408Z", + "result": result, + "invocationTime": "2023-03-30T10:30:21.408000+00:00", "invocationLatency": 0, "inputParameters": "string", "outputParameters": "string", "srcInterface": { "ipv4Addr": "192.168.1.1", - "fqdn": "string", "port": 65535, - "apiPrefix": "string", "securityMethods": [ "PSK", - "string" + "PKI" ] }, "destInterface": { "ipv4Addr": "192.168.1.23", - "fqdn": "string", "port": 65535, - "apiPrefix": "string", "securityMethods": [ "PSK", - "string" + "PKI" ] }, "fwdInterface": "string" @@ -131,3 +64,29 @@ def get_api_ids_and_names_from_discover_response(discover_response): api_ids.append(service_api_description['apiId']) api_names.append(service_api_description['apiName']) return api_ids, api_names + + +def create_log(apiId, apiName, result, api_version='v1'): + log= { + "apiId": apiId[0], + "apiName": apiName[0], + "apiVersion": api_version, + "resourceName": "string", + "uri": "http://resource/endpoint", + "protocol": "HTTP_1_1", + "operation": "GET", + "result": result, + "invocationTime": "2023-03-30T10:30:21.408000+00:00", + "invocationLatency": 0, + "inputParameters": "string", + "outputParameters": "string", + "srcInterface": { + "ipv4Addr": "192.168.1.1", + "port": 65535, + "securityMethods": [ + "PSK", + "PKI" + ] + } + } + return log \ No newline at end of file diff --git a/tests/libraries/api_publish_service/bodyRequests.py b/tests/libraries/api_publish_service/bodyRequests.py index 17ac4a794a5ba1a38357ee229d04b3b4590bfdc8..69e7bb117f01b4c53ee7d1db74b7da09e20bf3a1 100644 --- a/tests/libraries/api_publish_service/bodyRequests.py +++ b/tests/libraries/api_publish_service/bodyRequests.py @@ -7,7 +7,7 @@ def create_service_api_description(api_name="service_1",aef_id="aef_id"): "versions": [ { "apiVersion": "v1", - "expiry": "2021-11-30T10:32:02.004000Z", + "expiry": "2021-11-30T10:32:02.004000+00:00", "resources": [ { "resourceName": "string", @@ -20,27 +20,12 @@ def create_service_api_description(api_name="service_1",aef_id="aef_id"): "description": "string" } ], - "custOperations": [ - { - "commType": "REQUEST_RESPONSE", - "custOpName": "string", - "operations": [ - "GET" - ], - "description": "string" - } - ] } ], "protocol": "HTTP_1_1", "dataFormat": "JSON", "securityMethods": ["PSK"], "interfaceDescriptions": [ - { - "ipv4Addr": "string", - "port": 65535, - "securityMethods": ["PSK"] - }, { "ipv4Addr": "string", "port": 65535, diff --git a/tests/libraries/common/bodyRequests.py b/tests/libraries/common/bodyRequests.py index 3f1e482d4717bc5d3ddfc298962afb8e42bc1a7e..1d4ec14f60759bfb221b686ba84df23b1386fa6c 100644 --- a/tests/libraries/common/bodyRequests.py +++ b/tests/libraries/common/bodyRequests.py @@ -15,7 +15,7 @@ def check_variable(input, data_type): if isinstance(input, list): for one in input: check_variable(one, data_type) - return True + return True if data_type == "string": if isinstance(input, str): return True diff --git a/tests/libraries/common/types.json b/tests/libraries/common/types.json index 4b683595c43c912f868856107d21ed84ad08032d..220dd0f5710796fd875ddda8d05dfb3e39a272cd 100644 --- a/tests/libraries/common/types.json +++ b/tests/libraries/common/types.json @@ -259,6 +259,67 @@ "aefIds": "string" } }, + "EventNotification": { + "mandatory_attributes": { + "subscriptionId": "string", + "events": "CAPIFEvent" + }, + "optional_attributes": { + "eventDetail": "CAPIFEventDetail" + } + }, + "CAPIFEventDetail": { + "mandatory_attributes": {}, + "optional_attributes": { + "serviceAPIDescriptions": "ServiceAPIDescription", + "apiIds": "string", + "apiInvokerIds": "string", + "accCtrlPolList": "AccessControlPolicyListExt", + "invocationLogs": "InvocationLog", + "apiTopoHide": "TopologyHiding" + } + }, + "AccessControlPolicyListExt":{ + "mandatory_attributes": { + "apiId": "string" + }, + "optional_attributes": { + "apiInvokerPolicies": "ApiInvokerPolicy" + } + }, + "TopologyHiding": { + "mandatory_attributes":{ + "apiId": "string", + "routingRules": "RoutingRule" + }, + "optional_attributes": {} + }, + "RoutingRule":{ + "mandatory_attributes": { + "aefProfile": "AefProfile" + }, + "optional_attributes": { + "ipv4AddrRanges": "Ipv4AddressRange", + "ipv6AddrRanges": "Ipv6AddressRange" + } + }, + "Ipv4AddressRange":{ + "mandatory_attributes": {}, + "optional_attributes": { + "start": "Ipv4Addr", + "stop": "Ipv4Addr" + } + }, + "Ipv4Addr": { + "regex": "^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$" + }, + "Ipv6AddressRange":{ + "mandatory_attributes":{ + "start": "string", + "end": "string" + }, + "optional_attributes":{} + }, "ReportingInformation": { "mandatory_attributes": {}, "optional_attributes": { diff --git a/tests/requirements.txt b/tests/requirements.txt index c6d90325ac78628f7de12acc34a01497039f57f3..3119b5ca73095dfdc4a018dc35abaf83c5bc5ab2 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,8 +1,2 @@ # Requirements file for tests. -robotframework-mongodb-library==3.2 -requests==2.28.1 -configparser==5.3.0 -redis==4.3.4 -rfc3987==1.3.8 -robotframework-httpctrl -robotframework-archivelibrary == 0.4.2 \ No newline at end of file + diff --git a/tests/resources/common.resource b/tests/resources/common.resource index c46131b17388b006b9fa7f521033a3c32874c171..d4733b8ee4e12ac5477501b1e1c0ffab4a09f79d 100644 --- a/tests/resources/common.resource +++ b/tests/resources/common.resource @@ -1,5 +1,7 @@ *** Settings *** Library /opt/robot-tests/tests/libraries/helpers.py +Library Process +Library Collections Variables /opt/robot-tests/tests/libraries/environment.py Resource /opt/robot-tests/tests/resources/common/basicRequests.robot @@ -30,6 +32,9 @@ ${CAPIF_CALLBACK_PORT} 8086 ${REGISTER_ADMIN_USER} admin ${REGISTER_ADMIN_PASSWORD} password123 +${MOCK_SERVER_URL} + + ${DISCOVER_URL} /service-apis/v1/allServiceAPIs?api-invoker-id= @@ -85,3 +90,78 @@ Remove Keys From Object Test ${TEST NAME} Currently Not Supported Log Test "${TEST NAME}" Currently not supported WARN Skip Test "${TEST NAME}" Currently not supported + +Init Mock Server + Check Mock Server + Clean Mock Server + +Check Mock Server + Log Checking mock Server for Robot Framework. + + IF "${MOCK_SERVER_URL}" == "" + Fail Mock Server Url is not setup on Tests execution, check MOCK_SERVER_URL variable mockserver-not-ready + END + + Create Session mockserver ${MOCK_SERVER_URL} + + ${endpoint}= Set Variable /requests_list + + ${resp}= GET On Session + ... mockserver + ... ${endpoint} + ... expected_status=any + ... verify=False + + Status Should Be 200 ${resp} Mock Server response is not 200 OK at ${MOCK_SERVER_URL}, check server status. + +Clean Mock Server + Log Checking mock Server for Robot Framework. + + Create Session mockserver ${MOCK_SERVER_URL} + + ${endpoint}= Set Variable /requests_list + + ${resp}= DELETE On Session + ... mockserver + ... ${endpoint} + ... expected_status=any + ... verify=False + + Status Should Be 200 ${resp} + + +Get Mock Server Messages + Log Checking mock Server for Robot Framework. + + Create Session mockserver ${MOCK_SERVER_URL} + + ${endpoint}= Set Variable /requests_list + + ${resp}= GET On Session + ... mockserver + ... ${endpoint} + ... expected_status=any + ... verify=False + + Status Should Be 200 ${resp} + Log List ${resp.json()} + RETURN ${resp} + +Check Mock Server Notification Events + [Arguments] ${events_expected} + + Check Variable ${events_expected} EventNotification + # Check results + ${events_expected_length}= Get Length ${events_expected} + + Sleep 3s + # Get from Mock server the EventNotification Messages sent to callback setup on event subscription. + ${resp}= Get Mock Server Messages + ${notification_events_on_mock_server}= Set Variable ${resp.json()} + Check Variable ${notification_events_on_mock_server} EventNotification + + Length Should Be ${notification_events_on_mock_server} ${events_expected_length} + FOR ${event_expected} IN @{events_expected} + Log ${event_expected} + List Should Contain Value ${notification_events_on_mock_server} ${event_expected} + END \ No newline at end of file diff --git a/tests/resources/common/basicRequests.robot b/tests/resources/common/basicRequests.robot index ea3a96ffc432e86aa543ab51c796cbffb61bfc76..433a13cd74e3fb02a1333d812c9d5679a7b16b36 100644 --- a/tests/resources/common/basicRequests.robot +++ b/tests/resources/common/basicRequests.robot @@ -416,6 +416,11 @@ Create User At Register ... password=${password} ... description=${description} ... email=${email} + ... enterprise=enterprise + ... country=Spain + ... purpose=testing + ... phone_number=123456789 + ... company_web=www.enterprise.com ${resp}= Post On Session register_session /createUser headers=${headers} json=${body} Should Be Equal As Strings ${resp.status_code} 201 @@ -457,6 +462,7 @@ Get Auth For User ${resp}= GET On Session register_session /getauth auth=${auth} Should Be Equal As Strings ${resp.status_code} 200 + Log Dictionary ${resp.json()} RETURN ${resp.json()} @@ -744,3 +750,4 @@ Create Security Context Between invoker and provider ... username=${register_user_info_invoker['management_cert']} Check Response Variable Type And Values ${resp} 201 ServiceSecurity + diff --git a/tools/robot/Dockerfile b/tools/robot/Dockerfile index 49cea1714ace28c777903b7c30f30bad1ae674cf..347f226da0bf93d5e1f8a9adaa18901c5dafc000 100644 --- a/tools/robot/Dockerfile +++ b/tools/robot/Dockerfile @@ -25,6 +25,7 @@ VOLUME $ROBOT_RESULTS_DIRECTORY WORKDIR $ROBOT_DIRECTORY ENV DEBIAN_FRONTEND=noninteractive +RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections # Install dependencies RUN apt-get update @@ -49,14 +50,15 @@ RUN apt-get install -y --no-install-recommends \ python3-venv \ python2.7-dev \ libssl-dev \ - libldap2-dev libsasl2-dev ldap-utils slapd tox lcov valgrind\ - tshark + libldap2-dev libsasl2-dev ldap-utils slapd tox lcov valgrind \ + tshark \ + nodejs \ + npm -RUN add-apt-repository ppa:deadsnakes/ppa +RUN add-apt-repository -y ppa:deadsnakes/ppa RUN apt-get update RUN apt-get install -y --fix-missing python3.10 python3.10-venv python3.10-dev - RUN mkdir /opt/venv RUN python3.10 -m venv /opt/venv diff --git a/tools/robot/basicRequirements.txt b/tools/robot/basicRequirements.txt index 87752271253dc67c7e0c0d8e8f42d4768001b074..aa3e60e0f55ee408cb29605ef29446b3dd075d4f 100644 --- a/tools/robot/basicRequirements.txt +++ b/tools/robot/basicRequirements.txt @@ -13,7 +13,7 @@ certifi==2021.10.8 cffi==1.15.1 chardet==5.0.0 charset-normalizer==2.0.12 -click==8.0.1 +click==8.1.7 configparser==5.3.0 cookiecutter==2.1.1 coverage==4.5.4 @@ -25,8 +25,8 @@ docutils==0.19 exceptiongroup==1.0.0rc9 filelock==3.8.0 flake8==3.9.2 +flask==3.0.3 h11==0.14.0 -robotframework-httpctrl==0.3.1 idna==3.4 iniconfig==1.1.1 invoke==1.6.0 @@ -69,10 +69,12 @@ redis==4.3.4 rellu==0.7 requests==2.28.1 rfc3987==1.3.8 -robotframework==6.0 +robotframework==7.0 +robotframework-archivelibrary == 0.4.2 +robotframework-httpctrl==0.3.1 robotframework-lint==1.1 robotframework-mongodb-library==3.2 -robotframework-pythonlibcore==3.0.0 +robotframework-pythonlibcore==4.4.1 robotframework-requests==0.9.3 robotframework-seleniumlibrary==6.0.0 robotframework-sshlibrary==3.8.0 @@ -91,11 +93,11 @@ tox==3.26.0 tqdm==4.64.1 trio==0.22.0 trio-websocket==0.9.2 -typing-extensions==3.10.0.2 +typing-extensions==4.11.0 urllib3==1.26.12 virtualenv==20.16.5 -watchdog==0.9.0 +watchdog==4.0.0 webdrivermanager==0.10.0 -wrapt==1.14.1 +wrapt==1.15.0 wsproto==1.2.0 -xlrd==2.0.1 \ No newline at end of file +xlrd==2.0.1 diff --git a/tools/robot/basicRobotInstall.sh b/tools/robot/basicRobotInstall.sh index 511821fbab10d55f80dd2a13af1d60035ca0bd40..ae0bd6bd8b1bdedccc34b4d6152bffc8bb5f467b 100644 --- a/tools/robot/basicRobotInstall.sh +++ b/tools/robot/basicRobotInstall.sh @@ -1,7 +1,6 @@ #!/bin/bash echo "Installing basic software related with robotFramework" -source /opt/venv/bin/activate; +source /opt/venv/bin/activate pip install --upgrade pip -pip install --upgrade robotframework; -pip install -r $1 +pip install -r $1 echo "Robot framework installed"