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

Improving ACL #2951

Merged
merged 14 commits into from
Feb 13, 2019
Merged

Improving ACL #2951

merged 14 commits into from
Feb 13, 2019

Conversation

gitlw
Copy link

@gitlw gitlw commented Jan 30, 2019

  • Added a new subcommand to reset password for a user
  • Allow the useradd command to take a user's password through terminal
  • Added support to specify ACL rules through regex on predicates, e.g. ^user(.*)name$ -> Read

This change is Reviewable

@gitlw gitlw requested review from manishrjain and a team and removed request for manishrjain January 30, 2019 02:27
Copy link
Contributor

@martinmr martinmr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 0 of 7 files reviewed, 11 unresolved discussions (waiting on @gitlw and @manishrjain)


edgraph/access_ee.go, line 285 at r1 (raw file):

type PredRegexRule struct {
	PredRegex *regexp.Regexp
	Perm      int32

I am assuming Perm stands for permissions. Since we can have r+w permissions, would it be better to call it Perms?


edgraph/access_ee.go, line 288 at r1 (raw file):

}

// convert the acl blob to two data sets:

start the comment with

UnmarshalAcl converts the acl blob ...

to avoid a lint warning.


edgraph/access_ee.go, line 306 at r1 (raw file):

			predRegex, err := regexp.Compile(acl.PredFilter.Regex)
			if err != nil {
				glog.Errorf("Unable to compile the predicate regex %v to create an ACL rule",

Should we ignore this error? How will users realize that the ACL rules were not added?

I can see people not verifying the rules leading to unauthorized access.


edgraph/access_ee.go, line 679 at r1 (raw file):

// the operation on the given predicate
func hasAccess(groupId string, predicate string, operation *acl.Operation) error {
	// first try to evaluate the request using the predicate -> permission map

First, try to ...

Also period at the end.


edgraph/access_ee.go, line 712 at r1 (raw file):

	}

	return fmt.Errorf("unable to find a matched rule")

s/matched/matching


ee/acl/groups.go, line 173 at r1 (raw file):

	}
	if len(predicate) == 0 && len(predRegex) == 0 {
		return fmt.Errorf("either --pred or --pred_regex should be specified, but not both")

I think this error message is wrong. It should be something like

"one of --pred or --pred_regex must be specified"


ee/acl/groups.go, line 264 at r1 (raw file):

Quoted 6 lines of code…
		if (!curFilter.IsRegex &&
			!newFilter.IsRegex &&
			curFilter.Predicate == newFilter.Predicate) ||
			(curFilter.IsRegex &&
				newFilter.IsRegex &&
				curFilter.Regex == newFilter.Regex) {

For readability, I would move this into a method that compares two filters and returns true if they are equal.


ee/acl/groups_test.go, line 75 at r1 (raw file):

	require.Equal(t, "friend", updatedAcls5[0].PredFilter.Predicate,
		"the left acl should have the original "+
			"first predicate")

I think this fits well into the previous line. No need to split it.


ee/acl/run_ee.go, line 99 at r1 (raw file):

		},
	}
	chPdFlags := cmdPasswd.Cmd.Flags()

nit: chPwdFlags is a slightly better name


ee/acl/users.go, line 30 at r1 (raw file):

	userid := conf.GetString("user")
	if len(userid) == 0 {
		return fmt.Errorf("The user must not be empty")

lowercase to match the rest of the error messages.


ee/acl/utils.go, line 135 at r1 (raw file):

func askUserPassword(userid string, times int) (string, error) {
	x.AssertTrue(times == 1 || times == 2)

Why do we want to allow users to type their passwords without confirming them?

Copy link
Author

@gitlw gitlw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 0 of 7 files reviewed, 10 unresolved discussions (waiting on @manishrjain and @martinmr)


edgraph/access_ee.go, line 285 at r1 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

I am assuming Perm stands for permissions. Since we can have r+w permissions, would it be better to call it Perms?

Done.


edgraph/access_ee.go, line 288 at r1 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

start the comment with

UnmarshalAcl converts the acl blob ...

to avoid a lint warning.

Done.


edgraph/access_ee.go, line 306 at r1 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

Should we ignore this error? How will users realize that the ACL rules were not added?

I can see people not verifying the rules leading to unauthorized access.

I added a check in the chMod command to make sure the passed in regex strings can be compiled.
After that change, the error here should not happen. But I feel it's still worthwhile to log the error for unexpected cases.


edgraph/access_ee.go, line 679 at r1 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

First, try to ...

Also period at the end.

Done.


edgraph/access_ee.go, line 712 at r1 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

s/matched/matching

Done.


ee/acl/groups.go, line 173 at r1 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

I think this error message is wrong. It should be something like

"one of --pred or --pred_regex must be specified"

Done.


ee/acl/groups.go, line 264 at r1 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…
		if (!curFilter.IsRegex &&
			!newFilter.IsRegex &&
			curFilter.Predicate == newFilter.Predicate) ||
			(curFilter.IsRegex &&
				newFilter.IsRegex &&
				curFilter.Regex == newFilter.Regex) {

For readability, I would move this into a method that compares two filters and returns true if they are equal.

Done.


ee/acl/groups_test.go, line 75 at r1 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

I think this fits well into the previous line. No need to split it.

Done.


ee/acl/users.go, line 30 at r1 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

lowercase to match the rest of the error messages.

Done.


ee/acl/utils.go, line 135 at r1 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

Why do we want to allow users to type their passwords without confirming them?

Asking for password once is used when we ask for the groot password before proceeding in the subcommands. In that case, it's not used for changing its password.

ee/acl/run_ee.go Outdated
groupSB.WriteString(fmt.Sprintf("acls:%v\n", strings.Join(aclStrs, " ")))

glog.Infof(groupSB.String())
fmt.Sprintf("ACLs : %5s\n", strings.Join(aclStrs, " "))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result of fmt.Sprintf call not used (from govet)

Copy link
Contributor

@martinmr martinmr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 0 of 9 files reviewed, 2 unresolved discussions (waiting on @gitlw, @golangcibot, and @manishrjain)


edgraph/access_ee.go, line 306 at r1 (raw file):

Previously, gitlw (Lucas Wang) wrote…

I added a check in the chMod command to make sure the passed in regex strings can be compiled.
After that change, the error here should not happen. But I feel it's still worthwhile to log the error for unexpected cases.

Maybe it's better to do both? I can see why it's not a concern right now but in the future someone could mess up the code in chMod that does the check, causing the regex to fail to compile. Logging and then throwing an error would safeguard against that case.

Again, not very likely something like that would happen but it's better to err on the side of caution.

@@ -89,6 +89,8 @@ they form a Raft group and provide synchronous replication.
// with the flag name so that the values are picked up by Cobra/Viper's various config inputs
// (e.g, config file, env vars, cli flags, etc.)
flag := Alpha.Cmd.Flags()
flag.Bool("enterprise_features", false,
"Enable Dgraph enterprise features. If you set this to true, you agree to the Dgraph Community License.")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line is 107 characters (from lll)

Copy link
Author

@gitlw gitlw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 0 of 13 files reviewed, 3 unresolved discussions (waiting on @golangcibot, @manishrjain, and @martinmr)


dgraph/cmd/alpha/run.go, line 93 at r5 (raw file):

Previously, golangcibot (Bot from GolangCI) wrote…

line is 107 characters (from lll)

Done.


edgraph/access_ee.go, line 306 at r1 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

Maybe it's better to do both? I can see why it's not a concern right now but in the future someone could mess up the code in chMod that does the check, causing the regex to fail to compile. Logging and then throwing an error would safeguard against that case.

Again, not very likely something like that would happen but it's better to err on the side of caution.

Done.


ee/acl/run_ee.go, line 297 at r3 (raw file):

Previously, golangcibot (Bot from GolangCI) wrote…

result of fmt.Sprintf call not used (from govet)

Done.

Copy link
Author

@gitlw gitlw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 0 of 20 files reviewed, 4 unresolved discussions (waiting on @golangcibot, @manishrjain, and @martinmr)


dgraph/cmd/alpha/login_ee.go, line 60 at r6 (raw file):

Previously, golangcibot (Bot from GolangCI) wrote…

Error return value of writeResponse is not checked (from errcheck)

Done.

Copy link
Contributor

@manishrjain manishrjain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 0 of 20 files reviewed, 12 unresolved discussions (waiting on @gitlw, @golangcibot, @manishrjain, and @martinmr)


dgraph/cmd/root.go, line 85 at r7 (raw file):

	RootCmd.PersistentFlags().Bool("expose_trace", false,
		"Allow trace endpoint to be accessible from remote")
	RootCmd.PersistentFlags().Bool("enterprise_features", false,

Why remove this?


dgraph/cmd/alpha/login_ee.go, line 59 at r7 (raw file):

	var out bytes.Buffer
	out.WriteString(fmt.Sprintf("ACCESS JWT:\n%s\n", jwt.AccessJwt))

send back a JSON, so they can parse easily.


dgraph/cmd/alpha/run.go, line 93 at r7 (raw file):

	flag := Alpha.Cmd.Flags()
	flag.Bool("enterprise_features", false,
		"Enable Dgraph enterprise features. "+

This can lie in the last line.


dgraph/cmd/zero/zero.go, line 554 at r7 (raw file):

	// Multiple Groups might be assigned to same tablet, so during proposal we will check again.
	tablet.Force = false
	if _, isAclPred := x.AclPreds[tablet.Predicate]; isAclPred {

Can we just add a function in x, which can do this comparison in a good way, and use it here and elsewhere?


edgraph/access_ee.go, line 333 at r7 (raw file):

		// predPerms is the map descriebed above that maps a single
		// predicate to a submap, and the submap maps a group to a permission
		predPerms := make(map[string]map[string]int32)

This would go out of hands really quickly. Let's encapsulate all this logic of updating and accessing this cache in a separate class. And add tests for it.


edgraph/access_ee.go, line 383 at r7 (raw file):

		aclCache.Lock()
		aclCache.predPerms = predPerms

aclCache can just have a pointer to an object, which handles the logic of checking the group/pred perms.


edgraph/access_ee.go, line 541 at r7 (raw file):

	// if we get here, we know the user is not Groot.
	if op.DropAll {
		return fmt.Errorf("only Groot is allowed to drop all data")

Can still mention who the current user is. Can be anonymous.


edgraph/access_ee.go, line 740 at r7 (raw file):

func authorizePredicate(groups []string, predicate string, operation *acl.Operation) error {
	aclCache.RLock()
	predPerms, predRegexRules := aclCache.predPerms, aclCache.predRegexRules

still not safe. They are maps, which are pointers by default. This is not a copy.

You can just run the whole thing within the read lock. Not going to have much performance impact, because writes to these are rare.

Copy link
Author

@gitlw gitlw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 0 of 20 files reviewed, 12 unresolved discussions (waiting on @golangcibot, @manishrjain, and @martinmr)


dgraph/cmd/root.go, line 85 at r7 (raw file):

Previously, manishrjain (Manish R Jain) wrote…

Why remove this?

In our last review, we said we wanted to move this flag to be only applicable to alpha, since it does not make sense for the live loader, bulk loader, increment commands etc.


dgraph/cmd/alpha/login_ee.go, line 59 at r7 (raw file):

Previously, manishrjain (Manish R Jain) wrote…

send back a JSON, so they can parse easily.

Done.


dgraph/cmd/alpha/run.go, line 93 at r7 (raw file):

Previously, manishrjain (Manish R Jain) wrote…

This can lie in the last line.

Done.


dgraph/cmd/zero/zero.go, line 554 at r7 (raw file):

Previously, manishrjain (Manish R Jain) wrote…

Can we just add a function in x, which can do this comparison in a good way, and use it here and elsewhere?

Done.


edgraph/access_ee.go, line 333 at r7 (raw file):

Previously, manishrjain (Manish R Jain) wrote…

This would go out of hands really quickly. Let's encapsulate all this logic of updating and accessing this cache in a separate class. And add tests for it.

Done.


edgraph/access_ee.go, line 383 at r7 (raw file):

Previously, manishrjain (Manish R Jain) wrote…

aclCache can just have a pointer to an object, which handles the logic of checking the group/pred perms.

Done.


edgraph/access_ee.go, line 541 at r7 (raw file):

Previously, manishrjain (Manish R Jain) wrote…

Can still mention who the current user is. Can be anonymous.

Done.


edgraph/access_ee.go, line 740 at r7 (raw file):

Previously, manishrjain (Manish R Jain) wrote…

still not safe. They are maps, which are pointers by default. This is not a copy.

You can just run the whole thing within the read lock. Not going to have much performance impact, because writes to these are rare.

My current design is that these two data structures are immutable, and each update would create new maps and lists. Hence only the top level references are protected by the mutex.

Copy link
Contributor

@martinmr martinmr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 of 7 files at r1, 3 of 10 files at r5, 5 of 13 files at r6, 5 of 10 files at r7, 8 of 10 files at r8, 1 of 1 files at r9.
Reviewable status: all files reviewed, 19 unresolved discussions (waiting on @gitlw, @golangcibot, @manishrjain, and @martinmr)


dgraph/cmd/alpha/http.go, line 264 at r9 (raw file):

Quoted 5 lines of code…
	if accessJwt := r.Header.Get("X-Dgraph-AccessJWT"); accessJwt != "" {
		md := metadata.New(nil)
		md.Append("accessJwt", accessJwt)
		ctx = metadata.NewIncomingContext(ctx, md)
	}

This logic is repeated twice. Would it make sense to have it as a helper method.


dgraph/cmd/alpha/http.go, line 440 at r9 (raw file):

	if accessJwt := r.Header.Get("X-Dgraph-AccessJWT"); accessJwt != "" {
		md.Append("accessJwt", accessJwt)
	}

This logic is also similar. Would it make sense to include it in the helper method as well.


edgraph/access_ee.go, line 524 at r1 (raw file):

			}); err != nil {
			return status.Error(codes.PermissionDenied,
				fmt.Sprintf("unauthorized to alter the predicate:%v", err))

add space after the colon


edgraph/access_ee.go, line 578 at r9 (raw file):

}

type ACLAccessLog struct {

I'd prefer AclAccessLog. All those uppercase letters together are harder to read.


ee/acl/groups.go, line 44 at r9 (raw file):

	defer func() {
		if err := txn.Discard(ctx); err != nil {
			fmt.Printf("Unable to discard transaction: %v\n", err)

why are you moving this messages to stdout? Is this meant to be run from the console?


ee/acl/groups.go, line 157 at r9 (raw file):

	}
	if len(predicate) > 0 && len(predRegex) > 0 {
		return fmt.Errorf("one of --pred or --pred_regex must be specified")

change both error messages to "only one of ..."

Copy link
Contributor

@martinmr martinmr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 of 3 files at r10.
Reviewable status: 20 of 22 files reviewed, 14 unresolved discussions (waiting on @gitlw, @golangcibot, @manishrjain, and @martinmr)


dgraph/cmd/alpha/http.go, line 264 at r9 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…
	if accessJwt := r.Header.Get("X-Dgraph-AccessJWT"); accessJwt != "" {
		md := metadata.New(nil)
		md.Append("accessJwt", accessJwt)
		ctx = metadata.NewIncomingContext(ctx, md)
	}

This logic is repeated twice. Would it make sense to have it as a helper method.

Ok.


dgraph/cmd/alpha/http.go, line 440 at r9 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…
	if accessJwt := r.Header.Get("X-Dgraph-AccessJWT"); accessJwt != "" {
		md.Append("accessJwt", accessJwt)
	}

This logic is also similar. Would it make sense to include it in the helper method as well.

Ok.


edgraph/access_ee.go, line 524 at r1 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

add space after the colon

Ok.


edgraph/access_ee.go, line 578 at r9 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

I'd prefer AclAccessLog. All those uppercase letters together are harder to read.

Ok.


ee/acl/groups.go, line 44 at r9 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

why are you moving this messages to stdout? Is this meant to be run from the console?

Feel free to mark this as done if there's nothing to do here.


ee/acl/groups.go, line 157 at r9 (raw file):

Previously, martinmr (Martin Martinez Rivera) wrote…

change both error messages to "only one of ..."

Ok.

Copy link
Contributor

@manishrjain manishrjain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm: Couple of comments. Address those and merge.

Reviewable status: 20 of 22 files reviewed, 10 unresolved discussions (waiting on @gitlw, @golangcibot, @manishrjain, and @martinmr)


dgraph/cmd/alpha/login_ee.go, line 58 at r10 (raw file):

	response := map[string]interface{}{}
	mp := map[string]interface{}{}

aren't they strings? If so, can be a map[string]string.


edgraph/access_ee.go, line 462 at r10 (raw file):

		// check that we have the modify permission on the predicate
		err := aclCache.authorizePredicate(groupIds, op.DropAttr, acl.Modify)
		logACLAccess(&AclAccessLog{

Rename to logAccess(&AccessRecord)


edgraph/access_ee.go, line 578 at r10 (raw file):

}

type AclAccessLog struct {

rename to accessEntry.

@gitlw gitlw merged commit c8281f1 into master Feb 13, 2019
@gitlw gitlw deleted the gitlw/improving_acl branch February 13, 2019 18:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants