Skip to content
Snippets Groups Projects

Iss #121 - Add noreply support to bot email checks

Merged Martin Lowe requested to merge (removed):malowe/main/121 into main
4 files
+ 72
55
Compare changes
  • Side-by-side
  • Inline
Files
4
@@ -81,8 +81,7 @@ public class CachedUserService implements UserService {
if (StringUtils.isBlank(mail)) {
return null;
}
Optional<EclipseUser> u = cache.get(mail, new MultivaluedMapImpl<>(), EclipseUser.class,
() -> retrieveUser(mail));
Optional<EclipseUser> u = cache.get(mail, new MultivaluedMapImpl<>(), EclipseUser.class, () -> retrieveUser(mail));
if (u.isPresent()) {
LOGGER.debug("Found user with email {}", mail);
return u.get();
@@ -96,8 +95,9 @@ public class CachedUserService implements UserService {
if (StringUtils.isBlank(username)) {
return null;
}
Optional<EclipseUser> u = cache.get("gh:" + username, new MultivaluedMapImpl<>(), EclipseUser.class,
() -> accounts.getUserByGithubUname(getBearerToken(), username));
Optional<EclipseUser> u = cache
.get("gh:" + username, new MultivaluedMapImpl<>(), EclipseUser.class,
() -> accounts.getUserByGithubUname(getBearerToken(), username));
if (u.isPresent()) {
LOGGER.debug("Found user with name {}", username);
return u.get();
@@ -111,30 +111,39 @@ public class CachedUserService implements UserService {
if (StringUtils.isBlank(mail)) {
return false;
}
// set the values used to validate whether user is a bot
final String checkedField;
final String checkedValue;
// check if email is a noreply address
if (patterns.stream().anyMatch(pattern -> pattern.matcher(mail.trim()).find())) {
checkedValue = extractNoReplyUserFromAddress(mail);
checkedField = "username";
LOGGER.trace("Checking {} for bot status using noreply logic", checkedValue);
} else {
checkedValue = mail;
checkedField = "email";
LOGGER.trace("Checking {} for bot status using standard logic", checkedValue);
}
// get the bots to be compared against
List<JsonNode> botObjs = getBots();
// if there are no matching projects, then check against all bots, not just
// project bots
if (filteredProjects == null || filteredProjects.isEmpty()) {
return botObjs.stream().anyMatch(bot -> checkFieldsForMatchingMail(bot, mail));
}
// for each of the matched projects, check the bot for the matching project ID
for (Project p : filteredProjects) {
// check if any of the projects match any of the bots
return filteredProjects.stream().anyMatch(p -> botObjs.stream().anyMatch(bot -> {
LOGGER.debug("Checking project {} for matching bots", p.getProjectId());
for (JsonNode bot : botObjs) {
// if the project ID match, and one of the email fields matches, then user is
// bot
if (p.getProjectId().equalsIgnoreCase(bot.get("projectId").asText())
&& checkFieldsForMatchingMail(bot, mail)) {
return true;
}
}
}
return false;
return p.getProjectId().equalsIgnoreCase(bot.get("projectId").asText())
&& checkFieldsForMatchingValue(bot, checkedValue, checkedField);
}));
}
/**
* Checks for standard and noreply email address matches for a Git user and
* converts to a Eclipse Foundation account object.
* Checks for standard and noreply email address matches for a Git user and converts to a Eclipse Foundation account
* object.
*
* @param user the user to attempt account retrieval for.
* @return the user account if found by mail, or null if none found.
@@ -164,13 +173,11 @@ public class CachedUserService implements UserService {
}
/**
* Checks git user for no-reply address, and attempts to ratify user through
* reverse lookup in API service. Currently, this service only recognizes Github
* no-reply addresses as they have a route to be mapped.
* Checks git user for no-reply address, and attempts to ratify user through reverse lookup in API service. Currently,
* this service only recognizes Github no-reply addresses as they have a route to be mapped.
*
* @param user the Git user account to check for no-reply mail address
* @return the Eclipse user if email address is detected no reply and one can be
* mapped, otherwise null
* @return the Eclipse user if email address is detected no reply and one can be mapped, otherwise null
*/
private EclipseUser checkForNoReplyUser(String mail) {
if (StringUtils.isBlank(mail)) {
@@ -180,20 +187,9 @@ public class CachedUserService implements UserService {
LOGGER.debug("Checking user with mail {} for no-reply", mail);
boolean isNoReply = patterns.stream().anyMatch(pattern -> pattern.matcher(mail.trim()).find());
if (isNoReply) {
// get the username/ID string before the first @ symbol.
String noReplyUser = mail.substring(0, mail.indexOf("@", 0));
// split based on +, if more than one part, use second (contains user),
// otherwise, use whole string
String[] nameParts = noReplyUser.split("[\\+]");
String namePart;
if (nameParts.length > 1 && nameParts[1] != null) {
namePart = nameParts[1];
} else {
namePart = nameParts[0];
}
String uname = namePart.trim();
LOGGER.debug("User with mail {} detected as noreply account, checking services for username match on '{}'",
mail, uname);
// get the user from the noreply address
String uname = extractNoReplyUserFromAddress(mail);
LOGGER.debug("User with mail {} detected as noreply account, checking services for username match on '{}'", mail, uname);
// check github for no-reply (only allowed noreply currently)
if (mail.endsWith("noreply.github.com")) {
@@ -209,18 +205,21 @@ public class CachedUserService implements UserService {
}
/**
* Checks JSON node to look for email fields, both at the root, and nested email
* fields.
* Checks JSON node to look for email fields, both at the root, and nested email fields.
*
* @param bot the bots JSON object representation
* @param bot the bots JSON object representation
* @param mail the email to match against
* @return true if the bot has a matching email value, otherwise false
*/
private boolean checkFieldsForMatchingMail(JsonNode bot, String mail) {
return checkFieldsForMatchingValue(bot, mail, "email");
}
private boolean checkFieldsForMatchingValue(JsonNode bot, String targetValue, String fieldName) {
// check the root email for the bot for match
JsonNode botmail = bot.get("email");
if (mail != null && botmail != null && mail.equalsIgnoreCase(botmail.asText(""))) {
LOGGER.debug("Found matching bot at root level for '{}'", mail);
JsonNode botField = bot.get(fieldName);
if (targetValue != null && botField != null && targetValue.equalsIgnoreCase(botField.asText(""))) {
LOGGER.debug("Found matching bot at root level for '{}'", targetValue);
return true;
}
Iterator<Entry<String, JsonNode>> i = bot.fields();
@@ -229,11 +228,11 @@ public class CachedUserService implements UserService {
// check that our field is an object with fields
JsonNode node = e.getValue();
if (node.isObject()) {
LOGGER.debug("Checking {} for bot email", e.getKey());
LOGGER.debug("Checking {} for bot value in field {}", e.getKey(), fieldName);
// if the mail matches (ignoring case) user is bot
JsonNode botAliasMail = node.get("email");
if (mail != null && botAliasMail != null && mail.equalsIgnoreCase(botAliasMail.asText(""))) {
LOGGER.debug("Found match for bot email {}", mail);
JsonNode botAliasField = node.get(fieldName);
if (targetValue != null && botAliasField != null && targetValue.equalsIgnoreCase(botAliasField.asText(""))) {
LOGGER.debug("Found match for bot value {}", targetValue);
return true;
}
}
@@ -241,9 +240,23 @@ public class CachedUserService implements UserService {
return false;
}
private String extractNoReplyUserFromAddress(String mail) {
// get the username/ID string before the first @ symbol.
String noReplyUser = mail.substring(0, mail.indexOf("@", 0));
// split based on +, if more than one part, use second (contains user),
// otherwise, use whole string
String[] nameParts = noReplyUser.split("[\\+]");
String namePart;
if (nameParts.length > 1 && nameParts[1] != null) {
namePart = nameParts[1];
} else {
namePart = nameParts[0];
}
return namePart.trim();
}
private List<JsonNode> getBots() {
Optional<List<JsonNode>> allBots = cache.get("allBots", new MultivaluedMapImpl<>(), JsonNode.class,
() -> bots.getBots());
Optional<List<JsonNode>> allBots = cache.get("allBots", new MultivaluedMapImpl<>(), JsonNode.class, () -> bots.getBots());
if (!allBots.isPresent()) {
return Collections.emptyList();
}
Loading