-
Notifications
You must be signed in to change notification settings - Fork 388
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
Implementation of Oauth of Github, Google and Microsoft #4298
base: master
Are you sure you want to change the base?
Conversation
5ce7f18
to
68eaf7b
Compare
de2e213
to
d3f6b29
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.
Please see my remarks.
I have some additional comments apart from the direct in code messages:
- My major concern with the implementation is that the oauth related API and its implementation is not generalized enough. The configuration is good enough for the time being.
- I am not sure if we are allowed to use the Git Hub logo in our repo.
- Please invite @cservakt to review the JS and VueJS parts.
I did not do a thorough review of the oauth flow in authentication.py
after you addressed the above issues I will do another round concentrating on that.
Thanks for the hard work!
12c68e7
to
f064c2b
Compare
b4d5a0a
to
d3847d6
Compare
…s, commented lines
…y email for GitHub
9630841
to
4393e55
Compare
06d2ed8
to
c2d4ba5
Compare
…the token fetching process
c2d4ba5
to
e18d6ab
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.
Please make sure that you correctly and consistently use the token, session variable names.
Please consider factoring out the oauth access token into a separate table.
When do we remove rows from teh oauth session table?
How do we handle token refreshing? Would it be automated? what happens if an access token expires?
web/api/authentication.thrift
Outdated
// Returns list of providers for oauth for respective appearence of buttons. | ||
list<string> getOauthProviders(), | ||
|
||
// Create a link for the user to log in for github Oauth. |
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 github? I guess you could write instead: Create a link for the user to log in with an Oauth provider
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.
Fixed
try: | ||
date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") | ||
with DBSession(self.__config_db) as session: | ||
session.execute("DELETE FROM oauth_sessions " |
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 guess we should not use direct SQL statements in codechecker code. You should use sqlachemy statments instead.
https://docs.sqlalchemy.org/en/20/tutorial/data_update.html#the-delete-sql-expression-construct
also see other examples from the codechecker codebase. then you can also remove the sqlite3 dependency.
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.
Fixed, now uses sqlachemy statements for removal of expired sessions.
|
||
LOG.info("State inserted successfully.") | ||
|
||
except sqlite3.Error as e: |
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 you should not use sqlite3 exception handling and in general direct sqlite3 dependency should be removed.
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.
Resolved. Replaced all SQLite3-specific exceptions with standard exceptions. sqlite3 dependency has been removed
@@ -167,6 +270,160 @@ def performLogin(self, auth_method, auth_string): | |||
codechecker_api_shared.ttypes.ErrorCode.AUTH_DENIED, | |||
msg) | |||
|
|||
elif auth_method == "oauth": | |||
date_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |||
provider, url = auth_string.split("@") |
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.
could you please comment above what the auth_string is expected to look like in this case? an example is good enough.
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.
Fixed, added a comment with example of auth_string in case of OAuth authentication..
@timeit | ||
def createLink(self, provider): | ||
""" | ||
For creating a autehntication link for OAuth for specified provider |
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.
Please describe better what the funtion is expected to do.
This function returns an URL given the provider.
It also creates a session and inserts something(insertDataOauth()) in the dabase.
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.
Fixed, extended description of what the function does.
@@ -630,6 +669,65 @@ def create_session(self, auth_string): | |||
|
|||
return local_session | |||
|
|||
def create_session_oauth(self, provider, username, token): |
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.
please call token as access_token. If I understand correctly here token is the oauth access token
} | ||
|
||
# Generate a new token and create a local session. | ||
token = generate_session_token() |
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 is the codechecker session token, right?
maybe call is codechecker_session_token
# Generate a new token and create a local session. | ||
token = generate_session_token() | ||
# token_d is the access token of the oauth provider. | ||
token_d = user_data.get('oauth_access_token') |
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 call it oauth_access_token then?
|
||
if allowed_users == ["*"] or username in allowed_users: | ||
LOG.info("User %s is authorized.", username) | ||
session = self.__manager.create_session_oauth( |
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 is the codechecker session, right?
call it session_codecheker
code_verifier=pkce_verifier | ||
) | ||
# inserting data in database | ||
self.insertDataOauth(state=state, |
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 I understand correctly, we insert a row in this table every time a user tries to login. Then when will a row be removed from this table. Or will it grow indefinitely?
dfce37c
to
87c324b
Compare
ba4683b
to
3417d4d
Compare
3417d4d
to
a64fecb
Compare
d107d90
to
fcc5cd5
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.
Thank you for this development. This is a great and important work!
@@ -16,6 +16,9 @@ Table of Contents | |||
* [<i>LDAP</i> authentication](#ldap-authentication) | |||
* [Configuration options](#configuration-options) | |||
* Membership in custom groups with [<i>regex_groups</i>](#regex_groups-authentication) |
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.
The regex_groups
could be a sub-point like OAuth authentication.
web/api/authentication.thrift
Outdated
// Returns list of providers for oauth for respective appearence of buttons. | ||
list<string> getOauthProviders(), | ||
|
||
// Create a link for the user to log in for github Oauth. |
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.
Not just github.
"< DATETIME(\"" + date + "\")") | ||
session.commit() | ||
LOG.info("Expired state, validation codes removed successfully.") | ||
except sqlite3.Error as e: |
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 is it catching only sqlite3 errors? How about other database engines?
|
||
LOG.info("State inserted successfully.") | ||
|
||
except sqlite3.Error as e: |
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 is it catching only sqlite3 errors? How about other database engines?
@timeit | ||
def createLink(self, provider): | ||
""" | ||
For creating a autehntication link for OAuth for specified provider |
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 creating a autehntication link for OAuth for specified provider | |
For creating an authentication link for OAuth for specified provider. |
user_info = None | ||
username = None |
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.
Unnecessary initialization.
# if the provider is github it fetches primary email | ||
# from another api endpoint to maintain username as email | ||
# consistency between GitHub and other providers |
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.
What do we use "username" for in CodeChecker? If we want to identify authorized users by their e-mail addresses, then couldn't we use the "email" attribute of their user info? In this case it wouldn't be needed to distinguish GitHub here.
provider_cfg = self.__auth_config.get( | ||
'method_oauth', {}).get("providers", {}).get(provider, {}) | ||
|
||
# turn off configuration if it is set to default values |
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.
What is the purpose of this protection? I would say that examples can be provided in the documentation, but the template config file can have empty values and OAuth method being disabled.
@@ -64,6 +66,10 @@ def setup_class_common(): | |||
|
|||
codechecker.add_test_package_product(host_port_cfg, TEST_WORKSPACE) | |||
|
|||
subprocess.Popen(["python3", "oauth_server.py"], | |||
cwd="tests/functional/authentication") | |||
sleep(5) |
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.
Is this sleep()
operation intended or accidental?
self.last_access = last_access if last_access else datetime.now() | ||
|
||
def get_access_token(self): |
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.
Where is this function used?
1f5985d
to
45aa94d
Compare
45aa94d
to
2c535da
Compare
fixes #4160
The right way it should look after logging in
new added button to log in with github
Changes: