Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(52)

Unified Diff: scheduler/appengine/engine/engine_test.go

Issue 2986033003: [scheduler]: ACLs phase 1 - per Job ACL specification and enforcement. (Closed)
Patch Set: [WIP] ACLs into engine public API. Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: scheduler/appengine/engine/engine_test.go
diff --git a/scheduler/appengine/engine/engine_test.go b/scheduler/appengine/engine/engine_test.go
index a02df9c4bce57d17603b09eb55b06731401e8048..309143d1dccff3d17e771b41101233f2cddc0ea1 100644
--- a/scheduler/appengine/engine/engine_test.go
+++ b/scheduler/appengine/engine/engine_test.go
@@ -36,8 +36,11 @@ import (
"github.com/luci/luci-go/common/data/stringset"
"github.com/luci/luci-go/common/errors"
"github.com/luci/luci-go/common/retry/transient"
+ "github.com/luci/luci-go/server/auth"
+ "github.com/luci/luci-go/server/auth/authtest"
"github.com/luci/luci-go/server/secrets/testsecrets"
+ "github.com/luci/luci-go/scheduler/appengine/acl"
"github.com/luci/luci-go/scheduler/appengine/catalog"
"github.com/luci/luci-go/scheduler/appengine/messages"
"github.com/luci/luci-go/scheduler/appengine/task"
@@ -543,16 +546,38 @@ func TestQueries(t *testing.T) {
Convey("with mock data", t, func() {
c := newTestContext(epoch)
e, _ := newTestEngine()
+ // TODO(tandrii): remove aclDefault once all Jobs have ACLs.
+ aclDefault := acl.GrantsByRole{}
+ aclSome := acl.GrantsByRole{Readers: []string{"group:some"}}
+ aclOne := acl.GrantsByRole{Owners: []string{"one@example.com"}}
+
+ ctxAnon := auth.WithState(c, &authtest.FakeState{
+ Identity: "anonymous:anonymous",
+ })
+ ctxOne := auth.WithState(c, &authtest.FakeState{
+ Identity: "user:one@example.com",
+ IdentityGroups: []string{"all"},
+ })
+ ctxSome := auth.WithState(c, &authtest.FakeState{
+ Identity: "user:some@example.com",
+ IdentityGroups: []string{"some"},
+ })
+ ctxAdmin := auth.WithState(c, &authtest.FakeState{
+ Identity: "user:admin@example.com",
+ IdentityGroups: []string{"administrators"},
+ })
So(ds.Put(c,
- &Job{JobID: "abc/1", ProjectID: "abc", Enabled: true},
- &Job{JobID: "abc/2", ProjectID: "abc", Enabled: true},
- &Job{JobID: "def/1", ProjectID: "def", Enabled: true},
- &Job{JobID: "def/2", ProjectID: "def", Enabled: false},
+ &Job{JobID: "abc/1", ProjectID: "abc", Enabled: true, Acls: aclOne},
+ &Job{JobID: "abc/2", ProjectID: "abc", Enabled: true, Acls: aclSome},
+ &Job{JobID: "abc/3", ProjectID: "abc", Enabled: true, Acls: aclDefault},
+ &Job{JobID: "def/1", ProjectID: "def", Enabled: true, Acls: aclDefault},
+ &Job{JobID: "def/2", ProjectID: "def", Enabled: false, Acls: aclDefault},
), ShouldBeNil)
job1 := ds.NewKey(c, "Job", "abc/1", 0, nil)
job2 := ds.NewKey(c, "Job", "abc/2", 0, nil)
+ job3 := ds.NewKey(c, "Job", "abc/3", 0, nil)
So(ds.Put(c,
&Invocation{ID: 1, JobKey: job1, InvocationNonce: 123},
&Invocation{ID: 2, JobKey: job1, InvocationNonce: 123},
@@ -560,48 +585,82 @@ func TestQueries(t *testing.T) {
&Invocation{ID: 1, JobKey: job2},
&Invocation{ID: 2, JobKey: job2},
&Invocation{ID: 3, JobKey: job2},
+ &Invocation{ID: 1, JobKey: job3},
), ShouldBeNil)
ds.GetTestable(c).CatchupIndexes()
- Convey("GetAllJobs works", func() {
- jobs, err := e.GetAllJobs(c)
- So(err, ShouldBeNil)
- ids := stringset.New(0)
- for _, j := range jobs {
- ids.Add(j.JobID)
+ Convey("GetAllJobsRA works", func() {
+ getAllJobsRA := func(ctx context.Context) []string {
+ jobs, err := e.GetAllJobsRA(ctx)
+ So(err, ShouldBeNil)
+ return sortedJobIds(jobs)
}
- asSlice := ids.ToSlice()
- sort.Strings(asSlice)
- So(asSlice, ShouldResemble, []string{"abc/1", "abc/2", "def/1"}) // only enabled
+
+ Convey("Anonymous users see only public jobs", func() {
+ // Only 3 jobs with default ACLs granting READER access to everyone, but
+ // def/2 is disabled and so shouldn't be returned.
+ So(getAllJobsRA(ctxAnon), ShouldResemble, []string{"abc/3", "def/1"})
+ })
+ Convey("Owners can see their own jobs + public jobs", func() {
+ // abc/1 is owned by one@example.com.
+ So(getAllJobsRA(ctxOne), ShouldResemble, []string{"abc/1", "abc/3", "def/1"})
+ })
+ Convey("Explicit readers", func() {
+ So(getAllJobsRA(ctxSome), ShouldResemble, []string{"abc/2", "abc/3", "def/1"})
+ })
+ Convey("Admins have implicit READER access to all jobs", func() {
+ So(getAllJobsRA(ctxAdmin), ShouldResemble, []string{"abc/1", "abc/2", "abc/3", "def/1"})
+ })
})
- Convey("GetProjectJobs works", func() {
- jobs, err := e.GetProjectJobs(c, "def")
- So(err, ShouldBeNil)
- So(len(jobs), ShouldEqual, 1)
- So(jobs[0].JobID, ShouldEqual, "def/1")
+ Convey("GetProjectJobsRA works", func() {
+ getProjectJobsRA := func(ctx context.Context, project string) []string {
+ jobs, err := e.GetProjectJobsRA(ctx, project)
+ So(err, ShouldBeNil)
+ return sortedJobIds(jobs)
+ }
+ Convey("Anonymous can still see public jobs", func() {
+ So(getProjectJobsRA(ctxAnon, "def"), ShouldResemble, []string{"def/1"})
+ })
+ Convey("Admin have implicit READER access to all jobs", func() {
+ So(getProjectJobsRA(ctxAdmin, "abc"), ShouldResemble, []string{"abc/1", "abc/2", "abc/3"})
+ })
+ Convey("Owners can still see their jobs", func() {
+ So(getProjectJobsRA(ctxOne, "abc"), ShouldResemble, []string{"abc/1", "abc/3"})
+ })
+ Convey("Readers can see their jobs", func() {
+ So(getProjectJobsRA(ctxSome, "abc"), ShouldResemble, []string{"abc/2", "abc/3"})
+ })
})
- Convey("GetJob works", func() {
- job, err := e.GetJob(c, "missing/job")
+ Convey("GetJobRA works", func() {
+ job, err := e.GetJobRA(ctxAdmin, "missing/job")
+ So(job, ShouldBeNil)
+ So(err, ShouldBeNil)
+
+ job, err = e.GetJobRA(ctxAnon, "abc/1") // no READER permission.
So(job, ShouldBeNil)
So(err, ShouldBeNil)
- job, err = e.GetJob(c, "abc/1")
+ job, err = e.GetJobRA(ctxAnon, "def/1") // OK.
+ So(job, ShouldNotBeNil)
+ So(err, ShouldBeNil)
+
+ job, err = e.GetJobRA(ctxAnon, "def/2") // OK, even though not enabled.
So(job, ShouldNotBeNil)
So(err, ShouldBeNil)
})
- Convey("ListInvocations works", func() {
- invs, cursor, err := e.ListInvocations(c, "abc/1", 2, "")
+ Convey("ListInvocations works w/o ACL enforcement", func() {
+ invs, cursor, err := e.ListInvocations(ctxAnon, "abc/1", 2, "")
So(err, ShouldBeNil)
So(len(invs), ShouldEqual, 2)
So(invs[0].ID, ShouldEqual, 1)
So(invs[1].ID, ShouldEqual, 2)
So(cursor, ShouldNotEqual, "")
- invs, cursor, err = e.ListInvocations(c, "abc/1", 2, cursor)
+ invs, cursor, err = e.ListInvocations(ctxAdmin, "abc/1", 2, cursor)
So(err, ShouldBeNil)
So(len(invs), ShouldEqual, 1)
So(invs[0].ID, ShouldEqual, 3)
@@ -785,7 +844,7 @@ func TestAborts(t *testing.T) {
inv, err := e.GetInvocation(c, jobID, invID)
So(err, ShouldBeNil)
So(inv.Status, ShouldEqual, task.StatusRunning)
- job, err := e.GetJob(c, jobID)
+ job, err := e.GetJobRA(c, jobID)
So(err, ShouldBeNil)
So(job.State.State, ShouldEqual, JobStateRunning)
So(job.State.InvocationID, ShouldEqual, invID)
@@ -806,7 +865,7 @@ func TestAborts(t *testing.T) {
So(inv.Status, ShouldEqual, task.StatusAborted)
// The job moved on with its life.
- job, err := e.GetJob(c, jobID)
+ job, err := e.GetJobRA(c, jobID)
So(err, ShouldBeNil)
So(job.State.State, ShouldEqual, JobStateSuspended)
So(job.State.InvocationID, ShouldEqual, 0)
@@ -825,7 +884,7 @@ func TestAborts(t *testing.T) {
So(inv.Status, ShouldEqual, task.StatusAborted)
// The job moved on with its life.
- job, err := e.GetJob(c, jobID)
+ job, err := e.GetJobRA(c, jobID)
So(err, ShouldBeNil)
So(job.State.State, ShouldEqual, JobStateSuspended)
So(job.State.InvocationID, ShouldEqual, 0)
@@ -835,7 +894,7 @@ func TestAborts(t *testing.T) {
So(e.AbortJob(c, jobID, ""), ShouldBeNil)
// The job moved on with its life.
- job, err := e.GetJob(c, jobID)
+ job, err := e.GetJobRA(c, jobID)
So(err, ShouldBeNil)
So(job.State.State, ShouldEqual, JobStateSuspended)
So(job.State.InvocationID, ShouldEqual, 0)
@@ -863,7 +922,7 @@ func TestAddTimer(t *testing.T) {
So(e.startInvocation(c, jobID, invNonce, "", 0), ShouldBeNil)
// The job is running.
- job, err := e.GetJob(c, jobID)
+ job, err := e.GetJobRA(c, jobID)
So(err, ShouldBeNil)
So(job.State.State, ShouldEqual, JobStateRunning)
@@ -903,7 +962,7 @@ func TestAddTimer(t *testing.T) {
So(e.ExecuteSerializedAction(c, tqt.Payload, 0), ShouldBeNil)
// The job has finished (by timer handler). Moves back to SUSPENDED state.
- job, err = e.GetJob(c, jobID)
+ job, err = e.GetJobRA(c, jobID)
So(err, ShouldBeNil)
So(job.State.State, ShouldEqual, JobStateSuspended)
@@ -1046,6 +1105,16 @@ func (m fakeTaskManager) HandleTimer(c context.Context, ctl task.Controller, nam
////
+func sortedJobIds(jobs []*Job) []string {
+ ids := stringset.New(len(jobs))
+ for _, j := range jobs {
+ ids.Add(j.JobID)
+ }
+ asSlice := ids.ToSlice()
+ sort.Strings(asSlice)
+ return asSlice
+}
+
// prepareQueuedJob makes datastore entries for a job in QUEUED state.
func prepareQueuedJob(c context.Context, jobID string, invNonce int64) {
taskBlob, err := proto.Marshal(&messages.TaskDefWrapper{

Powered by Google App Engine
This is Rietveld 408576698