From: srinivasyanamadala Date: Mon, 3 Feb 2025 15:44:33 +0000 (+0100) Subject: Remove decision parameter from OPADecisionResponse X-Git-Tag: 1.0.3~19 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=c98f9a1086c1ede4b6ccb6a566197533b3001826;p=policy%2Fopa-pdp.git Remove decision parameter from OPADecisionResponse Issue-ID: POLICY-5261 Change-Id: I6e55a4050025fe8626a0a8cd3e114e7d3f137767 Signed-off-by: srinivasyanamadala --- diff --git a/api/openapi.yaml b/api/openapi.yaml index bd94ec2..8fe3d2c 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -1,389 +1,369 @@ -# -# ========================LICENSE_START================================= -# Copyright (C) 2024: Deutsche Telekom -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# SPDX-License-Identifier: Apache-2.0 -# ========================LICENSE_END=================================== -# -openapi: 3.0.3 -info: - title: "Policy OPA PDP Documentation" - description: Policy OPA PDP Service - version: 1.0.2 - x-component: Policy Framework - x-planned-retirement-date: tbd - contact: - name: Deena Mukundan - email: dm00536893@techmahindra.com -servers: -- url: http://policy-opa-pdp:8282/policy/pdpo/v1 -- url: https://policy-opa-pdp:8282/policy/pdpo/v1 -tags: -- name: Decision -- name: Statistics -- name: HealthCheck -paths: - /decision: - post: - tags: - - Decision - summary: Fetch the decision using specified decision parameters - description: Returns the policy decision from Policy OPA PDP - operationId: decision - parameters: - - name: X-ONAP-RequestID - in: header - description: RequestID for http transaction - schema: - type: string - format: uuid - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/OPADecisionRequest' - application/yaml: - schema: - $ref: '#/components/schemas/OPADecisionRequest' - required: false - responses: - 200: - description: successful operation - headers: - X-LatestVersion: - description: Used only to communicate an API's latest version - schema: - type: string - X-PatchVersion: - description: Used only to communicate a PATCH version in a response - for troubleshooting purposes only, and will not be provided by the - client on request - schema: - type: string - X-MinorVersion: - description: Used to request or communicate a MINOR version back from - the client to the server, and from the server back to the client - schema: - type: string - X-ONAP-RequestID: - description: Used to track REST transactions for logging purpose - schema: - type: string - format: uuid - content: - application/json: - schema: - $ref: '#/components/schemas/OPADecisionResponse' - application/yaml: - schema: - $ref: '#/components/schemas/OPADecisionResponse' - 400: - description: Bad Request - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - application/yaml: - schema: - $ref: '#/components/schemas/ErrorResponse' - 401: - description: Authentication Error - content: {} - 403: - description: Authorization Error - content: {} - 500: - description: Internal Server Error - content: {} - security: - - basicAuth: [] - x-interface info: - last-mod-release: Paris - pdpo-version: 1.0.0 - x-codegen-request-body-name: body - /healthcheck: - get: - tags: - - HealthCheck - summary: Perform a system healthcheck - description: Provides healthy status of the Policy OPA PDP component - operationId: healthcheck - parameters: - - name: X-ONAP-RequestID - in: header - description: RequestID for http transaction - schema: - type: string - format: uuid - responses: - 200: - description: successful operation - headers: - X-LatestVersion: - description: Used only to communicate an API's latest version - schema: - type: string - X-PatchVersion: - description: Used only to communicate a PATCH version in a response - for troubleshooting purposes only, and will not be provided by the - client on request - schema: - type: string - X-MinorVersion: - description: Used to request or communicate a MINOR version back from - the client to the server, and from the server back to the client - schema: - type: string - X-ONAP-RequestID: - description: Used to track REST transactions for logging purpose - schema: - type: string - format: uuid - content: - application/json: - schema: - $ref: '#/components/schemas/HealthCheckReport' - application/yaml: - schema: - $ref: '#/components/schemas/HealthCheckReport' - 401: - description: Authentication Error - content: {} - 403: - description: Authorization Error - content: {} - 500: - description: Internal Server Error - content: {} - security: - - basicAuth: [] - x-interface info: - last-mod-release: Paris - pdpo-version: 1.0.0 - /statistics: - get: - tags: - - Statistics - summary: Fetch current statistics - description: Provides current statistics of the Policy OPA PDP component - operationId: statistics - parameters: - - name: X-ONAP-RequestID - in: header - description: RequestID for http transaction - schema: - type: string - format: uuid - responses: - 200: - description: successful operation - headers: - X-LatestVersion: - description: Used only to communicate an API's latest version - schema: - type: string - X-PatchVersion: - description: Used only to communicate a PATCH version in a response - for troubleshooting purposes only, and will not be provided by the - client on request - schema: - type: string - X-MinorVersion: - description: Used to request or communicate a MINOR version back from - the client to the server, and from the server back to the client - schema: - type: string - X-ONAP-RequestID: - description: Used to track REST transactions for logging purpose - schema: - type: string - format: uuid - content: - application/json: - schema: - $ref: '#/components/schemas/StatisticsReport' - application/yaml: - schema: - $ref: '#/components/schemas/StatisticsReport' - 401: - description: Authentication Error - content: {} - 403: - description: Authorization Error - content: {} - 500: - description: Internal Server Error - content: {} - security: - - basicAuth: [] - x-interface info: - last-mod-release: Paris - pdpo-version: 1.0.0 -components: - schemas: - ErrorResponse: - type: object - properties: - responseCode: - type: string - enum: - - BAD_REQUEST - - UNAUTHORIZED - - METHOD_NOT_ALLOWED - - NOT_ACCEPTABLE - - REQUEST_TIMEOUT - - CONFLICT - - GONE - - LENGTH_REQUIRED - - PRECONDITION_FAILED - - REQUEST_ENTITY_TOO_LARGE - - REQUEST_URI_TOO_LONG - - UNSUPPORTED_MEDIA_TYPE - - REQUESTED_RANGE_NOT_SATISFIABLE - - EXPECTATION_FAILED - - PRECONDITION_REQUIRED - - TOO_MANY_REQUESTS - - REQUEST_HEADER_FIELDS_TOO_LARGE - - INTERNAL_SERVER_ERROR - - NOT_IMPLEMENTED - - BAD_GATEWAY - - SERVICE_UNAVAILABLE - - GATEWAY_TIMEOUT - - HTTP_VERSION_NOT_SUPPORTED - - NETWORK_AUTHENTICATION_REQUIRED - errorMessage: - type: string - policyName: - type: string - errorDetails: - type: array - items: - type: string - OPADecisionRequest: - type: object - properties: - onapName: - type: string - onapComponent: - type: string - onapInstance: - type: string - currentDateTime: - type: string - format: date-time - currentDate: - type: string - format: date - currentTime: - type: string - format: date-time - timeZone: - type: string - description: "Timezone in IANA format (e.g., 'America/NewYork', 'Europe/Paris', 'UTC')" - timeOffset: - type: string - pattern: '^[+-]?\d{2}:\d{2}$' - description: "Time offset in hours and minutes, e.g., '+02:00' or '-05:00'" - policyName: - type: string - policyFilter: - type: array - items: - type: string - input: - type: object - additionalProperties: true - example: - user: alice - action: read - object: id123 - type: dog - HealthCheckReport: - type: object - properties: - name: - type: string - url: - type: string - healthy: - type: boolean - code: - type: integer - format: int32 - message: - type: string - OPADecisionResponse: - type: object - properties: - statusMessage: - type: string - decision: - type: string - enum: - - PERMIT - - DENY - - INDETERMINATE - - NOTAPPLICABLE - policyName: - type: string - output: - type: object - additionalProperties: true - StatisticsReport: - type: object - properties: - code: - type: integer - format: int32 - totalPolicyTypesCount: - type: integer - format: int64 - totalPoliciesCount: - type: integer - format: int64 - totalErrorCount: - type: integer - format: int64 - permitDecisionsCount: - type: integer - format: int64 - denyDecisionsCount: - type: integer - format: int64 - deploySuccessCount: - type: integer - format: int64 - deployFailureCount: - type: integer - format: int64 - undeploySuccessCount: - type: integer - format: int64 - undeployFailureCount: - type: integer - format: int64 - indeterminantDecisionsCount: - type: integer - format: int64 - querySuccessCount: - type: integer - format: int64 - queryFailureCount: - type: integer - format: int64 - securitySchemes: - basicAuth: - type: http - description: "" - scheme: basic +# +# ========================LICENSE_START================================= +# Copyright (C) 2024-2025: Deutsche Telekom +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# SPDX-License-Identifier: Apache-2.0 +# ========================LICENSE_END=================================== +# +openapi: 3.0.3 +info: + title: "Policy OPA PDP Documentation" + description: Policy OPA PDP Service + version: 1.0.2 + x-component: Policy Framework + x-planned-retirement-date: tbd + contact: + name: Deena Mukundan + email: dm00536893@techmahindra.com +servers: +- url: http://policy-opa-pdp:8282/policy/pdpo/v1 +- url: https://policy-opa-pdp:8282/policy/pdpo/v1 +tags: +- name: Decision +- name: Statistics +- name: HealthCheck + +paths: + /decision: + post: + tags: + - Decision + summary: Fetch the decision using specified decision parameters + description: Returns the policy decision from Policy OPA PDP + operationId: decision + parameters: + - name: X-ONAP-RequestID + in: header + description: RequestID for http transaction + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/OPADecisionRequest' + application/yaml: + schema: + $ref: '#/components/schemas/OPADecisionRequest' + required: true + responses: + 200: + description: successful operation + headers: + X-LatestVersion: + description: Used only to communicate an API's latest version + schema: + type: string + X-PatchVersion: + description: Used only to communicate a PATCH version in a response + for troubleshooting purposes only, and will not be provided by the + client on request + schema: + type: string + X-MinorVersion: + description: Used to request or communicate a MINOR version back from + the client to the server, and from the server back to the client + schema: + type: string + X-ONAP-RequestID: + description: Used to track REST transactions for logging purpose + schema: + type: string + format: uuid + content: + application/json: + schema: + $ref: '#/components/schemas/OPADecisionResponse' + application/yaml: + schema: + $ref: '#/components/schemas/OPADecisionResponse' + + 400: + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + application/yaml: + schema: + $ref: '#/components/schemas/ErrorResponse' + 401: + description: Authentication Error + content: {} + 403: + description: Authorization Error + content: {} + 500: + description: Internal Server Error + content: {} + security: + - basicAuth: [] + x-interface info: + last-mod-release: Paris + pdpo-version: 1.0.0 + x-codegen-request-body-name: body + /healthcheck: + get: + tags: + - HealthCheck + summary: Perform a system healthcheck + description: Provides healthy status of the Policy OPA PDP component + operationId: healthcheck + parameters: + - name: X-ONAP-RequestID + in: header + description: RequestID for http transaction + schema: + type: string + format: uuid + responses: + 200: + description: successful operation + headers: + X-LatestVersion: + description: Used only to communicate an API's latest version + schema: + type: string + X-PatchVersion: + description: Used only to communicate a PATCH version in a response + for troubleshooting purposes only, and will not be provided by the + client on request + schema: + type: string + X-MinorVersion: + description: Used to request or communicate a MINOR version back from + the client to the server, and from the server back to the client + schema: + type: string + X-ONAP-RequestID: + description: Used to track REST transactions for logging purpose + schema: + type: string + format: uuid + content: + application/json: + schema: + $ref: '#/components/schemas/HealthCheckReport' + application/yaml: + schema: + $ref: '#/components/schemas/HealthCheckReport' + 401: + description: Authentication Error + content: {} + 403: + description: Authorization Error + content: {} + 500: + description: Internal Server Error + content: {} + security: + - basicAuth: [] + x-interface info: + last-mod-release: Paris + pdpo-version: 1.0.0 + /statistics: + get: + tags: + - Statistics + summary: Fetch current statistics + description: Provides current statistics of the Policy OPA PDP component + operationId: statistics + parameters: + - name: X-ONAP-RequestID + in: header + description: RequestID for http transaction + schema: + type: string + format: uuid + responses: + 200: + description: successful operation + headers: + X-LatestVersion: + description: Used only to communicate an API's latest version + schema: + type: string + X-PatchVersion: + description: Used only to communicate a PATCH version in a response + for troubleshooting purposes only, and will not be provided by the + client on request + schema: + type: string + X-MinorVersion: + description: Used to request or communicate a MINOR version back from + the client to the server, and from the server back to the client + schema: + type: string + X-ONAP-RequestID: + description: Used to track REST transactions for logging purpose + schema: + type: string + format: uuid + content: + application/json: + schema: + $ref: '#/components/schemas/StatisticsReport' + application/yaml: + schema: + $ref: '#/components/schemas/StatisticsReport' + 401: + description: Authentication Error + content: {} + 403: + description: Authorization Error + content: {} + 500: + description: Internal Server Error + content: {} + security: + - basicAuth: [] + x-interface info: + last-mod-release: Paris + pdpo-version: 1.0.0 + +components: + schemas: + ErrorResponse: + type: object + properties: + responseCode: + type: string + enum: + - internal_error + - bad_request + - evaluation_error + - unauthorized + - invalid_parameter + - invalid_operation + - resource_not_found + - resource_conflict + - undefined_document + - opa_undefined_error + errorMessage: + type: string + policyName: + type: string + OPADecisionRequest: + type: object + properties: + onapName: + type: string + onapComponent: + type: string + onapInstance: + type: string + currentDateTime: + type: string + format: date-time + currentDate: + type: string + format: date + currentTime: + type: string + format: time + timeZone: + type: string + description: "Timezone in IANA format (e.g., 'America/NewYork', 'Europe/Paris', 'UTC')" + timeOffset: + type: string + pattern: '^[+-]?\d{2}:\d{2}$' + description: "Time offset in hours and minutes, e.g., '+02:00' or '-05:00'" + policyName: + type: string + policyFilter: + type: array + items: + type: string + input: + type: object + additionalProperties: true + example: + user: alice + action: read + object: id123 + type: dog + required: + - policyName + - policyFilter + - input + + HealthCheckReport: + type: object + properties: + name: + type: string + url: + type: string + healthy: + type: boolean + code: + type: integer + format: int32 + message: + type: string + OPADecisionResponse: + type: object + properties: + policyName: + type: string + statusMessage: + type: string + output: + type: object + additionalProperties: true + required: + - policyName + - output + + StatisticsReport: + type: object + properties: + code: + type: integer + format: int32 + totalPolicyTypesCount: + type: integer + format: int64 + totalPoliciesCount: + type: integer + format: int64 + totalErrorCount: + type: integer + format: int64 + deploySuccessCount: + type: integer + format: int64 + deployFailureCount: + type: integer + format: int64 + undeploySuccessCount: + type: integer + format: int64 + undeployFailureCount: + type: integer + format: int64 + decisionSuccessCount: + type: integer + format: int64 + decisionFailureCount: + type: integer + format: int64 + + securitySchemes: + basicAuth: + type: http + description: "" + scheme: basic + diff --git a/cmd/opa-pdp/opa-pdp.go b/cmd/opa-pdp/opa-pdp.go index b48bd27..a2cbde2 100644 --- a/cmd/opa-pdp/opa-pdp.go +++ b/cmd/opa-pdp/opa-pdp.go @@ -95,15 +95,19 @@ func main() { defer producer.Close() sender := &publisher.RealPdpStatusSender{Producer: producer} + // start pdp message handler in a seperate routine + handleMessagesFunc(ctx, kc, sender) + + time.Sleep(10 * time.Second) + // pdp registration isRegistered := registerPDPFunc(sender) if !isRegistered { return } - // start pdp message handler in a seperate routine - handleMessagesFunc(ctx, kc, sender) - + time.Sleep(10 * time.Second) + log.Debugf("After registration successful delay") // Handle OS Interrupts and Graceful Shutdown interruptChannel := make(chan os.Signal, 1) signal.Notify(interruptChannel, os.Interrupt, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP) diff --git a/go.mod b/go.mod index 7130b4d..55d0e3e 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,11 @@ module policy-opa-pdp -go 1.23.4 +go 1.23.3 require ( + bou.ke/monkey v1.0.2 github.com/confluentinc/confluent-kafka-go v1.9.2 - github.com/go-playground/validator/v10 v10.23.0 + github.com/go-playground/validator/v10 v10.24.0 github.com/google/uuid v1.6.0 github.com/oapi-codegen/runtime v1.1.1 github.com/open-policy-agent/opa v0.70.0 @@ -14,10 +15,10 @@ require ( ) require ( - bou.ke/monkey v1.0.2 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/agnivade/levenshtein v1.2.0 // indirect + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -26,10 +27,9 @@ require ( github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/deepmap/oapi-codegen v1.16.3 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -59,11 +59,11 @@ require ( go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/otel/sdk v1.28.0 // indirect go.opentelemetry.io/otel/trace v1.28.0 // indirect - golang.org/x/crypto v0.28.0 // indirect - golang.org/x/net v0.30.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.19.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.7.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/grpc v1.67.1 // indirect diff --git a/go.sum b/go.sum index 4fa9ec5..c376a13 100644 --- a/go.sum +++ b/go.sum @@ -11,16 +11,20 @@ github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZ github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/actgardner/gogen-avro/v10 v10.1.0/go.mod h1:o+ybmVjEa27AAr35FRqU98DJu1fXES56uXniYFv4yDA= github.com/actgardner/gogen-avro/v10 v10.2.1/go.mod h1:QUhjeHPchheYmMDni/Nx7VB0RsT/ee8YIgGY/xpEQgQ= github.com/actgardner/gogen-avro/v9 v9.1.0/go.mod h1:nyTj6wPqDJoxM3qdnjcLv+EnMDSDFqE0qDpva2QRmKc= github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= @@ -59,8 +63,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deepmap/oapi-codegen v1.16.3 h1:GT9G86SbQtT1r8ZB+4Cybi9VGdu1P5ieNvNdEoCSbrA= -github.com/deepmap/oapi-codegen v1.16.3/go.mod h1:JD6ErqeX0nYnhdciLc61Konj3NBASREMlkHOgHn8WAM= github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= @@ -88,8 +90,8 @@ github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= @@ -104,8 +106,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= -github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg= +github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -171,6 +173,7 @@ github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7 github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E= github.com/jhump/protoreflect v1.12.0/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/juju/qthttptest v0.1.1/go.mod h1:aTlAv8TYaflIiTDIQYzxnl1QdPjAg8Q8qJMErpKy6A4= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= @@ -240,6 +243,7 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99 github.com/santhosh-tekuri/jsonschema/v5 v5.0.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -281,8 +285,8 @@ go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -303,8 +307,8 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -313,8 +317,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -326,15 +330,15 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/pkg/decision/decision-provider.go b/pkg/decision/decision-provider.go index 5f45668..a2cedd0 100644 --- a/pkg/decision/decision-provider.go +++ b/pkg/decision/decision-provider.go @@ -1,6 +1,6 @@ // - // ========================LICENSE_START================================= -// Copyright (C) 2024: Deutsche Telekom +// Copyright (C) 2024-2025: Deutsche Telekom // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -24,6 +24,9 @@ package decision import ( "context" "encoding/json" + "github.com/google/uuid" + openapi_types "github.com/oapi-codegen/runtime/types" + "github.com/open-policy-agent/opa/sdk" "net/http" "policy-opa-pdp/consts" "policy-opa-pdp/pkg/log" @@ -34,17 +37,13 @@ import ( "policy-opa-pdp/pkg/pdpstate" "policy-opa-pdp/pkg/utils" "strings" - "fmt" - "github.com/google/uuid" - openapi_types "github.com/oapi-codegen/runtime/types" - "github.com/open-policy-agent/opa/sdk" ) // creates a response code map to ErrorResponseResponseCode var httpToResponseCode = map[int]oapicodegen.ErrorResponseResponseCode{ - 400: oapicodegen.BADREQUEST, - 401: oapicodegen.UNAUTHORIZED, - 500: oapicodegen.INTERNALSERVERERROR, + 400: oapicodegen.BadRequest, + 401: oapicodegen.Unauthorized, + 500: oapicodegen.InternalError, } // Gets responsecode from map @@ -52,7 +51,7 @@ func GetErrorResponseResponseCode(httpStatus int) oapicodegen.ErrorResponseRespo if code, exists := httpToResponseCode[httpStatus]; exists { return code } - return oapicodegen.INTERNALSERVERERROR + return oapicodegen.InternalError } // writes a Successful JSON response to the HTTP response writer @@ -73,46 +72,92 @@ func writeErrorJSONResponse(res http.ResponseWriter, status int, errorDescriptio } } +// creates a success decision response +func createSuccessDecisionResponse(policyName string, output map[string]interface{}) *oapicodegen.OPADecisionResponse { + return &oapicodegen.OPADecisionResponse{ + PolicyName: policyName, + Output: output, + } +} + // creates a decision response based on the provided parameters -func createSuccessDecisionResponse(statusMessage, decision, policyName string, output map[string]interface{}) *oapicodegen.OPADecisionResponse { +func createSuccessDecisionResponseWithStatus(policyName string, output map[string]interface{}, statusMessage string) *oapicodegen.OPADecisionResponse { return &oapicodegen.OPADecisionResponse{ - StatusMessage: &statusMessage, - Decision: (*oapicodegen.OPADecisionResponseDecision)(&decision), - PolicyName: &policyName, - Output: &output, + PolicyName: policyName, + Output: output, + StatusMessage: &statusMessage, } } // creates a decision response based on the provided parameters -func createDecisionExceptionResponse(statusCode int, errorMessage string, errorDetails []string, policyName string) *oapicodegen.ErrorResponse { +func createDecisionExceptionResponse(statusCode int, errorMessage string, policyName string) *oapicodegen.ErrorResponse { responseCode := GetErrorResponseResponseCode(statusCode) return &oapicodegen.ErrorResponse{ ResponseCode: (*oapicodegen.ErrorResponseResponseCode)(&responseCode), ErrorMessage: &errorMessage, - ErrorDetails: &errorDetails, PolicyName: &policyName, } } -// handles HTTP requests for decisions using OPA. +// handles HTTP requests for decisions using OPA. func OpaDecision(res http.ResponseWriter, req *http.Request) { log.Debugf("PDP received a decision request.") + var errorDtls string + var httpStatus int + var policyId = "" + + requestId, _ := processRequestHeaders(req, res) + log.Debugf("Headers processed for requestId: %s", requestId) + + if !isSystemActive() { + errorDtls = "System Is In PASSIVE State, error handling request." + httpStatus = http.StatusInternalServerError + } else if req.Method != http.MethodPost { + errorDtls = req.Method + " MethodNotAllowed" + httpStatus = http.StatusMethodNotAllowed + } else { + decisionReq, err := parseRequestBody(req) + if err != nil { + errorDtls = err.Error() + httpStatus = http.StatusBadRequest + } else if (decisionReq.PolicyName == "") { + errorDtls = "Policy Name is nil which is invalid." + httpStatus = http.StatusBadRequest + } else if (decisionReq.PolicyFilter == nil || len(decisionReq.PolicyFilter) == 0) { + errorDtls = "Policy Filter is nil." + httpStatus = http.StatusBadRequest + } else { + opa, err := getOpaInstance() + if err != nil { + errorDtls = "Failed to get OPA instance." + httpStatus = http.StatusInternalServerError + policyId = decisionReq.PolicyName + } else { + processOpaDecision(res, opa, decisionReq) + return + } + } + } + //If it comes here then there will be error + sendErrorResponse(errorDtls, res, httpStatus, policyId) +} +//This function processes the request headers +func processRequestHeaders(req *http.Request, res http.ResponseWriter) (string, *oapicodegen.DecisionParams) { requestId := req.Header.Get("X-ONAP-RequestID") var parsedUUID *uuid.UUID var decisionParams *oapicodegen.DecisionParams - var err error if requestId != "" && utils.IsValidUUID(requestId) { tempUUID, err := uuid.Parse(requestId) - if err != nil { - log.Warnf("Error Parsing the requestID: %v", err) - } else { + if err == nil { parsedUUID = &tempUUID decisionParams = &oapicodegen.DecisionParams{ XONAPRequestID: (*openapi_types.UUID)(parsedUUID), } res.Header().Set("X-ONAP-RequestID", decisionParams.XONAPRequestID.String()) + } else { + log.Warnf("Error Parsing the requestID: %v", err) } } else { requestId = "Unknown" @@ -123,202 +168,105 @@ func OpaDecision(res http.ResponseWriter, req *http.Request) { res.Header().Set("X-PatchVersion", consts.PatchVersion) res.Header().Set("X-MinorVersion", consts.MinorVersion) - log.Debugf("Headers..") - for key, value := range res.Header() { - log.Debugf("%s: %s", key, value) - } - // Check if the system is in an active state - - if pdpstate.GetCurrentState() != model.Active { - msg := " System Is In PASSIVE State so Unable To Handle Decision wait until it becomes ACTIVE" - errorMsg := " System Is In PASSIVE State so error Handling the request" - decisionExc := createDecisionExceptionResponse(http.StatusInternalServerError, msg, []string{errorMsg}, "") - metrics.IncrementTotalErrorCount() - writeErrorJSONResponse(res, http.StatusInternalServerError, msg, *decisionExc) - return - } - ctx := context.Background() + return requestId, decisionParams +} - // Check if the request method is POST - if req.Method != http.MethodPost { - msg := " MethodNotAllowed" - decisionExc := createDecisionExceptionResponse(http.StatusMethodNotAllowed, "Only POST Method Allowed", - []string{req.Method + msg}, "") - metrics.IncrementTotalErrorCount() - writeErrorJSONResponse(res, http.StatusMethodNotAllowed, req.Method+msg, *decisionExc) - return - } +// This returns whether the system is active or not +func isSystemActive() bool { + return pdpstate.GetCurrentState() == model.Active +} +//This method parses the body and checks whether it is properly formatted JSON or not +func parseRequestBody(req *http.Request) (*oapicodegen.OPADecisionRequest, error) { var decisionReq oapicodegen.OPADecisionRequest - - // Decode the request body into a DecisionRequest struct if err := json.NewDecoder(req.Body).Decode(&decisionReq); err != nil { - decisionExc := createDecisionExceptionResponse(http.StatusBadRequest, "Error decoding the request", - []string{err.Error()}, "") - metrics.IncrementTotalErrorCount() - writeErrorJSONResponse(res, http.StatusBadRequest, err.Error(), *decisionExc) - return + return nil, err } + return &decisionReq, nil +} - // Check if the policy is provided in the request - if decisionReq.PolicyName == nil || *decisionReq.PolicyName == "" { - msg := "Policy used to make decision is nil" - decisionExc := createDecisionExceptionResponse(http.StatusBadRequest, "policy details not provided", - []string{msg}, "") - metrics.IncrementTotalErrorCount() - writeErrorJSONResponse(res, http.StatusBadRequest, msg, *decisionExc) - return - } +//This function sends the error response +func sendErrorResponse(msg string, res http.ResponseWriter, httpStatus int, policyName string) { + log.Warnf("%s", msg) + decisionExc := createDecisionExceptionResponse(httpStatus, msg, policyName) + metrics.IncrementTotalErrorCount() + writeErrorJSONResponse(res, httpStatus, msg, *decisionExc) +} - // Get the OPA singleton instance - opa, err := opasdk.GetOPASingletonInstance() - if err != nil { - msg := "Failed to get OPA instance" - log.Warnf("Failed to get OPA instance: %s", err) - decisionExc := createDecisionExceptionResponse(http.StatusInternalServerError, "OPA instance creation error", []string{msg}, - *decisionReq.PolicyName) - metrics.IncrementTotalErrorCount() - writeErrorJSONResponse(res, http.StatusInternalServerError, msg, *decisionExc) - return - } +//This function returns the opasdk instance +func getOpaInstance() (*sdk.OPA, error) { + return opasdk.GetOPASingletonInstance() +} +//This function processes the OPA decision +func processOpaDecision(res http.ResponseWriter, opa *sdk.OPA, decisionReq *oapicodegen.OPADecisionRequest) { + ctx := context.Background() log.Debugf("SDK making a decision") - options := sdk.DecisionOptions{Path: *decisionReq.PolicyName, Input: decisionReq.Input} - - decision, decision_err := opa.Decision(ctx, options) - - jsonOutput, err := json.MarshalIndent(decision, "", " ") - if err != nil { - log.Warnf("Error serializing decision output: %v\n", err) - return - } - log.Debugf("RAW opa Decision output:\n%s\n", string(jsonOutput)) - - // Check for errors in the OPA decision - if decision_err != nil { - if strings.Contains(decision_err.Error(), "opa_undefined_error") { - decisionRes := createSuccessDecisionResponse(decision_err.Error(), string(oapicodegen.INDETERMINATE), - *decisionReq.PolicyName, nil) - writeOpaJSONResponse(res, http.StatusOK, *decisionRes) - metrics.IncrementIndeterminantDecisionsCount() - return - } else { - decisionExc := createDecisionExceptionResponse(http.StatusBadRequest, "Error from OPA while making decision", - []string{decision_err.Error()}, *decisionReq.PolicyName) - metrics.IncrementTotalErrorCount() - writeErrorJSONResponse(res, http.StatusBadRequest, decision_err.Error(), *decisionExc) + var decisionRes *oapicodegen.OPADecisionResponse + //OPA is seding success with a warning message if "input" parameter is missing, so we need to send success response + if (decisionReq.Input == nil) { + statusMessage := "{\"warning\":{\"code\":\"api_usage_warning\",\"message\":\"'input' key missing from the request\"}}" + decisionRes = createSuccessDecisionResponseWithStatus(decisionReq.PolicyName, nil, statusMessage) + } else { + options := sdk.DecisionOptions{Path: decisionReq.PolicyName, Input: decisionReq.Input} + decisionResult, decisionErr := opa.Decision(ctx, options) + jsonOutput, err := json.MarshalIndent(decisionResult, "", " ") + if err != nil { + log.Warnf("Error serializing decision output: %v\n", err) return } - } - - var policyFilter []string - if decisionReq.PolicyFilter != nil { - policyFilter = *decisionReq.PolicyFilter - } - - // Decision Result Processing - outputMap := make(map[string]interface{}) - // Check if the decision result is a bool or a map - switch result := decision.Result.(type) { - case bool: - // If the result is a boolean (true/false) - if result { - // If "allow" is true, process filters if they exist - if len(policyFilter) > 0 { - // If filters are present, we apply them - decisionRes := createSuccessDecisionResponse("OPA Allowed", string(oapicodegen.PERMIT), *decisionReq.PolicyName, nil) - metrics.IncrementPermitDecisionsCount() - writeOpaJSONResponse(res, http.StatusOK, *decisionRes) - return - } - - // No filters provided, just allow the decision - decisionRes := createSuccessDecisionResponse("OPA Allowed", string(oapicodegen.PERMIT), *decisionReq.PolicyName, nil) - metrics.IncrementPermitDecisionsCount() - writeOpaJSONResponse(res, http.StatusOK, *decisionRes) + log.Debugf("RAW opa Decision output:\n%s\n", string(jsonOutput)) + + if decisionErr != nil { + handleOpaDecisionError(res, decisionErr, decisionReq.PolicyName) return } - - // If "allow" is false - decisionRes := createSuccessDecisionResponse("OPA Denied", string(oapicodegen.DENY), *decisionReq.PolicyName, nil) - metrics.IncrementDenyDecisionsCount() - writeOpaJSONResponse(res, http.StatusOK, *decisionRes) - return - - case map[string]interface{}: - if len(policyFilter) > 0 { - // Apply the policy filter if present - filteredResult := applyPolicyFilter(result, policyFilter) - if filteredResultMap, ok := filteredResult.(map[string]interface{}); ok && len(filteredResultMap) > 0 { - outputMap = filteredResultMap - } else { - decisionRes := createSuccessDecisionResponse( - "No Decision: Result is Empty after applying filter", - string(oapicodegen.NOTAPPLICABLE), - *decisionReq.PolicyName, nil) - metrics.IncrementQueryFailureCount() - writeOpaJSONResponse(res, http.StatusOK, *decisionRes) - return - } - } else { - // Process result without filters - var statusMessage string - boolValueFound := false - for key, value := range result { - if len(statusMessage) == 0 { - statusMessage = fmt.Sprintf("%s: %v", key, value) - } else { - statusMessage = fmt.Sprintf("%s ,%s: %v", statusMessage, key, value) - } - if boolVal, ok := value.(bool); ok { - boolValueFound = boolVal - } - } - // Return decision based on boolean value - if boolValueFound { - decisionRes := createSuccessDecisionResponse(statusMessage, string(oapicodegen.PERMIT), - *decisionReq.PolicyName, nil) - metrics.IncrementPermitDecisionsCount() - writeOpaJSONResponse(res, http.StatusOK, *decisionRes) - return - } else { - decisionRes := createSuccessDecisionResponse(statusMessage, string(oapicodegen.DENY), - *decisionReq.PolicyName, nil) - metrics.IncrementDenyDecisionsCount() - writeOpaJSONResponse(res, http.StatusOK, *decisionRes) - return - } - + + var policyFilter []string + if decisionReq.PolicyFilter != nil { + policyFilter = decisionReq.PolicyFilter } - - // If only non-boolean values were collected - if len(outputMap) > 0 { - decisionRes := createSuccessDecisionResponse( - "Decision Not Applicable, Output Only", - string(oapicodegen.NOTAPPLICABLE), - *decisionReq.PolicyName, outputMap) - metrics.IncrementQuerySuccessCount() - writeOpaJSONResponse(res, http.StatusOK, *decisionRes) + result, _ := decisionResult.Result.(map[string]interface{}) + outputMap := processPolicyFilter(result, policyFilter) + if outputMap == nil { + decisionRes = createSuccessDecisionResponseWithStatus(decisionReq.PolicyName, outputMap, "Policy Filter is not matching.") } else { - decisionRes := createSuccessDecisionResponse( - "No Decision: Result is Empty", - string(oapicodegen.NOTAPPLICABLE), - *decisionReq.PolicyName, nil) - metrics.IncrementQueryFailureCount() - writeOpaJSONResponse(res, http.StatusOK, *decisionRes) + decisionRes = createSuccessDecisionResponse(decisionReq.PolicyName, outputMap) } - return + } + metrics.IncrementDecisionSuccessCount() + writeOpaJSONResponse(res, http.StatusOK, *decisionRes) +} - default: - // Handle unexpected types in decision.Result - decisionRes := createSuccessDecisionResponse("Invalid decision result format", string(oapicodegen.INDETERMINATE), *decisionReq.PolicyName, nil) - metrics.IncrementIndeterminantDecisionsCount() - writeOpaJSONResponse(res, http.StatusOK, *decisionRes) - return +//This function validates the errors during decision process +func handleOpaDecisionError(res http.ResponseWriter, err error, policyName string) { + //As per the opa documentation in https://www.openpolicyagent.org/docs/latest/rest-api/#get-a-document-with-input + //when the path refers to an undefined document it will return 200 with no result. + //opasdk is returning opa_undefined_error for such case, so need to give sucess for such case and + //for other cases we have to send error response + if strings.Contains(err.Error(), string(oapicodegen.OpaUndefinedError)) { + decisionExc := createSuccessDecisionResponse(policyName, nil) + metrics.IncrementDecisionSuccessCount() + writeOpaJSONResponse(res, http.StatusOK, *decisionExc) + } else { + decisionExc := createDecisionExceptionResponse(http.StatusInternalServerError, err.Error(), policyName) + metrics.IncrementTotalErrorCount() + writeErrorJSONResponse(res, http.StatusInternalServerError, err.Error(), *decisionExc) } +} +//This function processes the policy filters +func processPolicyFilter(result map[string]interface{}, policyFilter []string) map[string]interface{} { + if len(policyFilter) > 0 { + filteredResult := applyPolicyFilter(result, policyFilter) + if filteredMap, ok := filteredResult.(map[string]interface{}); ok && len(filteredMap) > 0 { + return filteredMap + } + } + return nil } + // Function to apply policy filter to decision result func applyPolicyFilter(result map[string]interface{}, filters []string) interface{} { @@ -336,3 +284,4 @@ func applyPolicyFilter(result map[string]interface{}, filters []string) interfac return filteredOutput } + diff --git a/pkg/decision/decision-provider_test.go b/pkg/decision/decision-provider_test.go index 3a3a105..ef9e36a 100644 --- a/pkg/decision/decision-provider_test.go +++ b/pkg/decision/decision-provider_test.go @@ -25,6 +25,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "github.com/open-policy-agent/opa/sdk" "net/http" "net/http/httptest" @@ -36,10 +37,10 @@ import ( "policy-opa-pdp/pkg/pdpstate" "reflect" "testing" - "github.com/stretchr/testify/assert" ) +//Test for Invalid request method func TestOpaDecision_MethodNotAllowed(t *testing.T) { originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { @@ -55,6 +56,7 @@ func TestOpaDecision_MethodNotAllowed(t *testing.T) { assert.Contains(t, rec.Body.String(), "MethodNotAllowed") } +//Test for invalid JSON request func TestOpaDecision_InvalidJSON(t *testing.T) { originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { @@ -69,6 +71,7 @@ func TestOpaDecision_InvalidJSON(t *testing.T) { assert.Equal(t, http.StatusBadRequest, rec.Code) } +//Test for Missing Policy func TestOpaDecision_MissingPolicyPath(t *testing.T) { originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { @@ -84,9 +87,29 @@ func TestOpaDecision_MissingPolicyPath(t *testing.T) { OpaDecision(rec, req) assert.Equal(t, http.StatusBadRequest, rec.Code) - assert.Contains(t, rec.Body.String(), "Policy used to make decision is nil") + assert.Contains(t, rec.Body.String(), "Policy Name is nil which is invalid") +} + +//Test for Missing Policy Filter +func TestOpaDecision_MissingPolicyFilter(t *testing.T) { + originalGetState := pdpstate.GetCurrentState + pdpstate.GetCurrentState = func() model.PdpState { + return model.Active + } + defer func() { pdpstate.GetCurrentState = originalGetState }() + body := map[string]interface{}{"onapName": "CDS", "policyName": "data.policy", "onapComponent": "CDS", "onapInstance": "CDS", "requestId": "8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1", "input": nil} + + jsonBody, _ := json.Marshal(body) + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(jsonBody)) + rec := httptest.NewRecorder() + + OpaDecision(rec, req) + + assert.Equal(t, http.StatusBadRequest, rec.Code) + assert.Contains(t, rec.Body.String(), "Policy Filter is nil") } +//Test for OPA Instance Error func TestOpaDecision_GetInstanceError(t *testing.T) { originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { @@ -103,6 +126,7 @@ func TestOpaDecision_GetInstanceError(t *testing.T) { assert.Equal(t, http.StatusBadRequest, rec.Code) } +//Test for OPA decision Error func TestOpaDecision_OPADecisionError(t *testing.T) { originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { @@ -127,6 +151,7 @@ func TestOpaDecision_OPADecisionError(t *testing.T) { assert.Equal(t, http.StatusBadRequest, rec.Code) } +//Test for system in passive State func TestOpaDecision_PassiveState(t *testing.T) { originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { @@ -139,50 +164,45 @@ func TestOpaDecision_PassiveState(t *testing.T) { OpaDecision(rec, req) assert.Equal(t, http.StatusInternalServerError, rec.Code) - assert.Contains(t, rec.Body.String(), " System Is In PASSIVE State") + assert.Contains(t, rec.Body.String(), "System Is In PASSIVE State") } -// New // TestOpaDecision_ValidRequest tests if the request is handled correctly // Utility function to return a pointer to a string -func ptrString(s string) *string { - return &s +func ptrString(s string) string { + return s } +func ptrStringEx(s string) *string { + return &s +} // Utility function to return a pointer to a map -func ptrMap(m map[string]interface{}) *map[string]interface{} { - return &m +func ptrMap(m map[string]interface{}) map[string]interface{} { + return m } // Utility function to return a pointer to a OPADecisionResponseDecision -func ptrOPADecisionResponseDecision(decision oapicodegen.OPADecisionResponseDecision) *oapicodegen.OPADecisionResponseDecision { - return &decision -} - func TestWriteOpaJSONResponse(t *testing.T) { rec := httptest.NewRecorder() - // Use correct type for Decision, which is a pointer to OPADecisionResponseDecision - decision := oapicodegen.OPADecisionResponseDecision("PERMIT") data := &oapicodegen.OPADecisionResponse{ - Decision: ptrOPADecisionResponseDecision(decision), // Correct use of pointer - PolicyName: ptrString("test-policy"), - Output: ptrMap(map[string]interface{}{"key": "value"}), + PolicyName: ptrString("test-policy"), + Output: ptrMap(map[string]interface{}{"key": "value"}), } writeOpaJSONResponse(rec, http.StatusOK, *data) assert.Equal(t, http.StatusOK, rec.Code) - assert.Contains(t, rec.Body.String(), `"decision":"PERMIT"`) assert.Contains(t, rec.Body.String(), `"policyName":"test-policy"`) } +//Test for JSON response error func TestWriteErrorJSONResponse(t *testing.T) { rec := httptest.NewRecorder() // ErrorResponse struct uses pointers for string fields, so we use ptrString() errorResponse := oapicodegen.ErrorResponse{ - ErrorMessage: ptrString("Bad Request"), + ErrorMessage: ptrStringEx("Bad Request"), } writeErrorJSONResponse(rec, http.StatusBadRequest, "Bad Request", errorResponse) @@ -191,31 +211,26 @@ func TestWriteErrorJSONResponse(t *testing.T) { assert.Contains(t, rec.Body.String(), `"errorMessage":"Bad Request"`) } +//Test for Success Decision Response func TestCreateSuccessDecisionResponse(t *testing.T) { // Input values for creating the response - statusMessage := "Success" - decision := oapicodegen.OPADecisionResponseDecision("PERMIT") policyName := "policy-name" output := map[string]interface{}{"key": "value"} // Call the createSuccessDecisionResponse function - response := createSuccessDecisionResponse(statusMessage, string(decision), policyName, output) + response := createSuccessDecisionResponse( + policyName, output) // Assertions - // Check the StatusMessage field - assert.Equal(t, *response.StatusMessage, statusMessage, "StatusMessage should match") - - // Check the Decision field (it should be a pointer to the string "PERMIT") - assert.Equal(t, *response.Decision, decision, "Decision should match") - // Check the PolicyName field - assert.Equal(t, *response.PolicyName, policyName, "PolicyName should match") + assert.Equal(t, response.PolicyName, policyName, "PolicyName should match") // Check the Output field - assert.Equal(t, *response.Output, output, "Output should match") + assert.Equal(t, response.Output, output, "Output should match") } +//Test for policy filter func TestApplyPolicyFilter(t *testing.T) { originalPolicy := map[string]interface{}{ "policy1": map[string]interface{}{"key1": "value1"}, @@ -229,40 +244,34 @@ func TestApplyPolicyFilter(t *testing.T) { assert.Contains(t, result, "policy1") } +//Test for Opa response error func TestWriteOpaJSONResponse_Error(t *testing.T) { rec := httptest.NewRecorder() // Simulate an error response - statusMessage := "Error processing request" - decision := oapicodegen.OPADecisionResponseDecision("DENY") policyName := "error-policy" output := map[string]interface{}{"errorDetail": "Invalid input"} // Create a response object for error scenario data := &oapicodegen.OPADecisionResponse{ - Decision: ptrOPADecisionResponseDecision(decision), // Use correct pointer PolicyName: ptrString(policyName), Output: ptrMap(output), - StatusMessage: ptrString(statusMessage), } writeOpaJSONResponse(rec, http.StatusBadRequest, *data) // Assertions assert.Equal(t, http.StatusBadRequest, rec.Code, "Expected HTTP 400 status code") - assert.Contains(t, rec.Body.String(), `"decision":"DENY"`, "Response should contain 'DENY' decision") assert.Contains(t, rec.Body.String(), `"policyName":"error-policy"`, "Response should contain the policy name") - assert.Contains(t, rec.Body.String(), `"statusMessage":"Error processing request"`, "Response should contain the status message") assert.Contains(t, rec.Body.String(), `"errorDetail":"Invalid input"`, "Response should contain the error detail") } +//Test for JSON response success func TestWriteOpaJSONResponse_Success(t *testing.T) { // Prepare test data decisionRes := oapicodegen.OPADecisionResponse{ - StatusMessage: ptrString("Success"), - Decision: (*oapicodegen.OPADecisionResponseDecision)(ptrString("PERMIT")), PolicyName: ptrString("TestPolicy"), - Output: &map[string]interface{}{"key": "value"}, + Output: map[string]interface{}{"key": "value"}, } // Create a mock HTTP response writer @@ -286,16 +295,14 @@ func TestWriteOpaJSONResponse_Success(t *testing.T) { if err := json.NewDecoder(res.Body).Decode(&result); err != nil { t.Fatalf("Failed to decode response body: %v", err) } - if *result.StatusMessage != "Success" { - t.Errorf("Expected StatusMessage 'Success', got '%s'", *result.StatusMessage) - } } +// Test for JSON encoding errors func TestWriteOpaJSONResponse_EncodingError(t *testing.T) { - // Prepare invalid test data to trigger JSON encoding error - decisionRes := oapicodegen.OPADecisionResponse{ + // Prepare invalid test data to trigger JSON encoding error + decisionRes := oapicodegen.OPADecisionResponse { // Introducing an invalid type to cause encoding failure - Output: &map[string]interface{}{"key": make(chan int)}, + Output: map[string]interface{}{"key": make(chan int)}, } // Create a mock HTTP response writer @@ -319,12 +326,6 @@ func TestWriteOpaJSONResponse_EncodingError(t *testing.T) { var GetOPASingletonInstance = opasdk.GetOPASingletonInstance var mockDecisionResult = &sdk.DecisionResult{ - Result: map[string]interface{}{ - "allowed": true, - }, -} - -var mockDecisionResult2 = &sdk.DecisionResult{ Result: map[string]interface{}{ "allow": "true", }, @@ -335,9 +336,6 @@ var mockDecisionResultUnexp = &sdk.DecisionResult{ 123: 123, }, } -var mockDecisionResultBoolFalse = &sdk.DecisionResult{ - Result: false, -} var mockDecisionResultBool = &sdk.DecisionResult{ Result: true, @@ -345,14 +343,12 @@ var mockDecisionResultBool = &sdk.DecisionResult{ var mockDecisionReq = oapicodegen.OPADecisionRequest{ PolicyName: ptrString("mockPolicy"), - PolicyFilter: &[]string{"filter1", "filter2"}, - //Input: map[string]interface{}{"key": "value"}, + PolicyFilter: []string{"filter1", "filter2"}, } var mockDecisionReq2 = oapicodegen.OPADecisionRequest{ PolicyName: ptrString("mockPolicy"), - PolicyFilter: &[]string{"allow", "filter2"}, - //Input: map[string]interface{}{"key": "value"}, + PolicyFilter: []string{"allow", "filter2"}, } // Test to check invalid UUID in request @@ -378,7 +374,10 @@ func Test_Invalid_request_UUID(t *testing.T) { return model.Active } defer func() { pdpstate.GetCurrentState = originalGetState }() - body := map[string]interface{}{"PolicyName": "data.policy"} + jsonString := `{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"s3","policyFilter":["allow"],"input":{"content" : "content"}}` + var decisionReq oapicodegen.OPADecisionRequest + json.Unmarshal([]byte(jsonString), &decisionReq) + body := map[string]interface{}{"PolicyName": decisionReq.PolicyName, "PolicyFilter": decisionReq.PolicyFilter,} jsonBody, _ := json.Marshal(body) req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) req.Header.Set("X-ONAP-RequestID", "valid-uuid") @@ -425,14 +424,15 @@ func Test_valid_HTTP_method(t *testing.T) { ) defer patch.Unpatch() - body := map[string]interface{}{"PolicyName": jsonString} + var decisionReq oapicodegen.OPADecisionRequest + json.Unmarshal([]byte(jsonString), &decisionReq) + body := map[string]interface{}{"PolicyName": decisionReq.PolicyName, "PolicyFilter": decisionReq.PolicyFilter,} jsonBody, _ := json.Marshal(body) req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) res := httptest.NewRecorder() OpaDecision(res, req) assert.Equal(t, http.StatusOK, res.Code) - assert.Contains(t, res.Body.String(), "PERMIT") } // Test for Marshalling error in Decision Result @@ -458,115 +458,103 @@ func Test_Error_Marshalling(t *testing.T) { }, ) defer patch.Unpatch() - body := map[string]interface{}{"PolicyName": jsonString} - jsonBody, _ := json.Marshal(body) - req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) - res := httptest.NewRecorder() - - OpaDecision(res, req) - assert.Equal(t, http.StatusOK, res.Code) - assert.Empty(t, res.Body.String()) -} -// Test for Policy filter with invalid/not applicable Decision result -func Test_Policy_Filter_with_invalid_decision_result(t *testing.T) { - originalGetState := pdpstate.GetCurrentState - pdpstate.GetCurrentState = func() model.PdpState { - return model.Active - } - defer func() { pdpstate.GetCurrentState = originalGetState }() - jsonString := `{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"s3","policyFilter":["allow"],"input":{"content" : "content"}}` - - var patch *monkey.PatchGuard - - patch = monkey.PatchInstanceMethod( - reflect.TypeOf(&sdk.OPA{}), "Decision", - func(_ *sdk.OPA, _ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { - return mockDecisionResult, nil - }, - ) - defer patch.Unpatch() - body := map[string]interface{}{"PolicyName": jsonString} + var decisionReq oapicodegen.OPADecisionRequest + json.Unmarshal([]byte(jsonString), &decisionReq) + body := map[string]interface{}{"PolicyName": decisionReq.PolicyName, "PolicyFilter": decisionReq.PolicyFilter,} jsonBody, _ := json.Marshal(body) req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) res := httptest.NewRecorder() - var patch1 *monkey.PatchGuard - patch1 = monkey.PatchInstanceMethod( - reflect.TypeOf(&json.Decoder{}), "Decode", - func(_ *json.Decoder, v interface{}) error { - if req, ok := v.(*oapicodegen.OPADecisionRequest); ok { - *req = mockDecisionReq - } - return nil - }, - ) - defer patch1.Unpatch() OpaDecision(res, req) - assert.Equal(t, http.StatusOK, res.Code) - assert.Contains(t, res.Body.String(), "NOTAPPLICABLE") + assert.NotEmpty(t, res.Body.String()) } -// Test with OPA Decision of boolean type true -func Test_with_boolean_OPA_Decision(t *testing.T) { +// Test for Invalid Decision error in Decision Result +func Test_Invalid_Decision(t *testing.T) { + // Mock PDP state originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { return model.Active } defer func() { pdpstate.GetCurrentState = originalGetState }() - jsonString := `{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"s3","policyFilter":["allow"],"input":{"content" : "content"}}` - var patch *monkey.PatchGuard - patch = monkey.PatchInstanceMethod( + // Define a request body that matches expected input format + jsonString := `{ + "policyName": "s3", + "policyFilter": ["allow"], + "input": {"content": "content"} + }` + + // Patch the OPA Decision method to return an error + patch := monkey.PatchInstanceMethod( reflect.TypeOf(&sdk.OPA{}), "Decision", func(_ *sdk.OPA, _ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { - return mockDecisionResultBool, nil + // Return an explicit error + return nil, fmt.Errorf("opa_undefined_error") }, ) defer patch.Unpatch() - body := map[string]interface{}{"PolicyName": jsonString} - jsonBody, _ := json.Marshal(body) - req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) + // Create a test HTTP request + req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer([]byte(jsonString))) + req.Header.Set("Content-Type", "application/json") res := httptest.NewRecorder() + + // Call the handler function that processes OPA decision OpaDecision(res, req) - assert.Equal(t, http.StatusOK, res.Code) - assert.Contains(t, res.Body.String(), "PERMIT") + // Assert that the response status code is 400 + assert.Equal(t, 200, res.Code) } -// Test with OPA Decision of boolean type with false -func Test_Successful_decision_allow_false(t *testing.T) { +// Test for Invalid Decision error in Decision Result +func Test_Valid_Decision_String(t *testing.T) { + // Mock PDP state originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { return model.Active } defer func() { pdpstate.GetCurrentState = originalGetState }() - jsonString := `{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"s3","policyFilter":["allow"],"input":{"content" : "content"}}` - var patch *monkey.PatchGuard - patch = monkey.PatchInstanceMethod( + // Define a request body that matches expected input format + jsonString := `{ + "policyName": "s3", + "policyFilter": ["allow"], + "input": {"content": "content"} + }` + + // Patch the OPA Decision method to return an error + patch := monkey.PatchInstanceMethod( reflect.TypeOf(&sdk.OPA{}), "Decision", func(_ *sdk.OPA, _ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { - return mockDecisionResultBool, nil + // Return an explicit error + mockDecisionResult := &sdk.DecisionResult{ + Result: map[string]interface{}{ + "allowed": "true", + }, + } + return mockDecisionResult, nil }, ) + defer patch.Unpatch() - body := map[string]interface{}{"PolicyName": jsonString} - jsonBody, _ := json.Marshal(body) - req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) + // Create a test HTTP request + req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer([]byte(jsonString))) + req.Header.Set("Content-Type", "application/json") res := httptest.NewRecorder() + // Call the handler function that processes OPA decision OpaDecision(res, req) - assert.Equal(t, http.StatusOK, res.Code) - assert.Contains(t, res.Body.String(), "OPA Allowed") + // Assert that the response status code is 400 + assert.Equal(t, 200, res.Code) } -// Test with OPA Decision of boolean type with false having filter -func Test_decision_result_false_with_Filter(t *testing.T) { +// Test for Policy filter with invalid/not applicable Decision result +func Test_Policy_Filter_with_invalid_decision_result(t *testing.T) { originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { return model.Active @@ -579,8 +567,7 @@ func Test_decision_result_false_with_Filter(t *testing.T) { patch = monkey.PatchInstanceMethod( reflect.TypeOf(&sdk.OPA{}), "Decision", func(_ *sdk.OPA, _ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { - // Simulate an error to trigger the second error block - return mockDecisionResultBool, nil + return mockDecisionResult, nil }, ) defer patch.Unpatch() @@ -603,11 +590,10 @@ func Test_decision_result_false_with_Filter(t *testing.T) { OpaDecision(res, req) assert.Equal(t, http.StatusOK, res.Code) - assert.Contains(t, res.Body.String(), "OPA Allowed") } -// Test with OPA Decision of boolean type with true having filter -func Test_decision_result_true_with_Filter(t *testing.T) { +// Test with OPA Decision of boolean type true +func Test_with_boolean_OPA_Decision(t *testing.T) { originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { return model.Active @@ -616,35 +602,26 @@ func Test_decision_result_true_with_Filter(t *testing.T) { jsonString := `{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"s3","policyFilter":["allow"],"input":{"content" : "content"}}` var patch *monkey.PatchGuard - patch = monkey.PatchInstanceMethod( reflect.TypeOf(&sdk.OPA{}), "Decision", func(_ *sdk.OPA, _ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { - return mockDecisionResultBoolFalse, nil + return mockDecisionResultBool, nil }, ) defer patch.Unpatch() - body := map[string]interface{}{"PolicyName": jsonString} + + var decisionReq oapicodegen.OPADecisionRequest + json.Unmarshal([]byte(jsonString), &decisionReq) + body := map[string]interface{}{"PolicyName": decisionReq.PolicyName, "PolicyFilter": decisionReq.PolicyFilter,} jsonBody, _ := json.Marshal(body) req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) res := httptest.NewRecorder() - var patch1 *monkey.PatchGuard - patch1 = monkey.PatchInstanceMethod( - reflect.TypeOf(&json.Decoder{}), "Decode", - func(_ *json.Decoder, v interface{}) error { - if req, ok := v.(*oapicodegen.OPADecisionRequest); ok { - *req = mockDecisionReq - } - return nil - }, - ) - defer patch1.Unpatch() OpaDecision(res, req) assert.Equal(t, http.StatusOK, res.Code) - assert.Contains(t, res.Body.String(), "Denied") } + // Test with OPA Decision with String type func Test_decision_Result_String(t *testing.T) { originalGetState := pdpstate.GetCurrentState @@ -652,7 +629,7 @@ func Test_decision_Result_String(t *testing.T) { return model.Active } defer func() { pdpstate.GetCurrentState = originalGetState }() - jsonString := `{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"s3","policyFilter":["allow"],"input":{"content" : "content"}}` + jsonString := `{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"s3","policyFilter":["allowed"],"input":{"content" : "content"}}` var patch *monkey.PatchGuard @@ -662,14 +639,16 @@ func Test_decision_Result_String(t *testing.T) { // Create a mock result with an incompatible field (e.g., a channel) mockDecisionResult := &sdk.DecisionResult{ Result: map[string]interface{}{ - "allowed": "deny", + "allowed": "true", }, } return mockDecisionResult, nil }, ) defer patch.Unpatch() - body := map[string]interface{}{"PolicyName": jsonString} + var decisionReq oapicodegen.OPADecisionRequest + json.Unmarshal([]byte(jsonString), &decisionReq) + body := map[string]interface{}{"PolicyName": decisionReq.PolicyName, "PolicyFilter": decisionReq.PolicyFilter,} jsonBody, _ := json.Marshal(body) req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) res := httptest.NewRecorder() @@ -677,7 +656,6 @@ func Test_decision_Result_String(t *testing.T) { OpaDecision(res, req) assert.Equal(t, http.StatusOK, res.Code) - assert.Contains(t, res.Body.String(), "DENY") } // Test with OPA Decision with String type wth filtered result @@ -695,7 +673,7 @@ func Test_decision_Result_String_with_filtered_Result(t *testing.T) { reflect.TypeOf(&sdk.OPA{}), "Decision", func(_ *sdk.OPA, _ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { // Simulate an error to trigger the second error block - return mockDecisionResult2, nil + return mockDecisionResult, nil }, ) defer patch.Unpatch() @@ -717,7 +695,6 @@ func Test_decision_Result_String_with_filtered_Result(t *testing.T) { OpaDecision(res, req) assert.Equal(t, http.StatusOK, res.Code) - assert.Contains(t, res.Body.String(), "NOTAPPLICABLE") } @@ -758,7 +735,6 @@ func Test_decision_with_filtered_Result_as_unexpected_Res_Type(t *testing.T) { OpaDecision(res, req) assert.Equal(t, http.StatusOK, res.Code) - assert.Contains(t, res.Body.String(), "INDETERMINATE") } // Test with OPA Decision with Error in response @@ -767,9 +743,7 @@ func TestWriteErrorJSONResponse_EncodingFailure(t *testing.T) { errorMessage := "Test error message" policyName := "TestPolicy" responseCode := oapicodegen.ErrorResponseResponseCode("500") - errorDetails := []string{"Detail 1", "Detail 2"} mockDecisionExc := oapicodegen.ErrorResponse{ - ErrorDetails: &errorDetails, ErrorMessage: &errorMessage, PolicyName: &policyName, ResponseCode: &responseCode, diff --git a/pkg/kafkacomm/publisher/pdp-heartbeat.go b/pkg/kafkacomm/publisher/pdp-heartbeat.go index 0c48189..42a5e70 100644 --- a/pkg/kafkacomm/publisher/pdp-heartbeat.go +++ b/pkg/kafkacomm/publisher/pdp-heartbeat.go @@ -69,7 +69,9 @@ func StartHeartbeatIntervalTimer(intervalMs int64, s PdpStatusSender) { for { select { case <-ticker.C: - sendPDPHeartBeat(s) + if err := sendPDPHeartBeat(s); err != nil { + log.Errorf("Failed to send PDP Heartbeat: %v", err) + } case <-stopChan: ticker.Stop() return diff --git a/pkg/metrics/counters.go b/pkg/metrics/counters.go index bbf148b..e126554 100644 --- a/pkg/metrics/counters.go +++ b/pkg/metrics/counters.go @@ -1,6 +1,6 @@ // - // ========================LICENSE_START================================= -// Copyright (C) 2024: Deutsche Telekom +// Copyright (C) 2024-2025: Deutsche Telekom // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,55 +22,14 @@ package metrics import "sync" // global counter variables -var IndeterminantDecisionsCount int64 -var PermitDecisionsCount int64 -var DenyDecisionsCount int64 var TotalErrorCount int64 -var QuerySuccessCount int64 -var QueryFailureCount int64 +var DecisionSuccessCount int64 +var DecisionFailureCount int64 var mu sync.Mutex -// Increment counter -func IncrementIndeterminantDecisionsCount() { - mu.Lock() - IndeterminantDecisionsCount++ - mu.Unlock() -} - -// returns pointer to the counter -func IndeterminantDecisionsCountRef() *int64 { - mu.Lock() - defer mu.Unlock() - return &IndeterminantDecisionsCount -} - -// Increment counter -func IncrementPermitDecisionsCount() { - mu.Lock() - PermitDecisionsCount++ - mu.Unlock() -} -// returns pointer to the counter -func PermitDecisionsCountRef() *int64 { - mu.Lock() - defer mu.Unlock() - return &PermitDecisionsCount -} -// Increment counter -func IncrementDenyDecisionsCount() { - mu.Lock() - DenyDecisionsCount++ - mu.Unlock() -} -// returns pointer to the counter -func DenyDecisionsCountRef() *int64 { - mu.Lock() - defer mu.Unlock() - return &DenyDecisionsCount -} // Increment counter func IncrementTotalErrorCount() { @@ -87,31 +46,31 @@ func TotalErrorCountRef() *int64 { } // Increment counter -func IncrementQuerySuccessCount() { +func IncrementDecisionSuccessCount() { mu.Lock() - QuerySuccessCount++ + DecisionSuccessCount++ mu.Unlock() } // returns pointer to the counter -func TotalQuerySuccessCountRef() *int64 { +func TotalDecisionSuccessCountRef() *int64 { mu.Lock() defer mu.Unlock() - return &QuerySuccessCount + return &DecisionSuccessCount } // Increment counter -func IncrementQueryFailureCount() { +func IncrementDecisionFailureCount() { mu.Lock() - QueryFailureCount++ + DecisionFailureCount++ mu.Unlock() } // returns pointer to the counter -func TotalQueryFailureCountRef() *int64 { +func TotalDecisionFailureCountRef() *int64 { mu.Lock() defer mu.Unlock() - return &QueryFailureCount + return &DecisionFailureCount } diff --git a/pkg/metrics/counters_test.go b/pkg/metrics/counters_test.go index e421e46..ba8646b 100644 --- a/pkg/metrics/counters_test.go +++ b/pkg/metrics/counters_test.go @@ -29,42 +29,6 @@ import ( func TestCounters(t *testing.T) { var wg sync.WaitGroup - // Test IncrementIndeterminantDecisionsCount and IndeterminantDecisionsCountRef - IndeterminantDecisionsCount = 0 - wg.Add(10) - for i := 0; i < 10; i++ { - go func() { - defer wg.Done() - IncrementIndeterminantDecisionsCount() - }() - } - wg.Wait() - assert.Equal(t, int64(10), *IndeterminantDecisionsCountRef()) - - // Test IncrementPermitDecisionsCount and PermitDecisionsCountRef - PermitDecisionsCount = 0 - wg.Add(15) - for i := 0; i < 15; i++ { - go func() { - defer wg.Done() - IncrementPermitDecisionsCount() - }() - } - wg.Wait() - assert.Equal(t, int64(15), *PermitDecisionsCountRef()) - - // Test IncrementDenyDecisionsCount and DenyDecisionsCountRef - DenyDecisionsCount = 0 - wg.Add(20) - for i := 0; i < 20; i++ { - go func() { - defer wg.Done() - IncrementDenyDecisionsCount() - }() - } - wg.Wait() - assert.Equal(t, int64(20), *DenyDecisionsCountRef()) - // Test IncrementTotalErrorCount and TotalErrorCountRef TotalErrorCount = 0 wg.Add(5) @@ -79,7 +43,7 @@ func TestCounters(t *testing.T) { // Test IncrementQuerySuccessCount and TotalQuerySuccessCountRef - QuerySuccessCount = 0 + DecisionSuccessCount = 0 wg.Add(7) @@ -89,7 +53,7 @@ func TestCounters(t *testing.T) { defer wg.Done() - IncrementQuerySuccessCount() + IncrementDecisionSuccessCount() }() @@ -97,11 +61,11 @@ func TestCounters(t *testing.T) { wg.Wait() - assert.Equal(t, int64(7), *TotalQuerySuccessCountRef()) + assert.Equal(t, int64(7), *TotalDecisionSuccessCountRef()) - // Test IncrementQueryFailureCount and TotalQueryFailureCountRef + // Test IncrementDecisionFailureCount and TotalDecisionFailureCountRef - QueryFailureCount = 0 + DecisionFailureCount = 0 wg.Add(3) @@ -111,7 +75,7 @@ func TestCounters(t *testing.T) { defer wg.Done() - IncrementQueryFailureCount() + IncrementDecisionFailureCount() }() @@ -119,6 +83,6 @@ func TestCounters(t *testing.T) { wg.Wait() - assert.Equal(t, int64(3), *TotalQueryFailureCountRef()) + assert.Equal(t, int64(3), *TotalDecisionFailureCountRef()) } diff --git a/pkg/metrics/statistics-provider.go b/pkg/metrics/statistics-provider.go index ba55b27..9b34839 100644 --- a/pkg/metrics/statistics-provider.go +++ b/pkg/metrics/statistics-provider.go @@ -1,6 +1,6 @@ // - // ========================LICENSE_START================================= -// Copyright (C) 2024: Deutsche Telekom +// Copyright (C) 2024-2025: Deutsche Telekom // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,8 +17,7 @@ // ========================LICENSE_END=================================== // -// Handles an HTTP request to fetch the current system statistics. -// It aggregates various decision counts (e.g., indeterminate, permit, deny) +// Handles an HTTP request to fetch the current system statistics // and error counts into a structured response and sends it back to the client in JSON format. package metrics @@ -58,12 +57,9 @@ func FetchCurrentStatistics(res http.ResponseWriter, req *http.Request) { var statReport oapicodegen.StatisticsReport - statReport.IndeterminantDecisionsCount = IndeterminantDecisionsCountRef() - statReport.PermitDecisionsCount = PermitDecisionsCountRef() - statReport.DenyDecisionsCount = DenyDecisionsCountRef() + statReport.DecisionSuccessCount = TotalDecisionSuccessCountRef() + statReport.DecisionFailureCount = TotalDecisionFailureCountRef() statReport.TotalErrorCount = TotalErrorCountRef() - statReport.QuerySuccessCount = TotalQuerySuccessCountRef() - statReport.QueryFailureCount = TotalQueryFailureCountRef() // not implemented hardcoding the values to zero // will be implemeneted in phase-2 @@ -81,6 +77,9 @@ func FetchCurrentStatistics(res http.ResponseWriter, req *http.Request) { res.Header().Set("Content-Type", "application/json") res.WriteHeader(http.StatusOK) - json.NewEncoder(res).Encode(statReport) - + if err := json.NewEncoder(res).Encode(statReport); err != nil { + log.Errorf("Failed to encode JSON response: %v", err) + http.Error(res, "Internal Server Error", http.StatusInternalServerError) + return + } } diff --git a/pkg/metrics/statistics-provider_test.go b/pkg/metrics/statistics-provider_test.go index 6f90182..cf745a2 100644 --- a/pkg/metrics/statistics-provider_test.go +++ b/pkg/metrics/statistics-provider_test.go @@ -31,9 +31,6 @@ import ( func TestFetchCurrentStatistics(t *testing.T) { - IndeterminantDecisionsCount = 10 - PermitDecisionsCount = 15 - DenyDecisionsCount = 20 TotalErrorCount = 5 // Create a new HTTP request @@ -55,9 +52,6 @@ func TestFetchCurrentStatistics(t *testing.T) { assert.NoError(t, err) // Verify the response body - assert.Equal(t, int64(10), *statReport.IndeterminantDecisionsCount) - assert.Equal(t, int64(15), *statReport.PermitDecisionsCount) - assert.Equal(t, int64(20), *statReport.DenyDecisionsCount) assert.Equal(t, int64(5), *statReport.TotalErrorCount) assert.Equal(t, int64(0), *statReport.TotalPoliciesCount) assert.Equal(t, int64(1), *statReport.TotalPolicyTypesCount) diff --git a/pkg/model/oapicodegen/models.go b/pkg/model/oapicodegen/models.go index b6dc75e..e90b94b 100644 --- a/pkg/model/oapicodegen/models.go +++ b/pkg/model/oapicodegen/models.go @@ -15,43 +15,20 @@ const ( // Defines values for ErrorResponseResponseCode. const ( - BADGATEWAY ErrorResponseResponseCode = "BAD_GATEWAY" - BADREQUEST ErrorResponseResponseCode = "BAD_REQUEST" - CONFLICT ErrorResponseResponseCode = "CONFLICT" - EXPECTATIONFAILED ErrorResponseResponseCode = "EXPECTATION_FAILED" - GATEWAYTIMEOUT ErrorResponseResponseCode = "GATEWAY_TIMEOUT" - GONE ErrorResponseResponseCode = "GONE" - HTTPVERSIONNOTSUPPORTED ErrorResponseResponseCode = "HTTP_VERSION_NOT_SUPPORTED" - INTERNALSERVERERROR ErrorResponseResponseCode = "INTERNAL_SERVER_ERROR" - LENGTHREQUIRED ErrorResponseResponseCode = "LENGTH_REQUIRED" - METHODNOTALLOWED ErrorResponseResponseCode = "METHOD_NOT_ALLOWED" - NETWORKAUTHENTICATIONREQUIRED ErrorResponseResponseCode = "NETWORK_AUTHENTICATION_REQUIRED" - NOTACCEPTABLE ErrorResponseResponseCode = "NOT_ACCEPTABLE" - NOTIMPLEMENTED ErrorResponseResponseCode = "NOT_IMPLEMENTED" - PRECONDITIONFAILED ErrorResponseResponseCode = "PRECONDITION_FAILED" - PRECONDITIONREQUIRED ErrorResponseResponseCode = "PRECONDITION_REQUIRED" - REQUESTEDRANGENOTSATISFIABLE ErrorResponseResponseCode = "REQUESTED_RANGE_NOT_SATISFIABLE" - REQUESTENTITYTOOLARGE ErrorResponseResponseCode = "REQUEST_ENTITY_TOO_LARGE" - REQUESTHEADERFIELDSTOOLARGE ErrorResponseResponseCode = "REQUEST_HEADER_FIELDS_TOO_LARGE" - REQUESTTIMEOUT ErrorResponseResponseCode = "REQUEST_TIMEOUT" - REQUESTURITOOLONG ErrorResponseResponseCode = "REQUEST_URI_TOO_LONG" - SERVICEUNAVAILABLE ErrorResponseResponseCode = "SERVICE_UNAVAILABLE" - TOOMANYREQUESTS ErrorResponseResponseCode = "TOO_MANY_REQUESTS" - UNAUTHORIZED ErrorResponseResponseCode = "UNAUTHORIZED" - UNSUPPORTEDMEDIATYPE ErrorResponseResponseCode = "UNSUPPORTED_MEDIA_TYPE" -) - -// Defines values for OPADecisionResponseDecision. -const ( - DENY OPADecisionResponseDecision = "DENY" - INDETERMINATE OPADecisionResponseDecision = "INDETERMINATE" - NOTAPPLICABLE OPADecisionResponseDecision = "NOTAPPLICABLE" - PERMIT OPADecisionResponseDecision = "PERMIT" + BadRequest ErrorResponseResponseCode = "bad_request" + EvaluationError ErrorResponseResponseCode = "evaluation_error" + InternalError ErrorResponseResponseCode = "internal_error" + InvalidOperation ErrorResponseResponseCode = "invalid_operation" + InvalidParameter ErrorResponseResponseCode = "invalid_parameter" + OpaUndefinedError ErrorResponseResponseCode = "opa_undefined_error" + ResourceConflict ErrorResponseResponseCode = "resource_conflict" + ResourceNotFound ErrorResponseResponseCode = "resource_not_found" + Unauthorized ErrorResponseResponseCode = "unauthorized" + UndefinedDocument ErrorResponseResponseCode = "undefined_document" ) // ErrorResponse defines model for ErrorResponse. type ErrorResponse struct { - ErrorDetails *[]string `json:"errorDetails,omitempty"` ErrorMessage *string `json:"errorMessage,omitempty"` PolicyName *string `json:"policyName,omitempty"` ResponseCode *ErrorResponseResponseCode `json:"responseCode,omitempty"` @@ -71,15 +48,15 @@ type HealthCheckReport struct { // OPADecisionRequest defines model for OPADecisionRequest. type OPADecisionRequest struct { - CurrentDate *openapi_types.Date `json:"currentDate,omitempty"` - CurrentDateTime *time.Time `json:"currentDateTime,omitempty"` - CurrentTime *time.Time `json:"currentTime,omitempty"` - Input *map[string]interface{} `json:"input,omitempty"` - OnapComponent *string `json:"onapComponent,omitempty"` - OnapInstance *string `json:"onapInstance,omitempty"` - OnapName *string `json:"onapName,omitempty"` - PolicyFilter *[]string `json:"policyFilter,omitempty"` - PolicyName *string `json:"policyName,omitempty"` + CurrentDate *openapi_types.Date `json:"currentDate,omitempty"` + CurrentDateTime *time.Time `json:"currentDateTime,omitempty"` + CurrentTime *string `json:"currentTime,omitempty"` + Input map[string]interface{} `json:"input"` + OnapComponent *string `json:"onapComponent,omitempty"` + OnapInstance *string `json:"onapInstance,omitempty"` + OnapName *string `json:"onapName,omitempty"` + PolicyFilter []string `json:"policyFilter"` + PolicyName string `json:"policyName"` // TimeOffset Time offset in hours and minutes, e.g., '+02:00' or '-05:00' TimeOffset *string `json:"timeOffset,omitempty"` @@ -90,30 +67,23 @@ type OPADecisionRequest struct { // OPADecisionResponse defines model for OPADecisionResponse. type OPADecisionResponse struct { - Decision *OPADecisionResponseDecision `json:"decision,omitempty"` - Output *map[string]interface{} `json:"output,omitempty"` - PolicyName *string `json:"policyName,omitempty"` - StatusMessage *string `json:"statusMessage,omitempty"` + Output map[string]interface{} `json:"output"` + PolicyName string `json:"policyName"` + StatusMessage *string `json:"statusMessage,omitempty"` } -// OPADecisionResponseDecision defines model for OPADecisionResponse.Decision. -type OPADecisionResponseDecision string - // StatisticsReport defines model for StatisticsReport. type StatisticsReport struct { - Code *int32 `json:"code,omitempty"` - DenyDecisionsCount *int64 `json:"denyDecisionsCount,omitempty"` - DeployFailureCount *int64 `json:"deployFailureCount,omitempty"` - DeploySuccessCount *int64 `json:"deploySuccessCount,omitempty"` - IndeterminantDecisionsCount *int64 `json:"indeterminantDecisionsCount,omitempty"` - PermitDecisionsCount *int64 `json:"permitDecisionsCount,omitempty"` - QueryFailureCount *int64 `json:"queryFailureCount,omitempty"` - QuerySuccessCount *int64 `json:"querySuccessCount,omitempty"` - TotalErrorCount *int64 `json:"totalErrorCount,omitempty"` - TotalPoliciesCount *int64 `json:"totalPoliciesCount,omitempty"` - TotalPolicyTypesCount *int64 `json:"totalPolicyTypesCount,omitempty"` - UndeployFailureCount *int64 `json:"undeployFailureCount,omitempty"` - UndeploySuccessCount *int64 `json:"undeploySuccessCount,omitempty"` + Code *int32 `json:"code,omitempty"` + DecisionFailureCount *int64 `json:"decisionFailureCount,omitempty"` + DecisionSuccessCount *int64 `json:"decisionSuccessCount,omitempty"` + DeployFailureCount *int64 `json:"deployFailureCount,omitempty"` + DeploySuccessCount *int64 `json:"deploySuccessCount,omitempty"` + TotalErrorCount *int64 `json:"totalErrorCount,omitempty"` + TotalPoliciesCount *int64 `json:"totalPoliciesCount,omitempty"` + TotalPolicyTypesCount *int64 `json:"totalPolicyTypesCount,omitempty"` + UndeployFailureCount *int64 `json:"undeployFailureCount,omitempty"` + UndeploySuccessCount *int64 `json:"undeploySuccessCount,omitempty"` } // DecisionParams defines parameters for Decision. diff --git a/test/README.md b/test/README.md index 51cdcf0..96f4eed 100644 --- a/test/README.md +++ b/test/README.md @@ -2,91 +2,114 @@ ## Verification API Calls -curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"example/allow","input":{"method":"POST","path":["users"]}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22", "currentTime": "11:34:56", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z", "policyFilter" : [""], "policyName":"example","input":{"method":"POST","path":["users"]}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z", "policyName":"role/allow","input":{"user":"alice","action":"write","object":"id123","type":"dog"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision +{"output":{"allow":true},"policyName":"example"} -## PERMIT for policy:action +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22", "currentTime": "11:34:56", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z", "policyName":"role", "policyFilter": ["role_grants"], "input":{"user":"alice","action":"write","object":"id123","type":"dog"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"action/allow","input":{"user":"alice","action":"delete","type":"server"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision +{"output":{"role_grants":{"billing":[{"action":"read","type":"finance"},{"action":"update","type":"finance"}],"customer":[{"action":"read","type":"dog"},{"action":"read","type":"cat"},{"action":"adopt","type":"dog"},{"action":"adopt","type":"cat"}],"employee":[{"action":"read","type":"dog"},{"action":"read","type":"cat"},{"action":"update","type":"dog"},{"action":"update","type":"cat"}]}},"policyName":"role"} -{"decision":"PERMIT","policyName":"action/allow","statusMessage":"OPA Allowed"} +## OUTPUT for policy:action with filter role_permissions -## DENY for policy:action +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "11:34:56", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z",__"policyFilter": ["role_permissions"]__, "policyName":"action","input":{"user":"alice","action":"delete","type":"server"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"action/allow","input":{"user":"charlie","action":"delete","type":"server"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision +{"output":{"role_permissions":{"admin":{"actions":["read","write","delete"],"resources":["server","database"]},"editor":{"actions":["read","write"],"resources":["server"]},"viewer":{"actions":["read"],"resources":["server"]}}},"policyName":"action"} -{"decision":"DENY","policyName":"action/allow","statusMessage":"OPA Denied"} +## OUTPUT for policy:action with empty filters -## PERMIT for policy:account +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "11:34:56", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z",__"policyFilter": [""]__, "policyName":"action","input":{"user":"alice","action":"delete","type":"server"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC","timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"account/allow", "input":{"creditor_account":11111,"creditor":"alice","debtor_account":22222,"debtor":"bob","period":30,"amount":1000}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision +{"output":{"allow":true,"role_permissions":{"admin":{"actions":["read","write","delete"],"resources":["server","database"]},"editor":{"actions":["read","write"],"resources":["server"]},"viewer":{"actions":["read"],"resources":["server"]}},"user_roles":{"alice":["admin"],"bob":["editor"],"charlie":["viewer"]}},"policyName":"action"} -{"decision":"PERMIT","policyName":"account/allow","statusMessage":"OPA Allowed"} +## OUTPUT for policy:action without filter -## DENY for policy:account +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "11:34:56", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"action","input":{"user":"charlie","action":"delete","type":"server"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"account/allow", "input":{"creditor_account":11111,"creditor":"alice","debtor_account":22222,"debtor":"bob","period":31,"amount":1000}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision +{"errorMessage":"Policy Filter is nil.","policyName":"","responseCode":"bad_request"} -{"decision":"DENY","policyName":"account/allow","statusMessage":"OPA Denied"} +## OUTPUT for policy:account with filter account_attributes -## PERMIT for policy:organization +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "11:34:56", "timeZone": "UTC","timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z",__"policyFilter": ["account_attributes"]__, "policyName":"account", "input":{"creditor_account":11111,"creditor":"alice","debtor_account":22222,"debtor":"bob","period":30,"amount":1000}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"organization/allow", "input":{"user":"alice","action": "read","component": "component_A","project": "project_A", "organization": "org_A"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -{"decision":"PERMIT","policyName":"organization/allow","statusMessage":"OPA Allowed"} +{"output":{"account_attributes":{"11111":{"amount":10000,"owner":"alice"},"22222":{"amount":10000,"owner":"bob"},"33333":{"amount":10000,"owner":"cam"}}},"policyName":"account"} -## DENY for policy:organization +## OUTPUT for policy:account with empty filter -curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"organization/allow", "input":{"user":"charlie","action": "edit","component": "component_A","project": "project_A", "organization": "org_A"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "11:34:56", "timeZone": "UTC","timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z",__"policyFilter": [""]__, "policyName":"account", "input":{"creditor_account":11111,"creditor":"alice","debtor_account":22222,"debtor":"bob","period":30,"amount":1000}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -{"decision":"DENY","policyName":"organization/allow","statusMessage":"OPA Denied"} +{"output":{"account_attributes":{"11111":{"amount":10000,"owner":"alice"},"22222":{"amount":10000,"owner":"bob"},"33333":{"amount":10000,"owner":"cam"}},"allow":true,"amount_is_valid":true,"creditor_is_valid":true,"debtor_is_valid":true,"period_is_valid":true},"policyName":"account"} -## DENY for policy:abac(output) +## OUTPUT for policy:organization with filter acls -curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"abac", "policyFilter": ["action_is_read"], "input":{"actions": ["write"],"datatypes": ["location","temperature","precipitation","windspeed"],"time_period": {"from": "2024-03-27","to": "2024-03-31"}}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -{"decision":"DENY","output":{},"policyName":"abac","statusMessage":"OPA Denied"} +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "11:34:56", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z",__"policyFilter": ["acls"]__, "policyName":"organization", "input":{"user":"alice","action": "read","component": "component_A","project": "project_A", "organization": "org_A"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -## PERMIT for policy:abac +{"output":{"acls":[{"actions":["edit","read"],"component":"component_A","organization":"org_A","project":"project_A","user":"alice"},{"actions":["read"],"organization":"org_A","user":"bob"},{"action":["edit"],"component":"component_A","organization":"org_A","project":"project_B","user":"bob"},{"action":["read"],"organization":"org_A","project":"project_B","user":"charlie"}]},"policyName":"organization"} -curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"abac", "policyFilter": ["viewable_sensor_data"], "input":{"actions": ["read"],"datatypes": ["location","temperature","precipitation","windspeed"],"time_period": {"from": "2024-02-27","to": "2024-02-29"}}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -{"decision":"PERMIT","output":{"viewable_sensor_data":[{"location":"Galle","precipitation":"500 mm","temperature":"35 C","windspeed":"7.2 m/s"},{"location":"Jaffna","precipitation":"300 mm","temperature":"-5 C","windspeed":"3.8 m/s"},{"location":"Nuwara Eliya","precipitation":"600 mm","temperature":"25 C","windspeed":"4.0 m/s"},{"location":"Trincomalee","precipitation":"1000 mm","temperature":"20 C","windspeed":"5.0 m/s"}]},"policyName":"abac","statusMessage":"OPA Allowed"} +## OUTPUT for policy:organization with empty filter -## PERMIT for policy:zone -curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"zone", "policyFilter": ["has_zone_access"], "input":{"actions": ["view"],"log_id": "log1", "datatypes": ["access", "user"],"time_period": {"from": "2024-11-01T09:00:00Z","to": "2024-11-01T10:00:00Z"},"zone_id": "zoneA"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -{"decision":"PERMIT","output":{"has_zone_access":[{"access":"granted","user":"user1"}]},"policyName":"zone","statusMessage":"OPA Allowed"} +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "11:34:56", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z",__"policyFilter": [""]__, "policyName":"organization", "input":{"user":"alice","action": "read","component": "component_A","project": "project_A", "organization": "org_A"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -## DENY for policy: zone +{"output":{"acls":[{"actions":["edit","read"],"component":"component_A","organization":"org_A","project":"project_A","user":"alice"},{"actions":["read"],"organization":"org_A","user":"bob"},{"action":["edit"],"component":"component_A","organization":"org_A","project":"project_B","user":"bob"},{"action":["read"],"organization":"org_A","project":"project_B","user":"charlie"}],"allow":true},"policyName":"organization"} -curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"zone", "policyFilter": ["has_zone_access"], "input":{"actions": ["edit"],"log_id": "log1", "datatypes": ["access", "user"],"time_period": {"from": "2024-11-01T00:00:00Z","to": "2024-11-01T00:00:00Z"},"zone_id": "zoneA"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -{"decision":"DENY","output":{"has_zone_access":[]},"policyName":"zone","statusMessage":"OPA Denied"} +## OUTPUT for policy:abac with filter viewable_sensor_data -## PERMIT for policy:vehicle +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"abac", __"policyFilter": ["viewable_sensor_data"]__, "input":{"actions": ["write"],"datatypes": ["location","temperature","precipitation","windspeed"],"time_period": {"from": "2024-03-27","to": "2024-03-31"}}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"vehicle", "policyFilter": ["user_has_vehicle_access"], "input":{"actions": ["use"],"user":"user1", "vehicle_id": "v1", "attributes": ["type", "status"]}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision +{"output":{"viewable_sensor_data":[]},"policyName":"abac"} -{"decision":"PERMIT","output":{"user_has_vehicle_access":[{"status":"available","type":"car"}]},"policyName":"vehicle","statusMessage":"OPA Allowed"} +## OUTPUT for policy:abac with empty filter -## PERMIT for policy:docs +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"abac", __"policyFilter": [""]__, "input":{"actions": ["write"],"datatypes": ["location","temperature","precipitation","windspeed"],"time_period": {"from": "2024-03-27","to": "2024-03-31"}}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -`curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"docs", "policyFilter": ["has_access_to_file"], "input":{"action": "read","file_id": "file1","access_level": "admin","attributes": ["owner", "size"]}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision +{"output":{"allow":false,"sensor_data":[{"humidity":"40%","id":"0001","location":"Sri Lanka","particle_density":"1.3 g/l","precipitation":"1000 mm","temperature":"28 C","timestamp":"2024-02-26","windspeed":"5.5 m/s"},{"humidity":"45%","id":"0002","location":"Colombo","particle_density":"1.5 g/l","precipitation":"1200 mm","temperature":"30 C","timestamp":"2024-02-26","windspeed":"6.0 m/s"},{"humidity":"60%","id":"0003","location":"Kandy","particle_density":"1.1 g/l","precipitation":"800 mm","temperature":"25 C","timestamp":"2024-02-26","windspeed":"4.5 m/s"},{"humidity":"30%","id":"0004","location":"Galle","particle_density":"1.8 g/l","precipitation":"500 mm","temperature":"35 C","timestamp":"2024-02-27","windspeed":"7.2 m/s"},{"humidity":"20%","id":"0005","location":"Jaffna","particle_density":"0.9 g/l","precipitation":"300 mm","temperature":"-5 C","timestamp":"2024-02-27","windspeed":"3.8 m/s"},{"humidity":"55%","id":"0006","location":"Trincomalee","particle_density":"1.2 g/l","precipitation":"1000 mm","temperature":"20 C","timestamp":"2024-02-28","windspeed":"5.0 m/s"},{"humidity":"50%","id":"0007","location":"Nuwara Eliya","particle_density":"1.3 g/l","precipitation":"600 mm","temperature":"25 C","timestamp":"2024-02-28","windspeed":"4.0 m/s"},{"humidity":"40%","id":"0008","location":"Anuradhapura","particle_density":"1.4 g/l","precipitation":"700 mm","temperature":"28 C","timestamp":"2024-02-29","windspeed":"5.8 m/s"},{"humidity":"65%","id":"0009","location":"Matara","particle_density":"1.6 g/l","precipitation":"900 mm","temperature":"32 C","timestamp":"2024-02-29","windspeed":"6.5 m/s"}],"viewable_sensor_data":[]},"policyName":"abac"} -{"decision":"PERMIT","output":{"has_access_to_file":[{"owner":"user1","size":"10MB"}]},"policyName":"docs","statusMessage":"OPA Allowed"}` +## OUTPUT for policy:zone with filter has_zone_access -## DENY for policy:docs +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"zone", __"policyFilter": ["has_zone_access"]__, "input":{"actions": ["view"],"log_id": "log1", "datatypes": ["access", "user"],"time_period": {"from": "2024-11-01T09:00:00Z","to": "2024-11-01T10:00:00Z"},"zone_id": "zoneA"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision -`curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"docs", "policyFilter": ["has_access_to_file"], "input":{"action": "view","file_id": "file1","access_level": "employee","attributes": ["owner", "size"]}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision +{"output":{"has_zone_access":[{"access":"granted","user":"user1"}]},"policyName":"zone"} + +## OUTPUT for policy:zone with empty filter + +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"zone", __"policyFilter": [""]__, "input":{"actions": ["view"],"log_id": "log1", "datatypes": ["access", "user"],"time_period": {"from": "2024-11-01T09:00:00Z","to": "2024-11-01T10:00:00Z"},"zone_id": "zoneA"}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision + +{"output":{"action_is_log_view":true,"allow":true,"has_zone_access":[{"access":"granted","user":"user1"}],"zone":{"zone_access_logs":[{"access":"granted","log_id":"log1","timestamp":"2024-11-01T09:00:00Z","user":"user1","zone_id":"zoneA"},{"access":"denied","log_id":"log2","timestamp":"2024-11-01T10:30:00Z","user":"user2","zone_id":"zoneA"},{"access":"granted","log_id":"log3","timestamp":"2024-11-01T11:00:00Z","user":"user3","zone_id":"zoneB"}]}},"policyName":"zone"} + +## OUTPUT for policy:vehicle with filter user_has_vehicle_access + +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"vehicle", __"policyFilter": ["user_has_vehicle_access"]__, "input":{"actions": ["use"],"user":"user1", "vehicle_id": "v1", "attributes": ["type", "status"]}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision + +{"output":{"user_has_vehicle_access":[{"status":"available","type":"car"}]},"policyName":"vehicle"} + +## OUTPUT for policy:vehicle with empty filter + +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"vehicle", __"policyFilter": [""]__, "input":{"actions": ["use"],"user":"user1", "vehicle_id": "v1", "attributes": ["type", "status"]}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision + +{"output":{"action_is_granted":true,"allow":true,"user_has_vehicle_access":[{"status":"available","type":"car"}],"vehicles":[{"owner":"user1","status":"available","type":"car","vehicle_id":"v1"},{"owner":"user2","status":"in use","type":"bike","vehicle_id":"v2"}]},"policyName":"vehicle"} + +## OUTPUT for policy:docs with filter has_access_to_file + +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"docs", __"policyFilter": ["has_access_to_file"]__, "input":{"action": "read","file_id": "file1","access_level": "admin","attributes": ["owner", "size"]}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision + +{"output":{"has_access_to_file":[{"owner":"user1","size":"10MB"}]},"policyName":"docs"} + +## OUTPUT for policy:docs with empty filter + +curl -u 'policyadmin:zb!XztG34' -H 'Content-Type: application/json' -H 'Accept: application/json' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -d '{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS","currentDate": "2024-11-22","policyName":"docs", __"policyFilter": [""]__, "input":{"action": "read","file_id": "file1","access_level": "admin","attributes": ["owner", "size"]}}' -X POST http://0.0.0.0:8282/policy/pdpo/v1/decision + +{"output":{"action_is_read_or_write":true,"allow":true,"files":[{"access_level":"admin","file_id":"file1","owner":"user1","size":"10MB"},{"access_level":"user","file_id":"file2","owner":"user2","size":"5MB"}],"has_access_to_file":[{"owner":"user1","size":"10MB"}]},"policyName":"docs"} -{"decision":"DENY","output":{"has_access_to_file":[]},"policyName":"docs","statusMessage":"OPA Denied"}` ## HealthCheck API Call With Response curl -u 'policyadmin:zb!XztG34' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -X GET http://0.0.0.0:8282/policy/pdpo/v1/healthcheck -{"code":200,"healthy":true,"message":"alive","name":"opa-9f0248ea-807e-45f6-8e0f-935e570b75cc","url":"self"} +{"code":200,"healthy":true,"message":"alive","name":"opa-ea84b1ff-00de-4bf6-a039-222e4511d0a1","url":"self"} ## Statistics API Call With Response curl -u 'policyadmin:zb!XztG34' --header 'X-ONAP-RequestID:8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1' -X GET http://0.0.0.0:8282/policy/pdpo/v1/statistics -{"code":200,"denyDecisionsCount":10,"deployFailureCount":0,"deploySuccessCount":0,"indeterminantDecisionsCount":0,"permitDecisionsCount":18,"totalErrorCount":4,"totalPoliciesCount":0,"totalPolicyTypesCount":1,"undeployFailureCount":0,"undeploySuccessCount":0} +{"code":200,"decisionFailureCount":0,"decisionSuccessCount":9,"deployFailureCount":0,"deploySuccessCount":0,"totalErrorCount":5,"totalPoliciesCount":0,"totalPolicyTypesCount":1,"undeployFailureCount":0,"undeploySuccessCount":0} diff --git a/version b/version index 51f7e73..276823b 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.0.1-SNAPSHOT +1.0.2-SNAPSHOT diff --git a/version.properties b/version.properties index 266427f..359ebb9 100644 --- a/version.properties +++ b/version.properties @@ -4,7 +4,7 @@ major=1 minor=0 -patch=1 +patch=2 base_version=${major}.${minor}.${patch}