Skip to content

Commit 8b3855d

Browse files
authored
Merge pull request #2864 from objectcomputing/bugfix-2814/fixed-internal-text-key
Bugfix 2814/fixed internal text key
2 parents 3552705 + a73d878 commit 8b3855d

File tree

5 files changed

+113
-29
lines changed

5 files changed

+113
-29
lines changed

server/src/main/java/com/objectcomputing/checkins/services/pulseresponse/PulseResponseController.java

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@
2525
import jakarta.validation.Valid;
2626
import jakarta.validation.constraints.NotNull;
2727

28-
import org.slf4j.Logger;
29-
import org.slf4j.LoggerFactory;
30-
3128
import java.net.URI;
3229
import java.time.LocalDate;
3330
import java.util.Set;
@@ -39,8 +36,6 @@
3936
@ExecuteOn(TaskExecutors.BLOCKING)
4037
@Tag(name = "pulse-responses")
4138
public class PulseResponseController {
42-
private static final Logger LOG = LoggerFactory.getLogger(PulseResponseController.class);
43-
4439
private final PulseResponseService pulseResponseServices;
4540
private final MemberProfileServices memberProfileServices;
4641
private final SlackSignatureVerifier slackSignatureVerifier;
@@ -153,15 +148,9 @@ public HttpResponse externalPulseResponse(
153148
@Header("X-Slack-Request-Timestamp") String timestamp,
154149
@Body String requestBody,
155150
HttpRequest<?> request) {
156-
// DEBUG Only
157-
LOG.info(requestBody);
158-
159151
// Validate the request
160152
if (slackSignatureVerifier.verifyRequest(signature,
161153
timestamp, requestBody)) {
162-
// DEBUG Only
163-
LOG.info("Request has been verified");
164-
165154
// Convert the request body to a map of values.
166155
FormUrlEncodedDecoder formUrlEncodedDecoder =
167156
new FormUrlEncodedDecoder();
@@ -174,16 +163,14 @@ public HttpResponse externalPulseResponse(
174163
PulseResponseCreateDTO pulseResponseDTO =
175164
slackPulseResponseConverter.get(memberProfileServices,
176165
(String)body.get(key));
166+
177167
// If we receive a null DTO, that means that this is not the
178168
// actual submission of the form. We can just return 200 so
179169
// that Slack knows to continue without error.
180170
if (pulseResponseDTO == null) {
181171
return HttpResponse.ok();
182172
}
183173

184-
// DEBUG Only
185-
LOG.info("Request has been converted");
186-
187174
// Create the pulse response
188175
PulseResponse pulseResponse =
189176
pulseResponseServices.unsecureSave(

server/src/main/java/com/objectcomputing/checkins/services/pulseresponse/SlackPulseResponseConverter.java

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ public PulseResponseCreateDTO get(
4545
final Map<String, Object> values =
4646
(Map<String, Object>)state.get("values");
4747

48-
dumpMap(values, "");
49-
5048
// Create the pulse DTO and fill in the values.
5149
PulseResponseCreateDTO response = new PulseResponseCreateDTO();
5250
response.setTeamMemberId(lookupUser(memberProfileServices, map));
@@ -59,7 +57,7 @@ public PulseResponseCreateDTO get(
5957
internalBlock, "internalScore", "selected_option", true)));
6058
// Internal Feelings
6159
response.setInternalFeelings(getMappedValue(
62-
values, "internaltext", "internalFeelings", false));
60+
values, "internalText", "internalFeelings", false));
6361

6462
// External Score
6563
Map<String, Object> externalBlock =
@@ -126,15 +124,4 @@ private UUID lookupUser(MemberProfileServices memberProfileServices,
126124
MemberProfile member = memberProfileServices.findByWorkEmail(email);
127125
return member.getId();
128126
}
129-
130-
// DEBUG Only
131-
private void dumpMap(Map<?, ?> map, String indent) {
132-
for (Map.Entry<?, ?> entry : map.entrySet()) {
133-
LOG.info(indent + entry.getKey() + " : " + entry.getValue());
134-
135-
if (entry.getValue() instanceof Map) {
136-
dumpMap((Map<?, ?>) entry.getValue(), indent + " ");
137-
}
138-
}
139-
}
140127
}

server/src/main/resources/db/dev/R__Load_testing_data.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ VALUES
6060
INSERT INTO member_profile -- Unreal Ulysses
6161
(id, firstName, lastName, title, pdlid, location, workEmail, employeeid, startdate, biotext, supervisorid, birthDate, last_seen)
6262
VALUES
63-
('dfe2f986-fac0-11eb-9a03-0242ac130003', PGP_SYM_ENCRYPT('Unreal','${aeskey}'), PGP_SYM_ENCRYPT('Ulysses','${aeskey}'), PGP_SYM_ENCRYPT('Test Engineer 2','${aeskey}'), '1c813446-c65a-4f49-b980-0193f7bfff8c', PGP_SYM_ENCRYPT('St. Louis','${aeskey}'), PGP_SYM_ENCRYPT('[email protected]','${aeskey}'), '010101012', '2021-05-22', PGP_SYM_ENCRYPT('Test user 2','${aeskey}'), 'e4b2fe52-1915-4544-83c5-21b8f871f6db', '1950-02-01', '2021-05-22');
63+
('dfe2f986-fac0-11eb-9a03-0242ac130003', PGP_SYM_ENCRYPT('Unreal','${aeskey}'), PGP_SYM_ENCRYPT('Ulysses','${aeskey}'), PGP_SYM_ENCRYPT('Test Engineer 2','${aeskey}'), '1c813446-c65a-4f49-b980-0193f7bfff8c', PGP_SYM_ENCRYPT('St. Louis','${aeskey}'), PGP_SYM_ENCRYPT('[email protected]','${aeskey}'), '010101012', '2021-05-22', PGP_SYM_ENCRYPT('Test user 2','${aeskey}'), 'e4b2fe52-1915-4544-83c5-21b8f871f6db', '1950-01-01', '2021-05-22');
6464

6565
INSERT INTO member_profile -- Kazuhira Miller
6666
(id, firstName, lastName, title, pdlid, location, workEmail, employeeid, startdate, biotext, supervisorid, birthDate, last_seen)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.objectcomputing.checkins.services;
2+
3+
import com.objectcomputing.checkins.notifications.social_media.SlackSearch;
4+
import com.objectcomputing.checkins.configuration.CheckInsConfiguration;
5+
6+
import io.micronaut.context.annotation.Replaces;
7+
import io.micronaut.context.annotation.Requires;
8+
import io.micronaut.core.util.StringUtils;
9+
import io.micronaut.http.HttpResponse;
10+
import io.micronaut.http.HttpStatus;
11+
12+
import jakarta.inject.Singleton;
13+
14+
import java.util.HashMap;
15+
import java.util.Map;
16+
17+
@Singleton
18+
@Replaces(SlackSearch.class)
19+
@Requires(property = "replace.slacksearch", value = StringUtils.TRUE)
20+
public class SlackSearchReplacement extends SlackSearch {
21+
public final Map<String, String> channels = new HashMap<>();
22+
public final Map<String, String> users = new HashMap<>();
23+
24+
public SlackSearchReplacement(CheckInsConfiguration checkInsConfiguration) {
25+
super(checkInsConfiguration);
26+
}
27+
28+
@Override
29+
public String findChannelId(String channelName) {
30+
return channels.containsKey(channelName) ?
31+
channels.get(channelName) : null;
32+
}
33+
34+
@Override
35+
public String findUserEmail(String userId) {
36+
return users.containsKey(userId) ? users.get(userId) : null;
37+
}
38+
39+
@Override
40+
public String findUserId(String userEmail) {
41+
for (Map.Entry<String, String> entry : users.entrySet()) {
42+
if (entry.getValue().equals(userEmail)) {
43+
return entry.getKey();
44+
}
45+
}
46+
return null;
47+
}
48+
}
49+

server/src/test/java/com/objectcomputing/checkins/services/pulseresponse/PulseResponseControllerTest.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
import com.objectcomputing.checkins.services.fixture.RoleFixture;
88
import com.objectcomputing.checkins.services.memberprofile.MemberProfile;
99
import com.objectcomputing.checkins.util.Util;
10+
import com.objectcomputing.checkins.configuration.CheckInsConfiguration;
11+
import com.objectcomputing.checkins.services.SlackSearchReplacement;
12+
13+
import io.micronaut.core.util.StringUtils;
1014
import io.micronaut.core.type.Argument;
15+
import io.micronaut.context.annotation.Property;
1116
import io.micronaut.http.HttpRequest;
1217
import io.micronaut.http.HttpResponse;
1318
import io.micronaut.http.HttpStatus;
@@ -27,19 +32,31 @@
2732
import java.util.UUID;
2833
import java.util.stream.Collectors;
2934
import java.util.stream.Stream;
35+
import javax.crypto.Mac;
36+
import javax.crypto.spec.SecretKeySpec;
37+
import java.nio.charset.StandardCharsets;
38+
import java.util.Base64;
39+
import java.time.Instant;
3040

3141
import static com.objectcomputing.checkins.services.role.RoleType.Constants.ADMIN_ROLE;
3242
import static com.objectcomputing.checkins.services.role.RoleType.Constants.MEMBER_ROLE;
3343
import static org.junit.jupiter.api.Assertions.assertEquals;
3444
import static org.junit.jupiter.api.Assertions.assertThrows;
3545
import static org.junit.jupiter.api.Assertions.assertTrue;
3646

47+
@Property(name = "replace.slacksearch", value = StringUtils.TRUE)
3748
class PulseResponseControllerTest extends TestContainersSuite implements MemberProfileFixture, RoleFixture, PulseResponseFixture {
3849

3950
@Inject
4051
@Client("/services/pulse-responses")
4152
protected HttpClient client;
4253

54+
@Inject
55+
private CheckInsConfiguration configuration;
56+
57+
@Inject
58+
private SlackSearchReplacement slackSearch;
59+
4360
private Map<String, MemberProfile> hierarchy;
4461

4562
@BeforeEach
@@ -516,6 +533,50 @@ void testUpdateInvalidDatePulseResponse() {
516533
assertEquals(request.getPath(), href);
517534
}
518535

536+
@Test
537+
void testCreateAPulseResponseFromSlack() {
538+
MemberProfile memberProfile = createADefaultMemberProfile();
539+
slackSearch.users.put("SLACK_ID_HI", memberProfile.getWorkEmail());
540+
541+
final String rawBody = "payload=%7B%22type%22%3A+%22view_submission%22%2C+%22user%22%3A+%7B%22id%22%3A+%22SLACK_ID_HI%22%7D%2C+%22view%22%3A+%7B%22id%22%3A+%22VNHU13V36%22%2C+%22type%22%3A+%22modal%22%2C+%22state%22%3A+%7B%22values%22%3A+%7B%22internalNumber%22%3A+%7B%22internalScore%22%3A+%7B%22selected_option%22%3A+%7B%22type%22%3A+%22radio_buttons%22%2C+%22value%22%3A+%224%22%7D%7D%7D%2C+%22internalText%22%3A+%7B%22internalFeelings%22%3A+%7B%22type%22%3A+%22plain_text_input%22%2C+%22value%22%3A+%22I+am+a+robot.%22%7D%7D%2C+%22externalNumber%22%3A+%7B%22externalScore%22%3A+%7B%22selected_option%22%3A+%7B%22type%22%3A+%22radio_buttons%22%2C+%22value%22%3A+%225%22%7D%7D%7D%2C+%22externalText%22%3A+%7B%22externalFeelings%22%3A+%7B%22type%22%3A+%22plain_text_input%22%2C+%22value%22%3A+%22You+are+a+robot.%22%7D%7D%7D%7D%7D%7D";
542+
543+
long currentTime = Instant.now().getEpochSecond();
544+
String timestamp = String.valueOf(currentTime);
545+
546+
final HttpRequest request = HttpRequest.POST("/external", rawBody)
547+
.header("Content-Type", "application/x-www-form-urlencoded")
548+
.header("X-Slack-Signature", slackSignature(timestamp, rawBody))
549+
.header("X-Slack-Request-Timestamp", timestamp);
550+
551+
final HttpResponse response = client.toBlocking().exchange(request);
552+
553+
assertEquals(HttpStatus.OK, response.getStatus());
554+
}
555+
556+
private String slackSignature(String timestamp, String rawBody) {
557+
String baseString = "v0:" + timestamp + ":" + rawBody;
558+
String secret = configuration.getApplication()
559+
.getPulseResponse()
560+
.getSlack().getSigningSecret();
561+
562+
try {
563+
// Generate HMAC SHA-256 signature
564+
Mac mac = Mac.getInstance("HmacSHA256");
565+
SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
566+
mac.init(secretKeySpec);
567+
byte[] hash = mac.doFinal(baseString.getBytes(StandardCharsets.UTF_8));
568+
569+
// Convert hash to hex
570+
StringBuilder hexString = new StringBuilder();
571+
for (byte b : hash) {
572+
hexString.append(String.format("%02x", b));
573+
}
574+
return "v0=" + hexString.toString();
575+
} catch (Exception e) {
576+
return null;
577+
}
578+
}
579+
519580
private static PulseResponseCreateDTO createPulseResponseCreateDTO() {
520581
return createPulseResponseCreateDTO(UUID.randomUUID());
521582
}

0 commit comments

Comments
 (0)