-
Notifications
You must be signed in to change notification settings - Fork 411
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
Fix folder credential usage #541
Changes from 1 commit
d35f206
8d05af3
1c8d88e
7d3c7e9
a01335b
94811cd
4e071dc
8217e7c
2edbd9d
c63a66c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package jenkins.plugins.slack; | ||
|
||
import com.cloudbees.plugins.credentials.CredentialsMatcher; | ||
import com.cloudbees.plugins.credentials.CredentialsMatchers; | ||
import hudson.model.Item; | ||
import hudson.security.ACL; | ||
import org.apache.commons.lang.StringUtils; | ||
import org.jenkinsci.plugins.plaincredentials.StringCredentials; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
public class CredentialsObtainer { | ||
|
||
public static StringCredentials lookupCredentials(String credentialId, Item item) { | ||
List<StringCredentials> credentials = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(StringCredentials.class, item, ACL.SYSTEM, Collections.emptyList()); | ||
CredentialsMatcher matcher = CredentialsMatchers.withId(credentialId); | ||
return CredentialsMatchers.firstOrNull(credentials, matcher); | ||
} | ||
|
||
/** | ||
* Attempts to obtain the credential with the providedId from the item's credential context, otherwise returns token | ||
* @param credentialId the id from the credential to be used | ||
* @param item the item with the context to obtain the credential from. | ||
* @param token the fallback token | ||
* @return the obtained token | ||
*/ | ||
public static String getTokenToUse(String credentialId, Item item, String token) { | ||
if (StringUtils.isEmpty(credentialId)) { | ||
return token; | ||
} | ||
StringCredentials credentials = lookupCredentials(StringUtils.trim(credentialId), item); | ||
final String response; | ||
if (credentials != null) { | ||
response = credentials.getSecret().getPlainText(); | ||
} else { | ||
response = token; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can this throw an exception if token is null please? same on line 29 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All code paths on StandardSlackService should now throw an IllegalArgumentException if token is null. |
||
} | ||
return response; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -1,5 +1,6 @@ | ||||
package jenkins.plugins.slack; | ||||
|
||||
import com.cloudbees.plugins.credentials.Credentials; | ||||
import com.cloudbees.plugins.credentials.common.StandardListBoxModel; | ||||
import com.cloudbees.plugins.credentials.domains.HostnameRequirement; | ||||
import hudson.EnvVars; | ||||
|
@@ -34,6 +35,7 @@ | |||
import org.kohsuke.stapler.QueryParameter; | ||||
import org.kohsuke.stapler.StaplerRequest; | ||||
|
||||
import java.util.NoSuchElementException; | ||||
import java.util.function.Function; | ||||
import java.util.logging.Level; | ||||
import java.util.logging.Logger; | ||||
|
@@ -428,7 +430,14 @@ public SlackService newSlackService(AbstractBuild r, BuildListener listener) { | |||
authToken = env.expand(authToken); | ||||
authTokenCredentialId = env.expand(authTokenCredentialId); | ||||
room = env.expand(room); | ||||
return new StandardSlackService(baseUrl, teamDomain, authToken, authTokenCredentialId, botUser, room, false, r.getProject()); | ||||
final String populatedToken = CredentialsObtainer.getTokenToUse(authTokenCredentialId, r.getProject(), authToken); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||
if (populatedToken != null) { | ||||
return new StandardSlackService(baseUrl, teamDomain, botUser, room, false, populatedToken); | ||||
} else { | ||||
logger.log(Level.INFO, "No explicit token specified and could not obtain credentials for id: " + authTokenCredentialId); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should throw an exception depending, or at least log a message to the build log as well There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since now getTokenToUse throws an exception (as requested in another comment) this code is now unreachable so I'm removing it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... however, this will cause current misconfigured jobs to crash on notification calls, is that desirable? Should I surround the calls to slackFactory (which ends up calling this) with try catch blocks? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes given that the existing code does this we should keep doing it for now.
|
||||
return new StandardSlackService(baseUrl, teamDomain, authToken, authTokenCredentialId, botUser, room, false); | ||||
} | ||||
|
||||
|
||||
} | ||||
|
||||
|
@@ -606,7 +615,12 @@ public boolean configure(StaplerRequest req, JSONObject formData) { | |||
} | ||||
|
||||
SlackService getSlackService(final String baseUrl, final String teamDomain, final String authTokenCredentialId, final boolean botUser, final String roomId, final Item item) { | ||||
timja marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
return new StandardSlackService(baseUrl, teamDomain, null, authTokenCredentialId, botUser, roomId, false, item); | ||||
final String populatedToken = CredentialsObtainer.getTokenToUse(authTokenCredentialId, item,null ); | ||||
if (populatedToken != null) { | ||||
return new StandardSlackService(baseUrl, teamDomain, botUser, roomId, false, populatedToken); | ||||
} else { | ||||
throw new NoSuchElementException("Could not obtain credentials with credential id: " + authTokenCredentialId); | ||||
} | ||||
} | ||||
|
||||
@Nonnull | ||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,41 +45,70 @@ public class StandardSlackService implements SlackService { | |
private String host = "slack.com"; | ||
private String baseUrl; | ||
private String teamDomain; | ||
private String token; | ||
private String authTokenCredentialId; | ||
private String token = null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. null is default 😕 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed all useless null initializations. |
||
private String authTokenCredentialId = null; | ||
private boolean botUser; | ||
private String[] roomIds; | ||
private boolean replyBroadcast; | ||
private String responseString = null; | ||
private Item item; | ||
private String populatedToken = null; | ||
|
||
/** | ||
* @deprecated use {@link #StandardSlackService(String, String, boolean, String, boolean, String)} instead} | ||
*/ | ||
@Deprecated | ||
public StandardSlackService(String baseUrl, String teamDomain, String authTokenCredentialId, boolean botUser, String roomId) { | ||
this(baseUrl, teamDomain, null, authTokenCredentialId, botUser, roomId, false, null); | ||
this(baseUrl, teamDomain, null, authTokenCredentialId, botUser, roomId, false); | ||
} | ||
|
||
/** | ||
* @deprecated use {@link #StandardSlackService(String, String, boolean, String, boolean, String)} instead} | ||
*/ | ||
@Deprecated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll check this when I test the code out, but can you make sure that none of these constructors are called anymore internally to the codebase? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just had a look, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They're only used from tests. I'll adapt them. |
||
public StandardSlackService(String baseUrl, String teamDomain, String token, String authTokenCredentialId, boolean botUser, String roomId) { | ||
this(baseUrl, teamDomain, token, authTokenCredentialId, botUser, roomId, false, null); | ||
this(baseUrl, teamDomain, token, authTokenCredentialId, botUser, roomId, false); | ||
} | ||
|
||
/** | ||
* @deprecated use {@link #StandardSlackService(String, String, boolean, String, boolean, String)} instead} | ||
*/ | ||
@Deprecated | ||
public StandardSlackService(String baseUrl, String teamDomain, String token, String authTokenCredentialId, boolean botUser, String roomId, boolean replyBroadcast) { | ||
this(baseUrl, teamDomain, token, authTokenCredentialId, botUser, roomId, replyBroadcast, null); | ||
this(baseUrl, teamDomain, botUser, roomId, replyBroadcast); | ||
this.token = token; | ||
this.authTokenCredentialId = StringUtils.trim(authTokenCredentialId); | ||
} | ||
|
||
/** | ||
* @param baseUrl the full url to use, this is an alternative to specifying teamDomain | ||
* @param teamDomain the teamDomain inside slack.com to use | ||
* @param botUser | ||
* @param roomId a semicolon separated list of rooms to notify | ||
* @param replyBroadcast | ||
* @param populatedToken a non-null token to use for authentication | ||
*/ | ||
public StandardSlackService(String baseUrl, String teamDomain, boolean botUser, String roomId, boolean replyBroadcast, String populatedToken) { | ||
this(baseUrl, teamDomain, botUser, roomId, replyBroadcast); | ||
if (populatedToken == null) { | ||
throw new IllegalArgumentException("populatedToken cannot be null"); | ||
sirstrahd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
this.populatedToken = populatedToken; | ||
} | ||
|
||
public StandardSlackService(String baseUrl, String teamDomain, String token, String authTokenCredentialId, boolean botUser, String roomId, boolean replyBroadcast, Item item) { | ||
private StandardSlackService(String baseUrl, String teamDomain, boolean botUser, String roomId, boolean replyBroadcast) { | ||
super(); | ||
this.baseUrl = baseUrl; | ||
if(this.baseUrl != null && !this.baseUrl.isEmpty() && !this.baseUrl.endsWith("/")) { | ||
this.baseUrl += "/"; | ||
} | ||
this.teamDomain = teamDomain; | ||
this.token = token; | ||
this.authTokenCredentialId = StringUtils.trim(authTokenCredentialId); | ||
this.botUser = botUser; | ||
this.roomIds = roomId.split("[,; ]+"); | ||
this.replyBroadcast = replyBroadcast; | ||
this.item = item; | ||
} | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove extra line break? |
||
|
||
public String getResponseString() { | ||
return responseString; | ||
} | ||
|
@@ -193,25 +222,22 @@ public boolean publish(String message, JSONArray attachments, String color) { | |
} | ||
|
||
private String getTokenToUse() { | ||
if (authTokenCredentialId != null && !authTokenCredentialId.isEmpty()) { | ||
StringCredentials credentials = lookupCredentials(authTokenCredentialId); | ||
if (credentials != null) { | ||
logger.fine("Using Integration Token Credential ID."); | ||
return credentials.getSecret().getPlainText(); | ||
if (populatedToken != null) { | ||
return populatedToken; | ||
} else { | ||
if (!StringUtils.isEmpty(authTokenCredentialId)) { | ||
StringCredentials credentials = CredentialsObtainer.lookupCredentials(authTokenCredentialId, null); | ||
if (credentials != null) { | ||
logger.fine("Using Integration Token Credential ID."); | ||
return credentials.getSecret().getPlainText(); | ||
} | ||
} | ||
} | ||
|
||
logger.fine("Using Integration Token."); | ||
|
||
logger.fine("Using Integration Token."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this doesn't throw an exception if its not found, |
||
} | ||
return token; | ||
} | ||
|
||
private StringCredentials lookupCredentials(String credentialId) { | ||
List<StringCredentials> credentials = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(StringCredentials.class, item, ACL.SYSTEM, Collections.emptyList()); | ||
CredentialsMatcher matcher = CredentialsMatchers.withId(credentialId); | ||
return CredentialsMatchers.firstOrNull(credentials, matcher); | ||
} | ||
|
||
protected CloseableHttpClient getHttpClient() { | ||
final HttpClientBuilder clientBuilder = HttpClients.custom(); | ||
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -15,6 +15,7 @@ | |||||
import hudson.util.FormValidation; | ||||||
import hudson.util.ListBoxModel; | ||||||
import jenkins.model.Jenkins; | ||||||
import jenkins.plugins.slack.CredentialsObtainer; | ||||||
import jenkins.plugins.slack.Messages; | ||||||
import jenkins.plugins.slack.SlackNotifier; | ||||||
import jenkins.plugins.slack.SlackService; | ||||||
|
@@ -63,6 +64,7 @@ public class SlackSendStep extends Step { | |||||
private boolean replyBroadcast; | ||||||
|
||||||
|
||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. revert?
Suggested change
|
||||||
@Nonnull | ||||||
public String getMessage() { | ||||||
return message; | ||||||
|
@@ -255,10 +257,14 @@ protected SlackResponse run() throws Exception { | |||||
defaultIfEmpty(baseUrl), defaultIfEmpty(teamDomain), channel, defaultIfEmpty(color), botUser, | ||||||
defaultIfEmpty(tokenCredentialId)) | ||||||
); | ||||||
|
||||||
final String populatedToken = CredentialsObtainer.getTokenToUse(tokenCredentialId, item, token); | ||||||
if (populatedToken == null) { | ||||||
listener.error(Messages | ||||||
.NotificationFailedWithException(new IllegalArgumentException("the token with the provided ID could not be found and no token was specified"))); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
return null; | ||||||
} | ||||||
SlackService slackService = getSlackService( | ||||||
baseUrl, teamDomain, token, tokenCredentialId, botUser, channel, step.replyBroadcast, item | ||||||
); | ||||||
baseUrl, teamDomain, botUser, channel, step.replyBroadcast, populatedToken); | ||||||
final boolean publishSuccess; | ||||||
if (step.attachments != null) { | ||||||
JSONArray jsonArray = getAttachmentsAsJSONArray(); | ||||||
|
@@ -357,8 +363,8 @@ private String defaultIfEmpty(String value) { | |||||
} | ||||||
|
||||||
//streamline unit testing | ||||||
SlackService getSlackService(String baseUrl, String team, String token, String tokenCredentialId, boolean botUser, String channel, boolean replyBroadcast, Item item) { | ||||||
return new StandardSlackService(baseUrl, team, token, tokenCredentialId, botUser, channel, replyBroadcast, item); | ||||||
SlackService getSlackService(String baseUrl, String team, boolean botUser, String channel, boolean replyBroadcast, String populatedToken) { | ||||||
return new StandardSlackService(baseUrl, team, botUser, channel, replyBroadcast, populatedToken); | ||||||
} | ||||||
} | ||||||
} |
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.
Please import, 187 characters hurts readability 😄