-
Notifications
You must be signed in to change notification settings - Fork 15
validate github app #169
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
base: master
Are you sure you want to change the base?
validate github app #169
Conversation
github/githubapp.go
Outdated
| ClientId string `json:"client_id"` | ||
| InstallationId int `json:"installation_id"` | ||
| PrivateKey string `json:"private_key"` // SENSITIVE | ||
| parsedRSAKey *rsa.PrivateKey |
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 really don't like the fact that this is nil unless someone calls app.Validate() method first.
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 normal life with pointers.
The real question is: why do we need to use a pointer here?
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.
Ping
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.
These are not super valid reasons but:
- it seemed as a good "fit" because I could just do:
key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(app.PrivateKey))
...
app.parsedRSAKey = key
since jwt.ParseRSAPrivateKeyFromPEM returns a pointer.
2. it allow me to easily check for empty struct with: *app == GitHubApp{}
Now, I had to write a more "complex" function to do that.
Anyway... none of this is a good enough explanation and I am happy to hear your opinion on when to use one or the other approach...
| } | ||
|
|
||
| if len(mandatory) > 0 { | ||
| return fmt.Errorf("github_app: missing mandatory keys: %s", strings.Join(mandatory, ", ")) |
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 first validate that all 3 fields are configured and then we parse the private RSA key...
| }, | ||
| wantErr: "source: missing keys: github_app.client_id", | ||
| }, | ||
| { |
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 now better tested in: github/githubapp_test.go...
cogito/protocol.go
Outdated
| // if access token is not set; we are using GitHub app. Validate it. | ||
| if src.AccessToken == "" { |
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 find this way of testing confusing and even error-prone if we change the order of the code. It seems to me that the previous way (deleted line 266) was more straightforward. What was the reason to change the logic for the if ?
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.
At that point, we know that src.AccessToken != "" xor src.GitHubApp != github.GitHubApp{} is true so I agree with Marco that doing the corresponding direct test would be better than the contrapositive.
| // if access token is not set; we are using GitHub app. Validate it. | |
| if src.AccessToken == "" { | |
| // if access token is not set; we are using GitHub app. Validate it. | |
| if src.GitHubApp != github.GitHubApp{} { |
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.
Done as suggested. I keep using the IsZero() method..
We can't compare with: src.GitHubApp != github.GitHubApp{} anymore since I added the rsa.Private key field that is not a pointer.
github/githubapp_test.go
Outdated
| privateKey, err := testhelp.GeneratePrivateKey(t, 2048) | ||
| assert.NilError(t, err) | ||
|
|
||
| app := github.GitHubApp{ | ||
| ClientId: "client-id", | ||
| InstallationId: 12345, | ||
| PrivateKey: string(testhelp.EncodePrivateKeyToPEM(privateKey)), |
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 am looking at lines 66 and 72. In both cases we own the functions that manipulate the key data (they are 2 test helpers).
What is not clear to me is this: Why are we doing 2 transformations? Why not using only 1 test helper, that produces directly a private key encoded to PEM ?
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.
Ah the reason for the split was I need both the *rsa.PrivateKey and PEM string...
e.g. in DecodeJWT(... key *rsa.PrivateKey)) function I have to use: &key.PublicKey
Sadly, I remembered this point only after I implemented the fix according to your suggestion. Hence, I had to revert to what I had, with minor change... EncodePrivateKeyToPEM now returns a string instead of slice of bytes.
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.
GitHub UI is killing me. Going back to the thread in question:
I still do not understand. Where is privateKey used in the test?
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 privateKey used in the test?
You mean where is:
privateKey, err := testhelp.GeneratePrivateKey(t, 2048)
used?
Its used here:
app := github.GitHubApp{
...
PrivateKey: string(testhelp.EncodePrivateKeyToPEM(privateKey)),
where I pass it as a parameter to function: testhelp.EncodePrivateKeyToPEM
Or I don't understand your question....
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.
So it seems that my original question is still valid:
What is not clear to me is this: Why are we doing 2 transformations? Why not using only 1 test helper, that produces directly a private key encoded to PEM ?
?
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 thought I answered this already just above...
At first, I fell in the same trap and went to adjust according to your comment... Only to found the real reason: There are more tests... In some other tests, I need a public part of the private key in some third function: DecodeJWT(... key *rsa.PrivateKey)) where I have to use: &key.PublicKey
So:
- I create a private key with:
privateKey, err := testhelp.GeneratePrivateKey(t, 2048) - In some tests its enough to get the PEM string:
testhelp.EncodePrivateKeyToPEM - in other tests I need both the PEM string and public key part of the private key OBJECT (
&key.PublicKey)... In example, in some tests I want to decode the JWT token to confirm it contains correct/expected claims...
cogito/protocol.go
Outdated
| // if access token is not set; we are using GitHub app. Validate it. | ||
| if src.AccessToken == "" { |
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.
At that point, we know that src.AccessToken != "" xor src.GitHubApp != github.GitHubApp{} is true so I agree with Marco that doing the corresponding direct test would be better than the contrapositive.
| // if access token is not set; we are using GitHub app. Validate it. | |
| if src.AccessToken == "" { | |
| // if access token is not set; we are using GitHub app. Validate it. | |
| if src.GitHubApp != github.GitHubApp{} { |
| key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(app.PrivateKey)) | ||
| if err != nil { | ||
| return "", fmt.Errorf("could not parse private key: %w", err) | ||
| return fmt.Errorf("github_app: could not parse private key: %w", err) |
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 the error you get from jwt tell you it should be in PEM format? If not, that would be worthwhile to mention 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.
This would be the full error (see the test below):
github_app: could not parse private key: invalid key: Key must be a PEM encoded PKCS1 or PKCS8 ke
f5f010f to
ce472c4
Compare
validate github app
This is an attempt to address: #162 (comment)
and parse the private pem key during validation period.
@marco-m-pix4d attempt to implement what we were discussing..