Skip to content

Commit

Permalink
#76: Draft implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
skapral committed Jul 11, 2018
1 parent fc462e0 commit a0661f6
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 32 deletions.
3 changes: 2 additions & 1 deletion DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ $ GITHUB_AUTH_TOKEN=<token> GITLAB_AUTH_TOKEN=<token> java -jar puzzler-app-*.ja
| GITHUB_AUTH_TOKEN | True * | Valid Github API authentication token, which has at least `repo` permissions. |
| GITLAB_AUTH_TOKEN | True ** | Valid Gitlab personal access token, which has at least `api` permissions. |
| PORT | False | Port number for HTTP endpoints (5000 by default) |
| GITHUB_HOOK_SECRET | False | Github hook secret. If provided, each Github event payload is validated using this secret. |
| GITHUB_HOOK_SECRET | False | Github hook expectedToken. If provided, each Github event payload is validated using this expectedToken. |
| GITLAB_HOOK_SECRET | False | Gitlab hook expectedToken. If provided, each Gitlab event payload is validated using this expectedToken. |

`*` - Mandatory for using the `@puzzlebot`'s `/github` webhook

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
*
* @author Kapralov Sergey
*/
public class OpValidatingEventSignature implements Operation {
public class OpValidatingGithubEventSignature implements Operation {
private final ConfigProperty secret;
private final String eventBody;
private final String eventSignature;
Expand All @@ -56,7 +56,7 @@ public class OpValidatingEventSignature implements Operation {
* @param delegate Operation to delegate to, if signatures match.
* @param exceptionFn Constructor of exception to throw if signatures mismatch.
*/
public OpValidatingEventSignature(ConfigProperty secret, String eventBody, String eventSignature, Operation delegate, Function<String, RuntimeException> exceptionFn) {
public OpValidatingGithubEventSignature(ConfigProperty secret, String eventBody, String eventSignature, Operation delegate, Function<String, RuntimeException> exceptionFn) {
this.secret = secret;
this.eventBody = eventBody;
this.eventSignature = eventSignature;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ class OpIgnoringUnprivildgedEventSenderTest extends TestsSuite {

static {
try {
OWNER_IS_SENDER = IOUtils.toString(OpValidatingEventSignature.class.getResource("ownerIsSender"));
OWNER_IS_NOT_SENDER = IOUtils.toString(OpValidatingEventSignature.class.getResource("ownerIsNotSender"));
OWNER_IS_SENDER = IOUtils.toString(OpValidatingGithubEventSignature.class.getResource("ownerIsSender"));
OWNER_IS_NOT_SENDER = IOUtils.toString(OpValidatingGithubEventSignature.class.getResource("ownerIsNotSender"));
} catch(Exception ex) {
throw new RuntimeException(ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,23 @@
import com.pragmaticobjects.oo.tests.junit5.TestsSuite;
import org.apache.commons.io.IOUtils;

class OpValidatingEventSignatureTest extends TestsSuite {
class OpValidatingGithubEventSignatureTest extends TestsSuite {
private static final String SIGNED_BODY;

static {
try {
SIGNED_BODY = IOUtils.toString(OpValidatingEventSignature.class.getResource("signedBody"));
SIGNED_BODY = IOUtils.toString(OpValidatingGithubEventSignature.class.getResource("signedBody"));
} catch(Exception ex) {
throw new RuntimeException(ex);
}
}

public OpValidatingEventSignatureTest() {
public OpValidatingGithubEventSignatureTest() {
super(
new TestCase(
"operation passes if signatures match",
new AssertOperationSuccessful(
new OpValidatingEventSignature(
new OpValidatingGithubEventSignature(
new CpStatic("secret"),
SIGNED_BODY,
"sha1=e7e81bca3c1c67910123611b1d94bfe78760b9dd",
Expand All @@ -62,7 +62,7 @@ public OpValidatingEventSignatureTest() {
new TestCase(
"operation fails if signatures mismatch",
new AssertOperationFails(
new OpValidatingEventSignature(
new OpValidatingGithubEventSignature(
new CpStatic("secret"),
SIGNED_BODY,
"sha1=somefakesignature00000000000000000000000",
Expand All @@ -74,7 +74,7 @@ public OpValidatingEventSignatureTest() {
new TestCase(
"signatures are not validated if secret is not provided",
new AssertOperationSuccessful(
new OpValidatingEventSignature(
new OpValidatingGithubEventSignature(
new CpMissingValue(),
SIGNED_BODY,
"sha1=somefakesignature00000000000000000000000",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* MIT License
*
* Copyright (c) %today.year Kapralov Sergey
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/

package com.github.skapral.puzzler.gitlab.config;

import com.github.skapral.puzzler.core.config.CpEnvironment;

/**
* GITLAB_HOOK_SECRET environment variable's value
*
* @author Kapralov Sergey
*/
public class Cp_GITLAB_HOOK_SECRET extends CpEnvironment {
/**
* Ctor.
*/
public Cp_GITLAB_HOOK_SECRET() {
super("GITLAB_HOOK_SECRET");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* MIT License
*
* Copyright (c) %today.year Kapralov Sergey
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/

package com.github.skapral.puzzler.gitlab.operation;

import com.github.skapral.puzzler.core.Operation;
import com.github.skapral.puzzler.core.config.ConfigProperty;

import java.util.function.Function;

/**
* Operation which is executed only if GitLab event token
* match on GitLab event. See <a href="https://docs.gitlab.com/ee/user/project/integrations/webhooks.html#secret-token"></a>
* for details.
*
* @author Kapralov Sergey
*/
public class OpValidatingGitlabEventToken implements Operation {
private final ConfigProperty expectedToken;
private final String eventToken;
private final Operation delegate;
private final Function<String, RuntimeException> exceptionFn;

/**
* Ctor.
*
* @param expectedToken Secret. If the property value is not provided, validation will be skept.
* @param eventToken Event token (X-Gitlab-Token)
* @param delegate Operation to delegate to, if signatures match.
* @param exceptionFn Constructor of exception to throw if signatures mismatch.
*/
public OpValidatingGitlabEventToken(ConfigProperty expectedToken, String eventToken, Operation delegate, Function<String, RuntimeException> exceptionFn) {
this.expectedToken = expectedToken;
this.eventToken = eventToken;
this.delegate = delegate;
this.exceptionFn = exceptionFn;
}

@Override
public final void execute() {
if(expectedToken.optionalValue().isEmpty()) {
delegate.execute();
} else {
if (expectedToken.optionalValue().get().equals(eventToken)) {
delegate.execute();
} else {
throw exceptionFn.apply("Token mismatch");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* MIT License
*
* Copyright (c) %today.year Kapralov Sergey
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/

package com.github.skapral.puzzler.gitlab.operation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* MIT License
*
* Copyright (c) %today.year Kapralov Sergey
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/

package com.github.skapral.puzzler.gitlab.operation;


import com.github.skapral.puzzler.core.config.CpStatic;
import com.github.skapral.puzzler.core.operation.AssertOperationFails;
import com.github.skapral.puzzler.core.operation.AssertOperationSuccessful;
import com.github.skapral.puzzler.core.operation.OpDoNothing;
import com.pragmaticobjects.oo.tests.TestCase;
import com.pragmaticobjects.oo.tests.junit5.TestsSuite;

class OpValidatingGitlabEventTokenTest extends TestsSuite {
public OpValidatingGitlabEventTokenTest() {
super(
new TestCase(
"Positive case",
new AssertOperationSuccessful(
new OpValidatingGitlabEventToken(
new CpStatic("qwerty"),
"qwerty",
new OpDoNothing(),
RuntimeException::new
)
)
),
new TestCase(
"Negative case",
new AssertOperationFails(
new OpValidatingGitlabEventToken(
new CpStatic("qwerty"),
"asdfgh",
new OpDoNothing(),
RuntimeException::new
)
)
)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import com.github.skapral.puzzler.github.itracker.ItGithubIssues;
import com.github.skapral.puzzler.github.location.GhapiProduction;
import com.github.skapral.puzzler.github.operation.OpIgnoringUnprivildgedEventSender;
import com.github.skapral.puzzler.github.operation.OpValidatingEventSignature;
import com.github.skapral.puzzler.github.operation.OpValidatingGithubEventSignature;
import com.github.skapral.puzzler.github.project.GprjFromGithubEvent;
import com.github.skapral.puzzler.github.source.PsrcFromGithubEvent;
import com.pragmaticobjects.oo.atom.anno.NotAtom;
Expand Down Expand Up @@ -63,7 +63,7 @@ public class GithubHookEndpoint {
@Consumes("application/json")
@Produces("application/json")
public final Response githubHook(@HeaderParam("X-GitHub-Event") final String eventType, @HeaderParam("X-Hub-Signature") final String eventSignature, final String event) throws Exception {
new OpValidatingEventSignature(
new OpValidatingGithubEventSignature(
new Cp_GITHUB_HOOK_SECRET(),
event,
Objects.isNull(eventSignature) ? "" : eventSignature,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@

import com.github.skapral.puzzler.core.operation.OpPersistAllPuzzles;
import com.github.skapral.puzzler.gitlab.config.Cp_GITLAB_AUTH_TOKEN;
import com.github.skapral.puzzler.gitlab.config.Cp_GITLAB_HOOK_SECRET;
import com.github.skapral.puzzler.gitlab.itracker.ItGitlabIssues;
import com.github.skapral.puzzler.gitlab.location.GlapiProduction;
import com.github.skapral.puzzler.gitlab.operation.OpValidatingGitlabEventToken;
import com.github.skapral.puzzler.gitlab.project.GprjFromGitlabEvent;
import com.github.skapral.puzzler.gitlab.source.PsrcFromGitlabEvent;
import com.pragmaticobjects.oo.atom.anno.NotAtom;
Expand All @@ -46,34 +48,36 @@ public class GitlabHookEndpoint {
/**
* Gitlab hook endpoint
* @param eventType Event type, obtained from X-Gitlab-Event header.
* @param eventSignature Event signature from X-Gitlab-Token header.
* @param eventToken Event token from X-Gitlab-Token header.
* @param event Event body in JSON format
* @return HTTP response.
* @throws Exception If something went wrong.
*/
@POST
@Consumes("application/json")
@Produces("application/json")
public final Response githubHook(@HeaderParam("X-Gitlab-Event") final String eventType, @HeaderParam("X-Gitlab-Token") final String eventSignature, final String event) throws Exception {
System.out.println("X-Gitlab-Event = " + eventType);
System.out.println("X-Gitlab-Token = " + eventSignature);
System.out.println("Body = " + event);
new OpPersistAllPuzzles(
new PsrcFromGitlabEvent(
new GlapiProduction(
new Cp_GITLAB_AUTH_TOKEN()
),
eventType,
event
),
new ItGitlabIssues(
new GlapiProduction(
new Cp_GITLAB_AUTH_TOKEN()
),
new GprjFromGitlabEvent(
public final Response githubHook(@HeaderParam("X-Gitlab-Event") final String eventType, @HeaderParam("X-Gitlab-Token") final String eventToken, final String event) throws Exception {
new OpValidatingGitlabEventToken(
new Cp_GITLAB_HOOK_SECRET(),
eventToken,
new OpPersistAllPuzzles(
new PsrcFromGitlabEvent(
new GlapiProduction(
new Cp_GITLAB_AUTH_TOKEN()
),
eventType,
event
),
new ItGitlabIssues(
new GlapiProduction(
new Cp_GITLAB_AUTH_TOKEN()
),
new GprjFromGitlabEvent(
event
)
)
)
),
AuthenticationExceptionMapper.AuthenticationException::new
).execute();
return Response.ok("{}").build();
}
Expand Down

0 comments on commit a0661f6

Please sign in to comment.