Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add guardians group with full authorization #4447

Merged
merged 10 commits into from
Dec 24, 2019
97 changes: 63 additions & 34 deletions edgraph/access_ee.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,60 +358,87 @@ func ResetAcl() {
return
}

upsertGroot := func(ctx context.Context) error {
queryVars := map[string]string{
"$userid": x.GrootId,
"$password": "",
}
queryRequest := api.Request{
Query: queryUser,
Vars: queryVars,
upsertGuardians := func(ctx context.Context) error {
query := fmt.Sprintf(`
{
guid as var(func: eq(dgraph.xid, "%s"))
}
`, x.AdminGId)
groupNQuads := acl.CreateGroupNQuads(x.AdminGId)
req := &api.Request{
CommitNow: true,
Query: query,
Mutations: []*api.Mutation{
{
Set: groupNQuads,
Cond: "@if(eq(len(guid), 0))",
},
},
}

queryResp, err := (&Server{}).doQuery(ctx, &queryRequest, NoAuthorize)
_, err := (&Server{}).doQuery(ctx, req, NoAuthorize)
if err != nil {
return errors.Wrapf(err, "while querying user with id %s", x.GrootId)
return errors.Wrapf(err, "while upserting group with id %s", x.AdminGId)
}
startTs := queryResp.GetTxn().StartTs

rootUser, err := acl.UnmarshalUser(queryResp, "user")
if err != nil {
return errors.Wrapf(err, "while unmarshaling the root user")
}
if rootUser != nil {
glog.Infof("The groot account already exists, no need to insert again")
return nil
}
glog.Infof("Successfully upserted the guardian group")
return nil
}

// Insert Groot.
createUserNQuads := acl.CreateUserNQuads(x.GrootId, "password")
upsertGroot := func(ctx context.Context) error {
query := fmt.Sprintf(`
{
grootid as var(func: eq(dgraph.xid, "%s"))
guid as var(func: eq(dgraph.xid, "%s"))
}
`, x.GrootId, x.AdminGId)
userNQuads := acl.CreateUserNQuads(x.GrootId, "password")
userNQuads = append(userNQuads, &api.NQuad{
Subject: "_:newuser",
Predicate: "dgraph.user.group",
ObjectId: "uid(guid)",
})
req := &api.Request{
StartTs: startTs,
CommitNow: true,
Query: query,
Mutations: []*api.Mutation{
{
Set: createUserNQuads,
Set: userNQuads,
// Assuming that if groot exists, it is in guardian group
Cond: "@if(eq(len(grootid), 0) and gt(len(guid), 0))",
},
},
}

_, err = (&Server{}).doQuery(context.Background(), req, NoAuthorize)
_, err := (&Server{}).doQuery(ctx, req, NoAuthorize)
if err != nil {
return err
return errors.Wrapf(err, "while upserting user with id %s", x.GrootId)
}
glog.Infof("Successfully upserted the groot account")

glog.Infof("Successfully upserted groot account")
return nil
}

for {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
if err := upsertGuardians(ctx); err != nil {
glog.Infof("Unable to upsert the guardian group. Error: %v", err)
time.Sleep(100 * time.Millisecond)
continue
}
break
}

for {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
if err := upsertGroot(ctx); err != nil {
glog.Infof("Unable to upsert the groot account. Error: %v", err)
time.Sleep(100 * time.Millisecond)
} else {
return
continue
}
break
}
}

Expand Down Expand Up @@ -502,7 +529,8 @@ func authorizeAlter(ctx context.Context, op *api.Operation) error {
userId = userData[0]
groupIds = userData[1:]

if userId == x.GrootId {
if x.IsSuperUser(groupIds) {
// Members of guardian group are allowed to alter anything.
return nil
}
}
Expand Down Expand Up @@ -599,12 +627,13 @@ func authorizeMutation(ctx context.Context, gmu *gql.Mutation) error {
userId = userData[0]
groupIds = userData[1:]

if userId == x.GrootId {
// groot is allowed to mutate anything except the permission of the acl predicates
if x.IsSuperUser(groupIds) {
// Members of guardian group are allowed to mutate anything
// except the permission of the acl predicates
if isAclPredMutation(gmu.Set) {
return errors.Errorf("the permission of ACL predicates can not be changed")
} else if isAclPredMutation(gmu.Del) {
// even groot can't delete ACL predicates
// Even members of gurardian group can't delete ACL predicates
return errors.Errorf("ACL predicates can't be deleted")
}
return nil
Expand Down Expand Up @@ -699,8 +728,8 @@ func authorizeQuery(ctx context.Context, parsedReq *gql.Result) error {
userId = userData[0]
groupIds = userData[1:]

if userId == x.GrootId {
// groot is allowed to query anything
if x.IsSuperUser(groupIds) {
// Members of guardian groups are allowed to query anything.
return nil
}
}
Expand Down
13 changes: 1 addition & 12 deletions ee/acl/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,18 +151,7 @@ func groupAdd(conf *viper.Viper, groupId string) error {
return errors.Errorf("group %q already exists", groupId)
}

createGroupNQuads := []*api.NQuad{
{
Subject: "_:newgroup",
Predicate: "dgraph.xid",
ObjectValue: &api.Value{Val: &api.Value_StrVal{StrVal: groupId}},
},
{
Subject: "_:newgroup",
Predicate: "dgraph.type",
ObjectValue: &api.Value{Val: &api.Value_StrVal{StrVal: "Group"}},
},
}
createGroupNQuads := CreateGroupNQuads(groupId)

mu := &api.Mutation{
CommitNow: true,
Expand Down
70 changes: 70 additions & 0 deletions ee/acl/acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,3 +474,73 @@ func TestUnauthorizedDeletion(t *testing.T) {
require.Error(t, err)
require.Contains(t, err.Error(), "PermissionDenied")
}

func TestSuperUserAcess(t *testing.T) {
ctx, _ := context.WithTimeout(context.Background(), 100*time.Second)
unAuthPred := "unauthorizedPredicate"

dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr)
require.NoError(t, err)

op := api.Operation{
DropAll: true,
}
require.NoError(t, dg.Alter(ctx, &op))

op = api.Operation{
Schema: fmt.Sprintf("%s: string @index(exact) .", unAuthPred),
}
require.NoError(t, dg.Alter(ctx, &op))

createSuperUser := exec.Command("dgraph", "acl", "add", "-a", dgraphEndpoint,
"-u", "superuser", "-p", "superpassword", "-x", "password")
require.NoError(t, createSuperUser.Run(), "Error while creating super user")

makeSuperUser := exec.Command("dgraph", "acl", "mod", "-a", dgraphEndpoint, "-u", "superuser",
"-l", x.AdminGId, "-x", "password")
require.NoError(t, makeSuperUser.Run(), "Error while adding superuser to guardians group")

txn := dg.NewTxn()
mutation := &api.Mutation{
SetNquads: []byte(fmt.Sprintf("_:a <%s> \"testdata\" .", unAuthPred)),
CommitNow: true,
}
resp, err := txn.Mutate(ctx, mutation)
require.NoError(t, err)

nodeUID, ok := resp.Uids["a"]
require.True(t, ok)

time.Sleep(6 * time.Second)
superClient, err := testutil.DgraphClient(testutil.SockAddr)
require.NoError(t, err, "Error while creating client")

superClient.Login(ctx, "superuser", "superpassword")

txn = superClient.NewTxn()
mutString := fmt.Sprintf("<%s> <%s> \"testdata\" .", nodeUID, unAuthPred)
mutation = &api.Mutation{
SetNquads: []byte(mutString),
CommitNow: true,
}
_, err = txn.Mutate(ctx, mutation)
require.NoError(t, err, "Error while mutating unauthorized predicate")

txn = superClient.NewTxn()
query := fmt.Sprintf(`
{
me(func: eq(%s, "testdata")) {
uid
}
}`, unAuthPred)

resp, err = txn.Query(ctx, query)
require.NoError(t, err, "Error while querying unauthorized predicate")
require.Contains(t, string(resp.GetJson()), "uid")

op = api.Operation{
Schema: fmt.Sprintf("%s: int .", unAuthPred),
}
err = superClient.Alter(ctx, &op)
require.NoError(t, err, "Error while altering unauthorized predicate")
}
18 changes: 17 additions & 1 deletion ee/acl/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func getClientWithAdminCtx(conf *viper.Viper) (*dgo.Dgraph, x.CloseFunc, error)

// CreateUserNQuads creates the NQuads needed to store a user with the given ID and
// password in the ACL system.
func CreateUserNQuads(userId string, password string) []*api.NQuad {
func CreateUserNQuads(userId, password string) []*api.NQuad {
return []*api.NQuad{
{
Subject: "_:newuser",
Expand All @@ -194,3 +194,19 @@ func CreateUserNQuads(userId string, password string) []*api.NQuad {
},
}
}

// CreateGroupNQuads cretes NQuads needed to store a group with the give ID.
func CreateGroupNQuads(groupId string) []*api.NQuad {
return []*api.NQuad{
{
Subject: "_:newgroup",
Predicate: "dgraph.xid",
ObjectValue: &api.Value{Val: &api.Value_StrVal{StrVal: groupId}},
},
{
Subject: "_:newgroup",
Predicate: "dgraph.type",
ObjectValue: &api.Value{Val: &api.Value_StrVal{StrVal: "Group"}},
},
}
}
12 changes: 12 additions & 0 deletions x/x.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ const (

// GrootId is the ID of the admin user for ACLs.
GrootId = "groot"
// adminGId is the ID of the admin group for ACLs.
AdminGId = "guardians"
// AclPredicates is the JSON representation of the predicates reserved for use
// by the ACL system.
AclPredicates = `
Expand Down Expand Up @@ -734,3 +736,13 @@ func GetPassAndLogin(dg *dgo.Dgraph, opt *CredOpt) error {
// update the context so that it has the admin jwt token
return nil
}

func IsSuperUser(groups []string) bool {
for _, group := range groups {
if group == AdminGId {
return true
}
}

return false
}