Skip to content

Commit e3f8199

Browse files
authored
Merge branch 'master' into ib/bump-vite
2 parents 587cede + ea7ea43 commit e3f8199

File tree

6 files changed

+236
-48
lines changed

6 files changed

+236
-48
lines changed

it/server/src/test/java/com/walmartlabs/concord/it/server/AbstractGitHubTriggersIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ protected int waitForProcessesToFinish() throws Exception {
155155
}
156156
}
157157

158-
protected void expectNoProceses(String orgName, String projectName, OffsetDateTime afterCreatedAt) throws Exception {
158+
protected void expectNoProcesses(String orgName, String projectName, OffsetDateTime afterCreatedAt) throws Exception {
159159
ProcessV2Api processApi = new ProcessV2Api(getApiClient());
160160
ProcessListFilter filter = ProcessListFilter.builder()
161161
.orgName(orgName)

it/server/src/test/java/com/walmartlabs/concord/it/server/GitHubTriggersV2IT.java

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
*/
2222

2323
import com.walmartlabs.concord.client2.*;
24-
import com.walmartlabs.concord.client2.ProcessListFilter;
2524
import org.junit.jupiter.api.AfterEach;
2625
import org.junit.jupiter.api.Test;
2726

@@ -43,21 +42,9 @@ public void tearDown() throws Exception {
4342
}
4443

4544
/**
46-
* Test subscription to unknown repositories only:
47-
* <pre>
48-
* # project A
49-
* # a default onPush trigger for the default branch
50-
* triggers:
51-
* - github:
52-
* entryPoint: onPush
53-
*
54-
* # project G
55-
* # accepts only specific commit authors
56-
* triggers:
57-
* - github:
58-
* author: ".*xyz.*"
59-
* entryPoint: onPush
60-
* </pre>
45+
* Checks if "conditions->sender" works.
46+
* Tests filtering by "sender" in events that originate from "known"
47+
* and "unknown" (not registered in any Concord project) GitHub repositories.
6148
*/
6249
@Test
6350
public void testFilterBySender() throws Exception {
@@ -66,47 +53,41 @@ public void testFilterBySender() throws Exception {
6653
String orgXName = "orgX_" + randomString();
6754
orgApi.createOrUpdateOrg(new OrganizationEntry().name(orgXName));
6855

69-
Path repo = initRepo("githubTests/repos/v2/defaultTrigger");
70-
String branch = "branch_" + randomString();
71-
createNewBranch(repo, branch, "githubTests/repos/v2/defaultTriggerWithSender");
72-
73-
// Project A
74-
// master branch + a default trigger
56+
// project A uses a default trigger without conditions
57+
// should trigger on any event for the matching "known" repository
58+
// i.e. the event must come from a GitHub URL that is added to a Concord project
7559
String projectAName = "projectA_" + randomString();
7660
String repoAName = "repoA_" + randomString();
7761
Path projectARepo = initProjectAndRepo(orgXName, projectAName, repoAName, null, initRepo("githubTests/repos/v2/defaultTrigger"));
7862
refreshRepo(orgXName, projectAName, repoAName);
7963

80-
// Project G
81-
// accepts only specific commit authors
64+
// project G accepts only specific event senders but from any repository
65+
// (including events from GitHub URLs that are not registered in any Concord project)
8266
String projectGName = "projectG_" + randomString();
8367
String repoGName = "repoG_" + randomString();
84-
Path projectBRepo = initProjectAndRepo(orgXName, projectGName, repoGName, null, initRepo("githubTests/repos/v2/defaultTriggerWithSender"));
68+
initProjectAndRepo(orgXName, projectGName, repoGName, null, initRepo("githubTests/repos/v2/anyRepoWithSender"));
8569
refreshRepo(orgXName, projectGName, repoGName);
8670

87-
// ---
88-
71+
// send an a projectARepo event from a random sender
8972
sendEvent("githubTests/events/direct_branch_push.json", "push",
9073
"_FULL_REPO_NAME", toRepoName(projectARepo),
9174
"_REF", "refs/heads/master",
92-
"_USER_NAME", "aknowndude",
75+
"_USER_NAME", "arandomdude" + randomString(),
9376
"_USER_LDAP_DN", "");
9477

9578
// A's triggers should be activated
96-
ProcessEntry procA = waitForAProcess(orgXName, projectAName, "github");
97-
expectNoProceses(orgXName, projectGName, null);
98-
99-
// ---
79+
waitForAProcess(orgXName, projectAName, "github");
80+
// G's triggers should NOT be activated, the event is not from not the right sender
81+
expectNoProcesses(orgXName, projectGName, null);
10082

10183
// see https://github.com/walmartlabs/concord/issues/435
10284
// wait a bit to reliably filter out subsequent processes of projectA
10385
Thread.sleep(1000);
10486
OffsetDateTime now = OffsetDateTime.now().truncatedTo(ChronoUnit.SECONDS);
10587

106-
// ---
107-
88+
// send an a projectARepo event (again), this time from the user expected by the G trigger
10889
sendEvent("githubTests/events/direct_branch_push.json", "push",
109-
"_FULL_REPO_NAME", toRepoName(projectBRepo),
90+
"_FULL_REPO_NAME", "org" + randomString() + "/" + "repo" + randomString(), // a random ("unknown") GitHub repository
11091
"_REF", "refs/heads/master",
11192
"_USER_NAME", "somecooldude",
11293
"_USER_LDAP_DN", "");
@@ -115,9 +96,9 @@ public void testFilterBySender() throws Exception {
11596
waitForAProcess(orgXName, projectGName, "github");
11697

11798
// no A's are expected
118-
expectNoProceses(orgXName, projectAName, now);
99+
expectNoProcesses(orgXName, projectAName, now);
119100

120-
// ---
101+
// clean up
121102

122103
deleteOrg(orgXName);
123104
}

it/server/src/test/java/com/walmartlabs/concord/it/server/LdapIT.java

Lines changed: 193 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,32 @@
1919
* =====
2020
*/
2121

22+
import com.fasterxml.jackson.databind.ObjectMapper;
2223
import com.walmartlabs.concord.client2.*;
2324
import org.junit.jupiter.api.BeforeAll;
2425
import org.junit.jupiter.api.Test;
2526

2627
import javax.naming.Context;
2728
import javax.naming.NameAlreadyBoundException;
2829
import javax.naming.directory.*;
30+
import java.io.InputStream;
31+
import java.net.URI;
32+
import java.net.http.HttpClient;
33+
import java.net.http.HttpRequest;
34+
import java.net.http.HttpResponse;
35+
import java.util.Base64;
36+
import java.util.Map;
2937
import java.util.Properties;
38+
import java.util.UUID;
3039

3140
import static com.walmartlabs.concord.it.common.ITUtils.archive;
3241
import static com.walmartlabs.concord.it.common.ServerClient.assertLog;
3342
import static com.walmartlabs.concord.it.common.ServerClient.waitForCompletion;
43+
import static com.walmartlabs.concord.it.common.ServerClient.waitForStatus;
3444
import static org.junit.jupiter.api.Assertions.assertEquals;
3545
import static org.junit.jupiter.api.Assertions.assertFalse;
3646
import static org.junit.jupiter.api.Assertions.assertNotNull;
47+
import static org.junit.jupiter.api.Assertions.assertThrows;
3748
import static org.junit.jupiter.api.Assertions.assertTrue;
3849
import static org.junit.jupiter.api.Assumptions.assumeTrue;
3950

@@ -44,6 +55,7 @@ public class LdapIT extends AbstractServerIT {
4455
private static final String GROUP_OU = "ou=groups,dc=example,dc=org";
4556
private static final String USER_OU = "ou=users,dc=example,dc=org";
4657
private static DirContext ldapCtx;
58+
private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder().build();
4759

4860
@BeforeAll
4961
public static void createLdapStructure() throws Exception {
@@ -92,7 +104,6 @@ public void testLdapUserGroups() throws Exception {
92104
byte[] ab = getLog(pir.getInstanceId());
93105
String groupDn = "cn=" + groupName + "," + GROUP_OU;
94106
assertLog(".*" + groupDn + ".*", ab);
95-
96107
}
97108

98109
@Test
@@ -136,6 +147,140 @@ void testDisableLdapUser() throws Exception {
136147
assertTrue(ue.getPermanentlyDisabled());
137148
}
138149

150+
@Test
151+
void testSubmitFormRunAsGroupWithApiKey() throws Exception {
152+
// create users in ldap
153+
String noGroupUser = "noGroupUser" + randomString();
154+
createLdapUser(noGroupUser);
155+
156+
String username = "runAsUser" + randomString();
157+
createLdapUser(username);
158+
159+
// create group
160+
String groupName = "RunAsGroup" + randomString();
161+
createLdapGroupWithUser(groupName, username);
162+
163+
UsersApi usersApi = new UsersApi(getApiClient());
164+
usersApi.createOrUpdateUser(new CreateUserRequest()
165+
.username(username)
166+
.type(CreateUserRequest.TypeEnum.LDAP));
167+
usersApi.createOrUpdateUser(new CreateUserRequest()
168+
.username(noGroupUser)
169+
.type(CreateUserRequest.TypeEnum.LDAP));
170+
171+
String noGroupApiKey = createApiKey(noGroupUser);
172+
String validUserApiKey = createApiKey(username);
173+
174+
setApiKey(validUserApiKey);
175+
176+
// --- execute form
177+
178+
byte[] payload = archive(LdapIT.class.getResource("ldapFormRunAs").toURI());
179+
StartProcessResponse spr = start(Map.of(
180+
"archive", payload,
181+
"arguments.ldapGroupName", groupName
182+
));
183+
assertNotNull(spr.getInstanceId());
184+
185+
186+
// ---
187+
188+
ProcessEntry pir = waitForStatus(getApiClient(), spr.getInstanceId(), ProcessEntry.StatusEnum.SUSPENDED);
189+
190+
// --- try to get with user not in group (expect no permission)
191+
192+
ApiException noGroupEx = assertThrows(ApiException.class, () ->
193+
new ProcessFormsApi(getApiClientForKey(noGroupApiKey)).getProcessForm(pir.getInstanceId(), "myForm"));
194+
195+
assertEquals(403, noGroupEx.getCode());
196+
assertTrue(noGroupEx.getMessage().contains("doesn't have the necessary permissions to resume process. Expected LDAP group(s) '[CN=RunAsGroup"));
197+
198+
// --- get form with user in expected ldap group
199+
200+
ProcessFormsApi formsApi = new ProcessFormsApi(getApiClientForKey(validUserApiKey));
201+
202+
FormInstanceEntry form = formsApi.getProcessForm(pir.getInstanceId(), "myForm");
203+
204+
assertEquals("myForm", form.getName());
205+
assertEquals(1, form.getFields().size());
206+
assertEquals("inputName", form.getFields().get(0).getName());
207+
assertEquals("string", form.getFields().get(0).getType());
208+
209+
// --- submit form with user in expected ldap group
210+
211+
formsApi.submitForm(pir.getInstanceId(), "myForm", Map.of("inputName", "testuser"));
212+
213+
waitForStatus(getApiClient(), spr.getInstanceId(), ProcessEntry.StatusEnum.FINISHED);
214+
215+
byte[] ab = getLog(pir.getInstanceId());
216+
assertLog(".*Submitted name: testuser.*", ab);
217+
}
218+
219+
@Test
220+
void testSubmitFormRunAsGroupWithPassword() throws Exception {
221+
// create users in ldap
222+
String noGroupUser = "noGroupUser" + randomString();
223+
createLdapUser(noGroupUser);
224+
225+
String username = "runAsUser" + randomString();
226+
createLdapUser(username);
227+
228+
// create group
229+
String groupName = "RunAsGroup" + randomString();
230+
createLdapGroupWithUser(groupName, username);
231+
232+
UsersApi usersApi = new UsersApi(getApiClient());
233+
usersApi.createOrUpdateUser(new CreateUserRequest()
234+
.username(username)
235+
.type(CreateUserRequest.TypeEnum.LDAP));
236+
usersApi.createOrUpdateUser(new CreateUserRequest()
237+
.username(noGroupUser)
238+
.type(CreateUserRequest.TypeEnum.LDAP));
239+
240+
String validUserApiKey = createApiKey(username);
241+
242+
setApiKey(validUserApiKey);
243+
244+
// --- execute form
245+
246+
byte[] payload = archive(LdapIT.class.getResource("ldapFormRunAs").toURI());
247+
StartProcessResponse spr = start(Map.of(
248+
"archive", payload,
249+
"arguments.ldapGroupName", groupName
250+
));
251+
assertNotNull(spr.getInstanceId());
252+
253+
// ---
254+
255+
ProcessEntry pir = waitForStatus(getApiClient(), spr.getInstanceId(), ProcessEntry.StatusEnum.SUSPENDED);
256+
257+
// --- try to get with user not in group (expect no permission)
258+
259+
ApiException noGroupEx = assertThrows(ApiException.class, () ->
260+
getFormHttpClient(getApiClient().getBaseUrl(), pir.getInstanceId(), "myForm", noGroupUser, noGroupUser));
261+
262+
assertEquals(403, noGroupEx.getCode());
263+
assertTrue(noGroupEx.getResponseBody().contains("doesn't have the necessary permissions to resume process. Expected LDAP group(s) '[CN=RunAsGroup"));
264+
265+
// --- get form with user in expected ldap group
266+
267+
FormInstanceEntry form = getFormHttpClient(getApiClient().getBaseUrl(), pir.getInstanceId(), "myForm", username, username);
268+
269+
assertEquals("myForm", form.getName());
270+
assertEquals(1, form.getFields().size());
271+
assertEquals("inputName", form.getFields().get(0).getName());
272+
assertEquals("string", form.getFields().get(0).getType());
273+
274+
// --- submit form with user in expected ldap group
275+
276+
submitFormHttpClient(getApiClient().getBaseUrl(), pir.getInstanceId(), "myForm", Map.of("inputName", "testuser"), username, username);
277+
278+
waitForStatus(getApiClient(), spr.getInstanceId(), ProcessEntry.StatusEnum.FINISHED);
279+
280+
byte[] ab = getLog(pir.getInstanceId());
281+
assertLog(".*Submitted name: testuser.*", ab);
282+
}
283+
139284
public static DirContext createContext() throws Exception {
140285
String url = System.getenv("IT_LDAP_URL");
141286
String connectionType = "simple";
@@ -176,6 +321,7 @@ private static void createLdapUser(String username) throws Exception {
176321
Attribute uid = new BasicAttribute("uid", username);
177322
Attribute cn = new BasicAttribute(COMMON_NAME, username);
178323
Attribute sn = new BasicAttribute("sn", username);
324+
Attribute userPassword = new BasicAttribute("userPassword", username);
179325

180326
Attribute objectClass = new BasicAttribute(OBJECT_CLASS);
181327
objectClass.add("top");
@@ -186,6 +332,7 @@ private static void createLdapUser(String username) throws Exception {
186332
attributes.put(uid);
187333
attributes.put(cn);
188334
attributes.put(sn);
335+
attributes.put(userPassword);
189336
attributes.put(objectClass);
190337

191338
try {
@@ -216,4 +363,49 @@ private static void createLdapGroupWithUser(String groupName, String username) t
216363
// already exists, ignore
217364
}
218365
}
366+
367+
private String createApiKey(String username) throws Exception {
368+
ApiKeysApi apiKeyResource = new ApiKeysApi(getApiClient());
369+
CreateApiKeyResponse cakr = apiKeyResource.createUserApiKey(new CreateApiKeyRequest()
370+
.username(username)
371+
.userType(CreateApiKeyRequest.UserTypeEnum.LDAP));
372+
373+
return cakr.getKey();
374+
}
375+
376+
private FormInstanceEntry getFormHttpClient(String baseUrl, UUID instanceId, String formName, String username, String password) throws Exception {
377+
HttpRequest req = HttpRequest.newBuilder(URI.create(baseUrl + "/api/v1/process/" + instanceId + "/form/" + formName))
378+
.GET()
379+
.header("Authorization", "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes()))
380+
.build();
381+
382+
HttpResponse<InputStream> resp = HTTP_CLIENT.send(req, HttpResponse.BodyHandlers.ofInputStream());
383+
384+
try (InputStream is = resp.body()) {
385+
if (resp.statusCode() != 200) {
386+
throw new ApiException(resp.statusCode(), resp.headers(), new String(is.readAllBytes()));
387+
}
388+
389+
return new ObjectMapper().readValue(resp.body(), FormInstanceEntry.class);
390+
}
391+
}
392+
393+
private void submitFormHttpClient(String baseUrl, UUID instanceId, String formName, Map<String, Object> data, String username, String password) throws Exception {
394+
ObjectMapper mapper = new ObjectMapper();
395+
String requestBody = mapper.writeValueAsString(data);
396+
397+
HttpRequest req = HttpRequest.newBuilder(URI.create(baseUrl + "/api/v1/process/" + instanceId + "/form/" + formName))
398+
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
399+
.header("Content-Type", "application/json")
400+
.header("Authorization", "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes()))
401+
.build();
402+
403+
HttpResponse<InputStream> resp = HTTP_CLIENT.send(req, HttpResponse.BodyHandlers.ofInputStream());
404+
405+
try (InputStream is = resp.body()) {
406+
if (resp.statusCode() != 200) {
407+
throw new ApiException(resp.statusCode(), resp.headers(), new String(resp.body().readAllBytes()));
408+
}
409+
}
410+
}
219411
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ triggers:
77
entryPoint: "onPush"
88
version: 2
99
conditions:
10+
githubOrg: ".*"
11+
githubRepo: ".*"
12+
type: ".*"
1013
sender: ".*some.*dude.*"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
flows:
2+
default:
3+
- form: myForm
4+
fields:
5+
- inputName: { label: "name", type: "string" }
6+
runAs:
7+
ldap:
8+
- group: "CN=${ldapGroupName},.*"
9+
- log: "Submitted name: ${myForm.inputName}"

0 commit comments

Comments
 (0)