Skip to content
This repository has been archived by the owner on Nov 30, 2023. It is now read-only.

Commit

Permalink
Merge pull request #12 from birkland/issue-5
Browse files Browse the repository at this point in the history
* Add /policies endpoint and implementation
* Add integration tests for `/policies` endpoint
* Configure docker to use a docker-appropriate dsl by default

Resolves #5
  • Loading branch information
birkland authored Apr 23, 2019
2 parents 298ac93 + 2bc0a58 commit 8d176c4
Show file tree
Hide file tree
Showing 10 changed files with 662 additions and 3 deletions.
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@

POLICY_SERVICE_PORT=8088
POLICY_FILE=docker.json

PASS_EXTERNAL_FEDORA_BASEURL=http://localhost:8080/fcrepo/rest
PASS_FEDORA_BASEURL=http://fcrepo:8080/fcrepo/rest

Expand Down
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ RUN go generate ./... && \
CGO_ENABLED=0 go build ./cmd/pass-policy-service

FROM alpine:3.9
COPY --from=builder /root/pass-policy-service /root/scripts /
ENV POLICY_FILE=docker.json
COPY --from=builder /root/pass-policy-service /root/scripts /root/policies /

RUN chmod 700 /entrypoint.sh

Expand Down
215 changes: 215 additions & 0 deletions cmd/pass-policy-service/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,224 @@
package main

import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"testing"

"github.com/oa-pass/pass-policy-service/rule"
)

const defaultFedoraBaseuri = "http://localhost:8080/fcrepo/rest"

func TestFedoraIntegration(t *testing.T) {
client := &http.Client{}

fedora := resourceHelper{t, client}

nihRepo := fedora.repository("repositories/nih")
decRepo := fedora.repository("repositories/dec")

nihPolicy := fedora.policy("policies/nih", []string{nihRepo})
decPolcy := fedora.policy("policies/dec", []string{decRepo})

nihFunder := fedora.funder("funders/nih", nihPolicy)
decFunder := fedora.funder("funders/dec", decPolcy)

nihGrant := fedora.grant("grants/nih", nihFunder, "")
decGrant := fedora.grant("grants/dec", "", decFunder)

submission := fedora.submission("submissions/foo", []string{nihGrant, decGrant})

get, _ := http.NewRequest(http.MethodGet, policyServiceURI()+"/policies?submission="+submission, nil)
get.Header.Set("Eppn", "[email protected]")

resp, err := client.Do(get)
if err != nil {
t.Fatalf("GET request to %s failed: %s", get.RequestURI, err)
}

if resp.StatusCode > 299 {
t.Fatalf("Policy service returned an error on GET to %s: %d", get.URL, resp.StatusCode)
}

// Read in the body
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)

var policies []rule.Policy

_ = json.Unmarshal(body, &policies)
if len(policies) != 3 {
t.Fatalf("Wrong number of policies, got %d", len(policies))
}
}

func authz(r *http.Request) {
username, ok := os.LookupEnv("PASS_FEDORA_USER")
if !ok {
username = "fedoraAdmin"
}

passwd, ok := os.LookupEnv("PASS_FEDORA_PASSWORD")
if !ok {
passwd = "moo"
}

r.SetBasicAuth(username, passwd)
}

func policyServiceURI() string {
port, ok := os.LookupEnv("POLICY_SERVICE_PORT")
if !ok {
port = "8088"
}

host, ok := os.LookupEnv("POLICY_SERVICE_HOST")
if !ok {
host = "localhost"
}

return fmt.Sprintf("http://%s:%s", host, port)
}

func fedoraURI(uripath string) string {
return fmt.Sprintf("%s/%s", fedoraBaseURI(), uripath)
}

func fedoraBaseURI() string {
baseuri, ok := os.LookupEnv("PASS_EXTERNAL_FEDORA_BASEURL")
if !ok {
return defaultFedoraBaseuri
}

return strings.Trim(baseuri, "/")
}

type resourceHelper struct {
t *testing.T
c *http.Client
}

func (r *resourceHelper) submission(path string, funders []string) string {
uri := fedoraURI(path)

submission := fmt.Sprintf(`{
"@context" : "https://oa-pass.github.io/pass-data-model/src/main/resources/context-3.4.jsonld",
"@id" : "%s",
"grants": [
%s
],
"@type" : "Submission"
}
`, uri, jsonList(funders))

r.putResource(uri, submission)
return uri
}

func (r *resourceHelper) grant(path, priFunder, dirFunder string) string {
var funders string
if priFunder != "" {
funders = fmt.Sprintf(`"primaryFunder": "%s",`, priFunder) + "\n"
}
if dirFunder != "" {
funders = fmt.Sprintf(`%s"directFunder": "%s",`, funders, dirFunder) + "\n"
}

uri := fedoraURI(path)

funder := fmt.Sprintf(`{
"@context" : "https://oa-pass.github.io/pass-data-model/src/main/resources/context-3.4.jsonld",
"@id" : "%s",
%s
"@type" : "Grant"
}
`, uri, funders)

r.putResource(uri, funder)
return uri

}

