github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/remote/access/access_test.go (about)

     1  package access
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/qri-io/qri/dsref"
     9  	"github.com/qri-io/qri/profile"
    10  )
    11  
    12  func ExamplePolicy() {
    13  
    14  	const examplePolicy = `
    15  [
    16  	{
    17  		"title": "pull any dataset",
    18  		"effect": "allow",
    19  		"subject": "*",
    20  		"resources": [
    21  			"dataset:*"
    22  		],
    23  		"actions": [
    24  			"remote:pull"
    25  		]
    26  	},
    27  	{
    28  		"title": "push and delete user-owned datasets",
    29  		"effect": "allow",
    30  		"subject": "*",
    31  		"resources": [
    32  			"dataset:_subject:*"
    33  		],
    34  		"actions": [
    35  			"remote:push",
    36  			"remote:remove"
    37  		]
    38  	}
    39  ]
    40  `
    41  
    42  	p := &Policy{}
    43  	if err := json.Unmarshal([]byte(examplePolicy), p); err != nil {
    44  		panic(err)
    45  	}
    46  
    47  	bob := &profile.Profile{
    48  		ID:       profile.IDB58DecodeOrEmpty("QmZePf5LeXow3RW5U1AgEiNbW46YnRGhZ7HPvm1UmPFPwt"),
    49  		Peername: "bob",
    50  	}
    51  
    52  	if err := p.Enforce(bob, "dataset:someone_else:world_bank_population", "remote:pull"); err == nil {
    53  		fmt.Println("bob can pull someone_else/world_bank_population")
    54  	}
    55  	if err := p.Enforce(bob, "dataset:bob:bobs_dataset", "remote:remove"); err == nil {
    56  		fmt.Println("bob can remote-delete his own dataset")
    57  	}
    58  	if err := p.Enforce(bob, "dataset:someone_else:dataset", "remote:remove"); err == ErrAccessDenied {
    59  		fmt.Println("bob can't remote-delete someone else's dataset")
    60  	}
    61  
    62  	// Output:
    63  	// bob can pull someone_else/world_bank_population
    64  	// bob can remote-delete his own dataset
    65  	// bob can't remote-delete someone else's dataset
    66  }
    67  
    68  func TestEnforce(t *testing.T) {
    69  	pro := &profile.Profile{
    70  		ID:       profile.IDB58DecodeOrEmpty("QmZePf5LeXow3RW5U1AgEiNbW46YnRGhZ7HPvm1UmPFPwt"),
    71  		Peername: "bob",
    72  	}
    73  
    74  	p := Policy{
    75  		{
    76  			Subject:   "QmZePf5LeXow3RW5U1AgEiNbW46YnRGhZ7HPvm1UmPFPwt",
    77  			Resources: Resources{MustParseResource("dataset:foo:bar")},
    78  			Actions:   Actions{MustParseAction("*")},
    79  			Effect:    EffectAllow,
    80  		},
    81  	}
    82  
    83  	if err := p.Enforce(pro, "dataset:foo:bar", "read"); err != nil {
    84  		t.Error(err)
    85  	}
    86  }
    87  
    88  func TestPolicyJSON(t *testing.T) {
    89  	bad := [][2]string{
    90  		{"rule.Subject is required", `[{}]`},
    91  		{"rule.Resources field is required", `[{
    92  			"subject": "*", 
    93  			"resources": [], 
    94  			"actions": ["*"],
    95  			"effect": "deny"
    96  		}]`},
    97  		{`rule.Effect must be one of ("allow"|"deny")`, `[{
    98  			"subject": "*", 
    99  			"resources": ["*"], 
   100  			"actions": ["*"],
   101  			"effect": "evaporate"
   102  		}]`},
   103  		{`rule.Actions field is required`, `[{
   104  			"subject": "*", 
   105  			"resources": ["*"], 
   106  			"action": ["*"],
   107  			"effect": "allow"
   108  		}]`},
   109  	}
   110  
   111  	for _, c := range bad {
   112  		t.Run(c[0], func(t *testing.T) {
   113  			pol := &Policy{}
   114  
   115  			err := json.Unmarshal([]byte(c[1]), pol)
   116  			if err == nil {
   117  				t.Fatal("expected bad policy to fail. received no error")
   118  			}
   119  
   120  			if err.Error() != c[0] {
   121  				t.Errorf("error message mismatch. want: %q\ngot:  %q", c[1], err.Error())
   122  			}
   123  		})
   124  	}
   125  }
   126  
   127  func TestParseResource(t *testing.T) {
   128  	bad := []string{
   129  		"",
   130  		"*:foo",
   131  	}
   132  
   133  	for _, str := range bad {
   134  		if _, err := ParseResource(str); err == nil {
   135  			t.Errorf("expected error parsing bad resource. got nil.")
   136  		}
   137  	}
   138  }
   139  
   140  func TestResourceContains(t *testing.T) {
   141  	cases := []struct {
   142  		a, b   string
   143  		expect bool
   144  	}{
   145  		{"*", "apples", true},
   146  		{"candy:*", "apples", false},
   147  		{"candy:apples", "candy:apples", true},
   148  		{"candy:apples", "candy:applez", false},
   149  		{"dataset:foo:bar", "dataset", false},
   150  		{"dataset:foo:bar", "dataset:foo:bar:baz", false},
   151  		{"dataset:foo:*", "dataset:foo:bar:baz", true},
   152  		{"dataset:foo:bar:*", "dataset:foo:bar:baz", true},
   153  
   154  		{"dataset:*", "dataset:someone_else:world_bank_population", true},
   155  
   156  		{"dataset:_subject:bar:*", "dataset:user:bar:baz", true},
   157  		{"dataset:_subject:bar:*", "dataset:other_user:bar:baz", false},
   158  	}
   159  
   160  	for _, c := range cases {
   161  		t.Run(fmt.Sprintf("%s_contains_%s_is_%t", c.a, c.b, c.expect), func(t *testing.T) {
   162  			rscA, err := ParseResource(c.a)
   163  			if err != nil {
   164  				t.Fatal(err)
   165  			}
   166  
   167  			rscB, err := ParseResource(c.b)
   168  			if err != nil {
   169  				t.Fatal(err)
   170  			}
   171  
   172  			got := rscA.Contains(rscB, "user")
   173  			if c.expect != got {
   174  				t.Errorf("result mismatch expected %q.Contains(%q) == %t", c.a, c.b, c.expect)
   175  			}
   176  
   177  		})
   178  	}
   179  }
   180  
   181  func TestActionContains(t *testing.T) {
   182  	cases := []struct {
   183  		a, b   string
   184  		expect bool
   185  	}{
   186  		{"*", "apples", true},
   187  		{"candy:*", "apples", false},
   188  		{"candy:apples", "candy:apples", true},
   189  		{"candy:apples", "candy:applez", false},
   190  		{"dataset:foo:bar", "dataset", false},
   191  		{"dataset:foo:bar", "dataset:foo:bar:baz", false},
   192  		{"dataset:foo:*", "dataset:foo:bar:baz", true},
   193  		{"dataset:foo:bar:*", "dataset:foo:bar:baz", true},
   194  
   195  		{"remote:pull", "remote:pull", true},
   196  	}
   197  
   198  	for _, c := range cases {
   199  		t.Run(fmt.Sprintf("%s_contains_%s_is_%t", c.a, c.b, c.expect), func(t *testing.T) {
   200  			actA := MustParseAction(c.a)
   201  			actB := MustParseAction(c.b)
   202  
   203  			got := actA.Contains(actB)
   204  			if c.expect != got {
   205  				t.Errorf("result mismatch expected %q.Contains(%q) == %t", c.a, c.b, c.expect)
   206  			}
   207  		})
   208  	}
   209  }
   210  
   211  func TestResourceStrFromRef(t *testing.T) {
   212  	cases := []struct {
   213  		ref    dsref.Ref
   214  		expect string
   215  	}{
   216  		{dsref.Ref{Username: "foo", Name: "bar"}, "dataset:foo:bar"},
   217  	}
   218  
   219  	for _, c := range cases {
   220  		t.Run(fmt.Sprintf("ref_%s_becomes_resource_%s", c.ref, c.expect), func(t *testing.T) {
   221  
   222  			got := ResourceStrFromRef(c.ref)
   223  			if c.expect != got {
   224  				t.Errorf("result mismatch expected ResourceStrFromRef(%s) == %s", c.ref, c.expect)
   225  			}
   226  		})
   227  	}
   228  }