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
Support GitHub two factor authentication #536
Conversation
Hi, I'm concerned that this will give the user a false sense of security. Perhaps add a popup explaining the user what actually happens, as this is not true 2FA as might be expected when looking at the GUI. On the other hand, the current approach in abapGit does not work for GitHub users using 2FA, hmm For encrypting the token I suggest using https://github.com/Sumu-Ning/AES just make it a dynamic binding so it is not required statically, and show a popup if the user needs to install it. What 2FA setup have you used for testing? Something like Google Authenticator? I have a Yubikey, which sends a SMS code after entering the password if the Yubikey is not supported. Do you know if there is a feature in the github API to check if the user has 2FA enabled? Perhaps it will be easier to guide the user to create the access token and enter it manually in abapGit settings? |
Hi Lars, true 2FA would be quite cumbersome to implement. Git over http(s) on GitHub does not support authentication using 2FA while GitHub's own API does, so one would have to use GitHub's API for all git requests if it is a GitHub repository which I doubt is worth it. I agree with you though, currently it is not apparent for what the token is used. An info button with a documentation popup next to the token input field could solve this I think. I personally use the Google Authenticator app for several services supporting two factor authentication including GitHub. SMS should be solvable as well (or already works just you need two tries?). You can get the type of two factor auth the account uses by reading the Now that I think about it, a better work flow might be to try to authenticate just with the login data and if you get a 401 response with that specific header you reopen the popup with the entered data and additionally show the token field. If you dislike the approach of creating an access token "in application": Using manually created access tokens as the password already works very well without this PR. The problem is just that you need to reenter it every time. If there was a password safe in general in abapGit independent of 2FA this problem would be solved. The current repository based login data would need to be moved up one level though to git hosting services, because otherwise you create access tokens / save login data for each repository even though they all use the same logins. So which direction do you want to go? |
hmm, not sure, need some time to consider. Which direction do you prefer/recommend? |
New idea: how about deleting the token immediately after use? That way it will not have to be stored in abapGit. If abapGit dumps after having created the token, it will not delete it, but that might be okay? |
Good idea, that seems to work best with the existing concept. We might even be able to delete the last used token when requesting a new one instead of immediately after use. I also did some research on other git hosters. Gitlab and TFS support access tokens but I did not find an API to create one programmatically. Bitbucket does have one but it might require a browser based approach. So for these ones letting the user create a token on the respective website on his own and saving it in abapGit's not yet existing password storage would be the best solution in my opinion. However, I think GitHub is by far the most used option of all these hosters so it does make sense to support its two factor authentication "natively". Also your idea is more in scope of the current approach of this PR while a password storage could/should be a separate one if it is still needed then. So I would refactor this PR to achieve the following:
Is that in line with your idea? |
Yep, looks good to me 😄 |
047cee1
to
3b01baf
Compare
src/zabapgit_http.prog.abap
Outdated
@@ -494,6 +499,51 @@ CLASS lcl_http IMPLEMENTATION. | |||
iv_username = lv_user ). | |||
ENDIF. | |||
|
|||
" Is the repository hoster supported for using two factor authentication? | |||
TRY. | |||
IF lcl_2fa_authenticator_registry=>is_url_supported( iv_url ) = abap_true. |
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.
can we take most of this part and move to a method in the 2fa include? and have it raise lcx_exception
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.
Sure, I'll get on that
sorry, merging #559 and changing all files in abapGit so you might need to rebase/fix some conflicts |
cl_sxml_writer/reader do not support json on AS ABAP 7.02
3b01baf
to
0026e74
Compare
No problem, rebase worked out fine I think. I'll comment again once I got the token deletion working. |
Sorry for the delay!
One thing I am mildly concerned about is the parsing logic for the list of all access tokens to filter out the one by abapGit (code). Since on 7.02 the new fancy JSON and REST apis do not exist yet (or I do not know the older ones?) I try to parse the correct token ID using a regular expression which is not ideal and might not handle all edge cases if the response structure is any different. |
travis errors: fixed, I've released a new version of abaplint parsing logic: worst case we have to adjust it, lets just ship it and see if it works out 😄 |
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.
see comments
src/zabapgit_2fa.prog.abap
Outdated
|
||
CLASS lcl_2fa_github_authenticator IMPLEMENTATION. | ||
METHOD constructor. | ||
super->constructor( 'https?:\/\/(www\.)?github.com.*$' ). |
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.
should be "^https?://(www.)?github.com.*$" ? but I guess it does not matter much
src/zabapgit_2fa.prog.abap
Outdated
" Try to authenticate, if 2FA is required there will be a specific response header | ||
li_client->authenticate( username = iv_username password = iv_password ). | ||
li_client->send( ). | ||
li_client->receive( ). |
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.
dumps here, HTTP_COMMUNICATION_FAILURE, see comment for CREATE_BY_URL
src/zabapgit_2fa.prog.abap
Outdated
cl_http_client=>create_by_url( | ||
EXPORTING | ||
url = gc_github_api_url | ||
IMPORTING |
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.
add following, I get a dump due to untrusted SSL certificates, as it currently does not use the same PSE(?) as the other abapGit requests
ssl_id = 'ANONYM'
proxy_host = lo_settings->get_proxy_url( )
proxy_service = lo_settings->get_proxy_port( )
see example in include ZABAPGIT_HTTP
src/zabapgit_2fa.prog.abap
Outdated
" Try to login to GitHub API with username, password and 2fa token | ||
cl_http_client=>create_by_url( | ||
EXPORTING | ||
url = gc_github_api_url |
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.
also add SSL_ID and proxy settings here
Using tokens as before still works However I dont receive a SMS code, will look into why. |
SMS codes are currently not triggered, it requires a real request to be sent, I've changed the code in method
|
Thanks for the review! |
STRUST: nope, no known workaround, will do some testing, if possible would like to not break stuff for existing users. Perhaps we need to update the wiki installation guide |
src/zabapgit_2fa.prog.abap
Outdated
METHOD set_new_token_request. | ||
DATA: lv_json_string TYPE string. | ||
|
||
lv_json_string = |\{"scopes":["repo"],"note":"abapGit","fingerprint":"abapGit2FA"\}|. |
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.
Can we change the description of the token to "Generated by abapGit" or "Generated by ZABAPGIT"?
SMS: I get the SMS code and it works. But the 2nd time does not work, as it has to delete the old token, guess the SMS codes are real one time codes. Do you know if the |
SSL certificates: looks like api.github.com works with the main github.com certificate, so everything okay :) |
Can you try again now if the one SMS code is used for both the deletion and the creation request? |
Also I had to add the *.github.com certificate in addition to the normal one in STRUST for it to work. I guess we have configured the normal certificate differently? If anyone has the same configuration as me this would be a breaking change because I just read Installing abapGit and therefore assume you imported the root certificate in STRUST while I just imported the two "end of the chain" github certificates. Not sure how many others did it the way I did. Might want to have |
STRUST: yeah, the top certificate is enough Now it works when I have a breakpoint, and occasionally when pushing. I think we need to add a definitions-at-top: it is a but in abaplint, will fix it in a bit. However I'm also a bit afraid of using STATICS here, perhaps make it a member attribute and clear it whenever |
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.
also see comment above
RAISE EXCEPTION TYPE lcx_2fa_token_gen_failed | ||
EXPORTING | ||
iv_error_text = 'Token generation failed: parser error' ##NO_TEXT. | ||
ENDIF. |
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.
think we need to add a WAIT UP TO 1 SECONDS.
here, as Github needs to finish saving the token before it is used
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.
Interesting that GitHub returns the token before it is fully persisted and ready to use. I also don't have that problem on my end.
I'd prefer adding RZL_SLEEP
instead of WAIT
to avoid the implicit commit.
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.
yeah, I guess the token has to be distributed to the other servers in the cluster. But it seems like this is the issue,
RZL_SLEEP will also do the trick
tested ok, now it seems to work every time, merging 😄 thanks @flaiker |
@flaiker are you on twitter? |
@larshp My pleasure! And no I am not, some bot took my name a while ago and since then I am averted :P |
References #494
Fixes #253
This should work for now. General approach:
And when leaving and reentering abapGit:
The token can also be deleted from the login popup.
One thing that is still todo is encrypting the access token. Currently it is stored in abapGit's persistence layer in plain text which is not ideal on multi user development systems where anyone can access database tables directly. My idea was to symmetrically encrypt the access token using the user's password he entered for generating the token and decrypt it using the same password. abapGit does not need to validate the decryption because if the password is wrong and a wrong access key is decrypted authentication will fail anyways. But I could not find any standard function modules or classes that support symmetric encryption using a key. Any tips would be appreciated there. The two spots in code are marked with TODO.
Another thing is that it sometimes takes multiple tries for me to generate a new access token. Might be caused by my authenticator being out of sync or an API rate limit but also could be a programming error on my end.
I think I removed all the uses of >702 stuff but I am not sure how to test for that (my system is 750).