-
Notifications
You must be signed in to change notification settings - Fork 26
Restrict access of authenticated users #60
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
\cc @carlosms for review
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like your implementation!!!
I'd only require some things:
checkAccess
does not need to be an OAuth member, #60 (comment)checkAccess
has a too long body that could be extracted in separate functions, #60 (comment)- coding style #60 (comment)
questions:
- do we really need to exclude
pending
users? #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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does not need access to (o *OAuth)
, does it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't, but I prefer to group functions as struct methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we (at src-d) are separating code blocks with a new line in between.
(examples in gogit)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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}
server/service/oauth.go
Outdated
if resp.StatusCode != http.StatusOK { | ||
return NoAccessError | ||
} | ||
// Don't allow access to pending members |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 nil
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because pending member isn't member
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't feel too long for me, but ok.
README.md
Outdated
|
||
### 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not add them to the .env.tpl
file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
server/service/oauth.go
Outdated
"golang.org/x/oauth2/github" | ||
) | ||
|
||
var NoAccessError = errors.New("access deniend") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exported without comment
if err == NoAccessError { | ||
role = model.Worker | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
}
}
server/service/oauth.go
Outdated
} | ||
} | ||
|
||
role := model.Requester |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are using the reviewer
and requester
words for the same concept.
Maybe this PR is a good place to fix this.
server/service/oauth.go
Outdated
} | ||
return nil | ||
} | ||
return errors.New("invalid restriction") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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_ACCESS
OAUTH_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? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e0bdc4d
to
d19af6f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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, nil
If 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything is fine but one thing easy to change.
Once it is addressed, feel free to merge 😄
.env.tpl
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 for consistency.
Let's rename and merge, with subsequent improvements planned in #72
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's try to use correct names everywhere. In the issue description, it's called reviewers
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, many thanks!
Fixes: #48