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 #17 from birkland/issue-16
Browse files Browse the repository at this point in the history
Add Harvard policy
  • Loading branch information
emetsger authored Aug 22, 2019
2 parents 83be19b + ca1fd85 commit 0405104
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 19 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ services:
install: true

script:
- go build ./...
- go install ./...
- go test ./...
- pass-policy-service validate policies/*.json
- docker-compose build policyservice
- docker-compose pull fcrepo && docker-compose up -d && bash ./scripts/wait_for_docker.sh
- go test -v -tags=integration ./... || docker logs policyservice
Expand Down
44 changes: 27 additions & 17 deletions cmd/pass-policy-service/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,55 @@ import (
"fmt"
"io/ioutil"
"log"
"strings"

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

func validate() cli.Command {

return cli.Command{
Name: "validate",
Usage: "Validate a given policy rules file",
Usage: "Validate policy rules files",
Description: `
Given a policy rules file, validate will attempt to parse the document
and validate it with respect to the schema used by this polivy service.
Given a list of policy rules files, validate will attempt to parse
the documents and validate it with respect to the schema used by this
policy service.
Note, the document will be validated against schemas supported by this
Note, the documents will be validated against schemas supported by this
application regardless of any schema declarations in the file.
`,
ArgsUsage: "file",
ArgsUsage: "files",
Action: func(c *cli.Context) error {
return validateAction(c.Args())
},
}
}

func validateAction(args []string) error {
if len(args) != 1 {
return fmt.Errorf("validate expects exactly one argument")
if len(args) < 1 {
return fmt.Errorf("validate requires at least one schema")
}

content, err := ioutil.ReadFile(args[0])
if err != nil {
return errors.Wrapf(err, "error opening file")
var lastErr error

for _, instance := range args {
content, err := ioutil.ReadFile(instance)
if err != nil {
lastErr = err
continue
}

_, err = rule.Validate(content)
if err == nil {
log.Printf("Validation OK: %s", instance)
} else {
errtxt := strings.ReplaceAll(fmt.Sprintf("%v", err), "\n", "\n ")
log.Printf("Validation failed: %s:\n %v", instance, errtxt)
lastErr = err
}
}

_, err = rule.Validate(content)
if err == nil {
log.Println("Validation OK")
}

return err
return lastErr
}
77 changes: 77 additions & 0 deletions policies/harvard.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"$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}",
"type": "funder",
"repositories": [
{
"repository-id": "${policy.repositories}"
}
]
},
{
"description": "Must deposit to one of the repositories indicated by direct funder",
"policy-id": "${submission.grants.directFunder.policy}",
"type": "funder",
"repositories": [
{
"repository-id": "${policy.repositories}"
}
]
},
{
"description": "Faculty members must deposit into DASH",
"policy-id": "/policies/f9/b6/01/25/f9b60125-662d-4e03-a7b0-eec0df2b50de",
"type": "institution",
"conditions": [
{
"endsWith": {
"@harvard.edu": "${header.Ajp_eppn}"
}
},
{
"contains": {
"FACULTY": "${header.Ajp_affiliation}"
}
}
],
"repositories": [
{
"repository-id": "/repositories/93/c5/ff/37/93c5ff37-ca2b-4652-a2af-3b6794ff8790"
}
]
},
{
"description": "Non-faculty members may optionally deposit into DASH",
"policy-id": "/policies/f9/b6/01/25/f9b60125-662d-4e03-a7b0-eec0df2b50de",
"type": "institution",
"conditions": [
{
"endsWith": {
"@harvard.edu": "${header.Ajp_eppn}"
}
},
{
"noneOf": [
{
"contains": {
"FACULTY": "${header.Ajp_Affiliation}"
}
}
]
}
],
"repositories": [
{
"repository-id": "/repositories/93/c5/ff/37/93c5ff37-ca2b-4652-a2af-3b6794ff8790",
"selected": true
},
{
"repository-id": "*"
}
]
}
]
}
7 changes: 6 additions & 1 deletion rule/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ Policy inclusion rules are JSON objects containing the following fields:
* `policy-id`: a string containing a a single policy URI, or a variable substitution resulting in one or more policy URIs
* In the case of a variable substitution resulting in many URIs, it is equivalent to creating multiple policy rules, each one containing a single policy-id from that list.
repositories: contains a list of repository description JSON objects, specifying which repositories satisfy the given policy.
* `condition`: Optional. JSON object describing a condition where the policy is included only if the condition evaluates to true. If this field is not present, it is presumed that inclusion of the policy is unconditional
* `condition`: Optional. JSON object describing a condition where the policy is included only if the condition evaluates to true. If this field is not present, it is presumed that inclusion of the policy is unconditional. See the schema for more details, but conditions include:
* `equals`: true if two strings are equal
* `endsWith`: true if a string ends with another
* `contains`: true if a string contains another as a substring
* `anyOf`: true if any of the given list of conditions are true
* `noneOf`: true if none of the given list of conditions are true

Repositories are JSON objects with the following fields:

Expand Down
12 changes: 12 additions & 0 deletions rule/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ func init() {
"endsWith": endsWith,
"equals": equals,
"anyOf": anyOf,
"noneOf": noneOf,
"contains": contains,
}
}

Expand Down Expand Up @@ -54,6 +56,11 @@ func endsWith(fromCondition interface{}, variables VariableResolver) (bool, erro
return eachPair(fromCondition, variables, strings.HasSuffix)
}

func contains(fromCondition interface{}, variables VariableResolver) (bool, error) {

return eachPair(fromCondition, variables, strings.Contains)
}

func equals(fromCondition interface{}, variables VariableResolver) (bool, error) {

return eachPair(fromCondition, variables, func(a, b string) bool {
Expand Down Expand Up @@ -86,6 +93,11 @@ func anyOf(arg interface{}, variables VariableResolver) (bool, error) {
return false, nil
}

func noneOf(arg interface{}, variables VariableResolver) (bool, error) {
passes, err := anyOf(arg, variables)
return !passes, err
}

func eachPair(src interface{}, variables VariableResolver, test func(string, string) bool) (passes bool, err error) {
operands, ok := src.(map[string]interface{})
if !ok {
Expand Down
22 changes: 22 additions & 0 deletions rule/condition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,34 @@ func TestConditionApply(t *testing.T) {
{"endsWith":{"one": "goner"}}
]
}`,
}, {
expected: true,
json: `{
"noneOf": [
{"equals":{"one": "two"}},
{"endsWith":{"one": "goner"}}
]
}`,
}, {
expected: false,
json: `{
"noneOf": [
{"equals":{"one": "two"}},
{"endsWith":{"one": "gone"}}
]
}`,
}, {
expected: false,
json: `{"equals":{"one": "two"}}`,
}, {
expected: true,
json: `{"equals":{"two": "two"}}`,
}, {
expected: true,
json: `{"contains": {"FACULTY": "STAFF,FACULTY,COW"}}`,
}, {
expected: false,
json: `{"contains": {"BOVINE": "STAFF,FACULTY,COW"}}`,
}, {
expected: true,
json: `{
Expand Down
9 changes: 9 additions & 0 deletions rule/testdata/good.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@
"endsWith": {
"@johnshopkins.edu": "${header.Eppn}"
}
},
{
"noneOf": [
{
"contains": {
"foo": "${header.Foo}"
}
}
]
}
],
"repositories": [
Expand Down
37 changes: 37 additions & 0 deletions schemas/policy_config_1.0.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@
},
{
"$ref": "#/definitions/anyOf"
},
{
"$ref": "#/definitions/noneOf"
}
]
}
Expand Down Expand Up @@ -127,6 +130,25 @@
}
}
}
},
{
"type": "object",
"required": [
"contains"
],
"additionalProperties": false,
"properties": {
"contains": {
"type": "object",
"title": "Contains",
"description": "Evaluates to 'true' when the given value contains the key",
"patternProperties": {
"^.+$": {
"type": "string"
}
}
}
}
}
]
},
Expand All @@ -144,6 +166,21 @@
}
}
}
},
"noneOf": {
"type": "object",
"title": "None Of",
"description": "Evaluates to true when none the conditions listed within evaluate to true",
"required": ["noneOf"],
"additionalProperties": false,
"properties": {
"noneOf": {
"type": "array",
"items": {
"$ref": "#/definitions/condition"
}
}
}
}
}
}

0 comments on commit 0405104

Please sign in to comment.