Restrict access of authenticated users#60
Conversation
There was a problem hiding this comment.
I like your implementation!!!
I'd only require some things:
checkAccessdoes not need to be an OAuth member, #60 (comment)checkAccesshas a too long body that could be extracted in separate functions, #60 (comment)- coding style #60 (comment)
questions:
- do we really need to exclude
pendingusers? #60 (comment)
| const orgPrefix = "org:" | ||
| const teamPrefix = "team:" | ||
|
|
||
| func (o *OAuth) checkAccess(client *http.Client, restriction, login string) error { |
There was a problem hiding this comment.
does not need access to (o *OAuth), does it?
There was a problem hiding this comment.
doesn't, but I prefer to group functions as struct methods.
There was a problem hiding this comment.
wow... I think that's an ugly thing (and also forces extra steps in unit tests...) :(
| } | ||
| return nil | ||
| } | ||
| if strings.HasPrefix(restriction, teamPrefix) { |
There was a problem hiding this comment.
we (at src-d) are separating code blocks with a new line in between.
(examples in gogit)
There was a problem hiding this comment.
if we do that, we need linter for it. And please describe which blocks should be separated. From reading the source code of go-git I don't always see the pattern.
There was a problem hiding this comment.
Most of our code is always written that way (with NewLine after every code block).
(random files, also in borges... https://github.com/src-d/borges/blob/master/archiver.go https://github.com/src-d/borges/blob/master/common.go)
(we would also need a linter to ensure the opposite; the reason is it's an internal convention we decided to follow...)
The rule could be:
a NewLine after
}if it's not followed by another}
| if resp.StatusCode != http.StatusOK { | ||
| return NoAccessError | ||
| } | ||
| // Don't allow access to pending members |
There was a problem hiding this comment.
Why do we need to forbid "pending" members? If we don't care about that, all code below this comment can be replaced by
return nilThere was a problem hiding this comment.
because pending member isn't member
There was a problem hiding this comment.
but they was added by the org's owner, what suits our purpose...
|
|
||
| func (o *OAuth) checkAccess(client *http.Client, restriction, login string) error { | ||
| if strings.HasPrefix(restriction, orgPrefix) { | ||
| org := strings.TrimPrefix(restriction, orgPrefix) |
There was a problem hiding this comment.
too long.
we could extract the body of both ifs to separated functions.
func (o *OAuth) checkAccess(client *http.Client, restriction, login string) error {
if strings.HasPrefix(restriction, orgPrefix) {
org := strings.TrimPrefix(restriction, orgPrefix)
return checkUserInOrganization(client, org, login)
}
if strings.HasPrefix(restriction, teamPrefix) {
team := strings.TrimPrefix(restriction, teamPrefix)
return checkUserInTeam(client, team, login)
}
return return errors.New("invalid restriction")
}
func checkUserInOrganization(client *http.Client, org string, login string) error {}
func checkUserInTeam(client *http.Client, ream string, login string) error {}There was a problem hiding this comment.
doesn't feel too long for me, but ok.
|
|
||
| ### Restrict access | ||
|
|
||
| It's possible to restrict access to the tool or `reviewer` role by setting environmental variables: |
There was a problem hiding this comment.
Why not add them to the .env.tpl file?
carlosms
left a comment
There was a problem hiding this comment.
Besides the inline comments, there is a big obstacle in my opinion:
Right now we ask the administrator to set a team-id, but it seems that you can't get that ID from the GitHub web interface.
We should do something about it, ideally the code should be able to work with a team name instead of the ID.
I think that using something like team:<org-name>/<team-name> we should be able to use this api call to the the team ID:
https://developer.github.com/v3/orgs/teams/#list-teams
| "golang.org/x/oauth2/github" | ||
| ) | ||
|
|
||
| var NoAccessError = errors.New("access deniend") |
| if err == NoAccessError { | ||
| role = model.Worker | ||
| } | ||
| } |
There was a problem hiding this comment.
With the code above, the reviewers are a subgroup of the workers, right?
This is not wrong, but it does not match with what we discussed IRL. We said that for greater flexibility we would allow independent orgs/teams for each role.
What about something like this?
role := model.Requester
if o.restrictReviewerAccess != "" {
err := o.checkAccess(client, o.restrictReviewerAccess, user.Login)
if err != nil && err != NoAccessError {
return nil, err
}
if err == NoAccessError {
role = model.Worker
}
}
if role == model.Worker && o.restrictAccess != "" {
if err := o.checkAccess(client, o.restrictAccess, user.Login); err != nil {
return nil, err
}
}
| } | ||
| } | ||
|
|
||
| role := model.Requester |
There was a problem hiding this comment.
We are using the reviewer and requester words for the same concept.
Maybe this PR is a good place to fix this.
| } | ||
| return nil | ||
| } | ||
| return errors.New("invalid restriction") |
There was a problem hiding this comment.
Since this is an error intended for the admin responsible for the settings, and not a developer, I'd add more details to help:
return errors.New("invalid restriction %s, it must be one of 'org:<organization-name>' or '<team:team-id>'")
| OAUTH_RESTRICT_ACCESS=org:my-organization | ||
| OAUTH_RESTRICT_REVIEWER_ACCESS=team:123456 | ||
| ``` | ||
|
|
There was a problem hiding this comment.
May I suggest something more extended? The following text assumes that Reviewers are not a subgroup of the Workers. It is also worth noting that the current readme does not explain the two different roles.
Access Control
It is possible to restrict access and choose each user's role by adding their GitHub accounts to a specific organization or team.
This is optional, if you don't set any restrictions all users with a valid GitHub account will be able to login as a Reviewer. You may also set a restriction only for Reviewer users, and leave open access to anyone as Workers.
To do so, set the following variables in you .env file:
OAUTH_RESTRICT_ACCESSOAUTH_RESTRICT_REVIEWER_ACCESS
Both variables accept a string with either org:<organization-name> or team:<team-id>. For example:
OAUTH_RESTRICT_ACCESS=org:my-organization
OAUTH_RESTRICT_REVIEWER_ACCESS=team:123456|
Comments are addressed. About team-id:
|
b413329 to
83a0cf3
Compare
|
Probably a minor thing, but CI seems to be failing, @smacker could you briefly summarize the reason and how is it taken care of? All reviews seems to be addressed, so @carlosms @dpordomingo could you please make another round? |
|
@bzz restart of CI helped. It didn't clean up after itself first. (not sure why) |
As amusing your answer may be, the point still stands. We ask our users to provide the team-id without explaining what it is and how to obtain it. Could you please try to come up with a more constructive proposal? |
|
@carlosms haha. Let me rephrase. I propose to keep it as an internal feature. From requirements, I understood we need teams, not only organizations. And for internal usage, we can grab ID easily. Maybe we need to just remove documentation about it. The main reason why I don't want to write more code around it and expose it to external users:
|
I personally don't like the idea of having undocumented functionality, we either support teams or not. And if it's needed for our internal needs, that's a sign that it may be needed by other users.
Then maybe we should be using the latest v4 API: |
|
v4 is GraphQL API, not a REST one. |
83a0cf3 to
e0bdc4d
Compare
|
@carlosms thank you for keeping the discussion focused and appreciate the constructive feedback on why teams are not exposed yet. I think it should be OK for now to keep it as is, merge PR and then add nice-to-have integration that uses the name/more docs around it. @carlosms @dpordomingo could you please make another pass over it as the rest of the feedback seems to be addressed? |
e0bdc4d to
d19af6f
Compare
dpordomingo
left a comment
There was a problem hiding this comment.
This comments could be addressed by #72, so would not block the merge
| const orgPrefix = "org:" | ||
| const teamPrefix = "team:" | ||
|
|
||
| func (o *OAuth) checkAccess(client *http.Client, restriction, login string) error { |
There was a problem hiding this comment.
wow... I think that's an ugly thing (and also forces extra steps in unit tests...) :(
| return nil, fmt.Errorf("can't parse github user response: %s", err) | ||
| } | ||
|
|
||
| role := model.Requester |
There was a problem hiding this comment.
I think this code would be easier to read if it was:
if o.restrictRequesterAccess != "" {
if err := o.checkAccess(client, o.restrictRequesterAccess, user.Login); err == nil {
user.Role = model.Requester
return &user, nil
} else if err != ErrNoAccess {
return nil, err
}
}
if o.restrictAccess != "" {
if err := o.checkAccess(client, o.restrictAccess, user.Login); err == nil {
user.Role = model.Worker
return &user, nil
} else {
return nil, err
}
}
user.Role = model.Requester
return &user, nilIf I'm not wrong, that way it's easier to understand the logic behind the role assignment depending on GitHub groups and the default value if nothing happens.
dpordomingo
left a comment
There was a problem hiding this comment.
Everything is fine but one thing easy to change.
Once it is addressed, feel free to merge 😄
| JWT_SIGNING_KEY=testing | ||
| DB_CONNECTION=sqlite:///path/to/db.db | ||
| OAUTH_RESTRICT_ACCESS= | ||
| OAUTH_RESTRICT_REVIEWER_ACCESS= |
There was a problem hiding this comment.
Reviewer instead of Requesters?
I don't see the reason because this PR introduces a new concept colliding with an already existing one. We decide to call that role requester, so it would be a good idea to stick to our convention,
I'd suggest doing s/reviewer/requester/g before merging.
There was a problem hiding this comment.
👍 for consistency.
Let's rename and merge, with subsequent improvements planned in #72
There was a problem hiding this comment.
let's try to use correct names everywhere. In the issue description, it's called reviewers.
There was a problem hiding this comment.
It might be a misunderstanding when the latest issue was created.
In the schema definition issue and in our code, we're already using requester.
Signed-off-by: Maxim Sukharev <maxim@sourced.tech>
d19af6f to
8b3174e
Compare
|
pushed rename reviewer -> requester. |
Fixes: #48