func (r *resourceHelper) funder(path, policy string) string {
uri := fedoraURI(path)

funder := fmt.Sprintf(`{
"@context" : "https://oa-pass.github.io/pass-data-model/src/main/resources/context-3.4.jsonld",
"@id" : "%s",
"policy": "%s",
"@type" : "Funder"
}
`, uri, policy)

r.putResource(uri, funder)
return uri
}

func (r *resourceHelper) policy(path string, repositories []string) string {
uri := fedoraURI(path)

policy := fmt.Sprintf(`{
"@context" : "https://oa-pass.github.io/pass-data-model/src/main/resources/context-3.4.jsonld",
"@id" : "%s",
"repositories": [
%s
],
"@type" : "Policy"
}
`, uri, jsonList(repositories))

r.putResource(uri, policy)
return uri
}

func jsonList(list []string) string {
var jsonList []string
for _, item := range list {
jsonList = append(jsonList, fmt.Sprintf(`"%s"`, item))
}

return strings.Trim(strings.Join(jsonList, ",\n"), ",\n")
}

func (r *resourceHelper) repository(path string) string {
uri := fedoraURI(path)

repo := fmt.Sprintf(`{
"@context" : "https://oa-pass.github.io/pass-data-model/src/main/resources/context-3.4.jsonld",
"@id" : "%s",
"@type" : "Repository"
}
`, uri)

r.putResource(uri, repo)
return uri
}

func (r *resourceHelper) putResource(uri, body string) {
request, err := http.NewRequest(http.MethodPut, uri, strings.NewReader(body))
if err != nil {
r.t.Fatalf("Building request failed: %s", err)
}

request.Header.Set("Content-Type", "application/ld+json")
request.Header.Set("Prefer", `handling=lenient; received="minimal"`)
authz(request)

resp, err := r.c.Do(request)
if err != nil {
r.t.Fatalf("PUT request failed: %s", err)
}

defer resp.Body.Close()
io.Copy(ioutil.Discard, resp.Body)

if resp.StatusCode > 299 {
r.t.Fatalf("Could not add resource: %d, body:\n%s", resp.StatusCode, body)
}
}
46 changes: 45 additions & 1 deletion cmd/pass-policy-service/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ package main

import (
"fmt"
"io/ioutil"
"log"
"net"
"net/http"

"github.com/oa-pass/pass-policy-service/web"
"github.com/pkg/errors"
"github.com/urfave/cli"
)

Expand Down Expand Up @@ -63,5 +69,43 @@ func serve() cli.Command {
}

func serveAction(opts serveOpts, args []string) error {
return fmt.Errorf("not implemented")

if len(args) != 1 {
return fmt.Errorf("expecting exactly one argument: the rules doc file")
}

var credentials *web.Credentials
if opts.username != "" {
credentials = &web.Credentials{
Username: opts.username,
Password: opts.passwd,
}
}

rules, err := ioutil.ReadFile(args[0])
if err != nil {
return fmt.Errorf("error reading %s: %s", args[0], err.Error())
}

policyService, err := web.NewPolicyService(rules, &web.InternalPassClient{
Requester: &http.Client{},
ExternalBaseURI: opts.publicBaseURI,
InternalBaseURI: opts.privateBaseURI,
Credentials: credentials,
})
if err != nil {
return errors.Wrapf(err, "could not initialize policy service")
}

http.HandleFunc("/policies", policyService.RequestPolicies)

listener, err := net.Listen("tcp", fmt.Sprintf(":%d", opts.port))
if err != nil {
return err
}

opts.port = listener.Addr().(*net.TCPAddr).Port
log.Printf("Listening on port %d", opts.port)

return http.Serve(listener, nil)
}
43 changes: 43 additions & 0 deletions policies/docker.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"$schema": "https://oa-pass.github.io/pass-policy-service/schemas/policy_config_1.0.json",
"policy-rules": [
{
"description": "Must deposit to one of the repositories indicated by primary funder",
"policy-id": "${submission.grants.primaryFunder.policy}",
"repositories": [
{
"repository-id": "${policy.repositories}"
}
]
},
{
"description": "Must deposit to one of the repositories indicated by direct funder",
"policy-id": "${submission.grants.directFunder.policy}",
"repositories": [
{
"repository-id": "${policy.repositories}"
}
]
},
{
"description": "Members of the JHU community must deposit into JScholarship, or some other repository.",
"policy-id": "policies/",
"conditions": [
{
"endsWith": {
"@johnshopkins.edu": "${header.Eppn}"
}
}
],
"repositories": [
{
"repository-id": "https://pass.local/fcrepo/rest/repositories/j10p",
"selected": true
},
{
"repository-id": "*"
}
]
}
]
}
2 changes: 1 addition & 1 deletion scripts/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ printf "\n**** Begin Environment Variable Dump ****\n\n"
printenv | sort
printf "\n**** End Environment Variable Dump ****\n\n"

./pass-policy-service serve
./pass-policy-service serve ${POLICY_FILE}
Loading

0 comments on commit 8d176c4

Please sign in to comment.