Skip to content

Commit

Permalink
move data from headers to body
Browse files Browse the repository at this point in the history
  • Loading branch information
gerardsn committed Jul 16, 2024
1 parent bbfc356 commit 26885a9
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 139 deletions.
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# golang alpine
FROM golang:1.22.4-alpine as builder
FROM golang:1.22.5-alpine AS builder

ARG TARGETARCH
ARG TARGETOS
Expand All @@ -12,8 +12,8 @@ RUN apk update \
musl-dev \
&& update-ca-certificates

ENV GO111MODULE on
ENV GOPATH /
ENV GO111MODULE=on
ENV GOPATH=/

RUN mkdir /opt/nuts-pxp && cd /opt/nuts-pxp
COPY go.mod .
Expand Down
81 changes: 35 additions & 46 deletions api/opa/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
"encoding/json"
"errors"
"fmt"
"strings"

"github.com/nuts-foundation/nuts-pxp/policy"
)

Expand All @@ -18,6 +16,9 @@ type Wrapper struct {
}

func (w Wrapper) EvaluateDocumentApisix(ctx context.Context, request EvaluateDocumentApisixRequestObject) (EvaluateDocumentApisixResponseObject, error) {
if request.Body == nil {
return nil, errors.New("missing body")
}
// APISIX combines the 'openid-connect' and 'opa' plugin results into the following body:
//{
// "input": {
Expand Down Expand Up @@ -50,15 +51,41 @@ func (w Wrapper) EvaluateDocumentApisix(ctx context.Context, request EvaluateDoc
// }
// }
//}
outcome, err := w.handleEvaluate(ctx, *request.Body)
if err != nil {
return nil, err
}

input, ok := (*request.Body)["input"].(map[string]interface{})
if !ok {
return nil, errors.New("invalid request, missing 'input'")
// Expected response by APISIX is of the form:
//{
// "result": {
// "allow": true
// }
//}
return EvaluateDocumentApisix200JSONResponse(*outcome), nil
}

func (w Wrapper) EvaluateDocument(ctx context.Context, request EvaluateDocumentRequestObject) (EvaluateDocumentResponseObject, error) {
if request.Body == nil {
return nil, errors.New("missing body")
}
httpRequest, ok := input["request"].(map[string]interface{})
//fmt.Printf("%v\n", *request.Body)

outcome, err := w.handleEvaluate(ctx, *request.Body)
if err != nil {
return nil, err
}

return EvaluateDocument200JSONResponse(*outcome), nil
}

func (w Wrapper) handleEvaluate(ctx context.Context, input Input) (*Outcome, error) {

httpRequest, ok := input.Input["request"].(map[string]interface{})
if !ok {
return nil, errors.New("invalid request, missing 'input.request'")
}

httpHeaders, ok := httpRequest["headers"].(map[string]interface{})
if !ok {
return nil, errors.New("invalid request, missing 'input.request.headers'")
Expand All @@ -77,47 +104,9 @@ func (w Wrapper) EvaluateDocumentApisix(ctx context.Context, request EvaluateDoc
return nil, fmt.Errorf("invalid request, failed to unmarshal X-Userinfo: %w", err)
}

descision, err := w.DecisionMaker.Query(ctx, httpRequest, xUserinfo)
if err != nil {
return nil, err
}

// Expected response by APISIX is of the form:
//{
// "result": {
// "allow": true
// }
//}
result := map[string]interface{}{"allow": descision}
return EvaluateDocumentApisix200JSONResponse{Result: result}, nil
}

func (w Wrapper) EvaluateDocument(ctx context.Context, request EvaluateDocumentRequestObject) (EvaluateDocumentResponseObject, error) {
// parse the requestLine and extract the method and path
// the requestLine is formatted as an HTTP request line
// e.g. "GET /api/v1/resource HTTP/1.1"
// we are only interested in the method and path
method, path, err := parseRequestLine(request.Params.Request)
if err != nil {
return nil, err
}
httpRequest := map[string]interface{}{}
httpRequest["method"] = method
httpRequest["path"] = path

descision, err := w.DecisionMaker.Query(ctx, httpRequest, request.Params.XUserinfo)
decision, err := w.DecisionMaker.Query(ctx, httpRequest, xUserinfo)
if err != nil {
return nil, err
}
return EvaluateDocument200JSONResponse{Allow: descision}, nil
}

// parseRequestLine parses the request line and extracts the method and path
// e.g. "GET /api/v1/resource HTTP/1.1" -> "GET", "/api/v1/resource"
func parseRequestLine(requestLine string) (method, path string, err error) {
parts := strings.Split(requestLine, " ")
if len(parts) != 3 {
return "", "", fmt.Errorf("invalid request line: %s", requestLine)
}
return parts[0], parts[1], nil
return &Outcome{Result: map[string]interface{}{"allow": decision}}, nil
}
82 changes: 19 additions & 63 deletions api/opa/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 33 additions & 27 deletions oas/opa.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,11 @@ paths:
The given request and X-Userinfo headers are used to create the input document for the OPA policy.
tags:
- opa
parameters:
- name: request
in: header
required: true
description: request line from nginx
schema:
type: string
example: GET /fhir/Patient/123?subject.identifier=1234567890 HTTP/1.1
- name: X-Userinfo
in: header
required: true
description: token introspection result
content:
application/json:
schema:
type: object
example: {"client_id":"did:web:example.com:iam:client","sub":"did:web:example.com:iam:verifier", "scope":"eOverdracht-sender"}
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Input'
responses:
'200':
description: Successful request. Returns the result of the OPA policy evaluation
Expand All @@ -50,29 +38,47 @@ paths:
content:
application/json:
schema:
type: object
$ref: '#/components/schemas/Input'
responses:
'200':
description: Successful request. Returns the result of the OPA policy evaluation
content:
application/json:
schema:
$ref: '#/components/schemas/ApisixOutcome'
$ref: '#/components/schemas/Outcome'
components:
schemas:
Outcome:
Input:
type: object
required:
- allow
- input
properties:
allow:
type: boolean
description: The result of the OPA policy evaluation
example: true
ApisixOutcome:
input:
type: object
description: Policy decision information. Must contain the fields in the example.
example: |
{
"input": {
"request": {
"method": "GET",
"path": "/resource",
"headers": {
"X-Userinfo": "base64 encoded user info"
}
}
}
}
Outcome:
type: object
required:
- result
properties:
result:
type: object
type: object
description: The result of the OPA policy evaluation
example: |
{
"result": {
"allow": true
}
}

0 comments on commit 26885a9

Please sign in to comment.