Skip to content

Commit

Permalink
Merge pull request #80 from jenkinsci-cert/SECURITY-362
Browse files Browse the repository at this point in the history
[SECURITY-362] Do not persist User in OfflineCause.UserCause
  • Loading branch information
jglick committed Jan 23, 2017
1 parent 15128c6 commit 3cd946c
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 9 deletions.
48 changes: 39 additions & 9 deletions core/src/main/java/hudson/slaves/OfflineCause.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

package hudson.slaves;

import jenkins.model.Jenkins;
import hudson.Functions;
import hudson.model.Computer;
import hudson.model.User;
Expand All @@ -33,7 +32,10 @@
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.export.Exported;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.io.ObjectStreamException;
import java.util.Collections;
import java.util.Date;

/**
Expand Down Expand Up @@ -128,21 +130,49 @@ public String toString() {

/**
* Taken offline by user.
*
* @since 1.551
*/
public static class UserCause extends SimpleOfflineCause {
private final User user;

public UserCause(User user, String message) {
super(hudson.slaves.Messages._SlaveComputer_DisconnectedBy(
user!=null ? user.getId() : Jenkins.ANONYMOUS.getName(),
@Deprecated
private transient User user;
// null when unknown
private /*final*/ @CheckForNull String userId;

public UserCause(@CheckForNull User user, @CheckForNull String message) {
this(
user != null ? user.getId() : null,
message != null ? " : " + message : ""
));
this.user = user;
);
}

private UserCause(String userId, String message) {
super(hudson.slaves.Messages._SlaveComputer_DisconnectedBy(userId, message));
this.userId = userId;
}

public User getUser() {
return user;
return userId == null
? User.getUnknown()
: User.getById(userId, true)
;
}

// Storing the User in a filed was a mistake, switch to userId
@SuppressWarnings("deprecation")
private Object readResolve() throws ObjectStreamException {
if (user != null) {
String id = user.getId();
if (id != null) {
userId = id;
} else {
// The user field is not properly deserialized so id may be missing. Look the user up by fullname
User user = User.get(this.user.getFullName(), true, Collections.emptyMap());
userId = user.getId();
}
this.user = null;
}
return this;
}
}

Expand Down
29 changes: 29 additions & 0 deletions test/src/test/java/hudson/model/ComputerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,22 @@
*/
package hudson.model;

import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.*;


import java.io.File;

import com.gargoylesoftware.htmlunit.xml.XmlPage;
import hudson.slaves.OfflineCause;
import jenkins.model.Jenkins;
import hudson.slaves.DumbSlave;

import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.recipes.LocalData;

public class ComputerTest {

Expand All @@ -52,4 +58,27 @@ public void discardLogsAfterDeletion() throws Exception {

assertTrue("Slave log should be kept", keep.toComputer().getLogFile().exists());
}

@Test
public void doNotShowUserDetailsInOfflineCause() throws Exception {
DumbSlave slave = j.createOnlineSlave();
final Computer computer = slave.toComputer();
computer.setTemporarilyOffline(true, new OfflineCause.UserCause(User.get("username"), "msg"));
verifyOfflineCause(computer);
}

@Test @LocalData
public void removeUserDetailsFromOfflineCause() throws Exception {
Computer computer = j.jenkins.getComputer("deserialized");
verifyOfflineCause(computer);
}

private void verifyOfflineCause(Computer computer) throws Exception {
XmlPage page = j.createWebClient().goToXml("computer/" + computer.getName() + "/config.xml");
String content = page.getWebResponse().getContentAsString("UTF-8");
assertThat(content, containsString("temporaryOfflineCause"));
assertThat(content, containsString("<userId>username</userId>"));
assertThat(content, not(containsString("ApiTokenProperty")));
assertThat(content, not(containsString("apiToken")));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version='1.0' encoding='UTF-8'?>
<hudson>
<disabledAdministrativeMonitors/>
<version>1.0</version>
<numExecutors>2</numExecutors>
<mode>NORMAL</mode>
<useSecurity>true</useSecurity>
<authorizationStrategy class="hudson.security.AuthorizationStrategy$Unsecured"/>
<securityRealm class="hudson.security.SecurityRealm$None"/>
<disableRememberMe>false</disableRememberMe>
<projectNamingStrategy class="jenkins.model.ProjectNamingStrategy$DefaultProjectNamingStrategy"/>
<workspaceDir>${JENKINS_HOME}/workspace/${ITEM_FULLNAME}</workspaceDir>
<buildsDir>${ITEM_ROOTDIR}/builds</buildsDir>
<jdks/>
<viewsTabBar class="hudson.views.DefaultViewsTabBar"/>
<myViewsTabBar class="hudson.views.DefaultMyViewsTabBar"/>
<clouds/>
<scmCheckoutRetryCount>0</scmCheckoutRetryCount>
<views>
<hudson.model.AllView>
<owner class="hudson" reference="../../.."/>
<name>All</name>
<filterExecutors>false</filterExecutors>
<filterQueue>false</filterQueue>
<properties class="hudson.model.View$PropertyList"/>
</hudson.model.AllView>
</views>
<primaryView>All</primaryView>
<slaveAgentPort>0</slaveAgentPort>
<label></label>
<nodeProperties/>
<globalNodeProperties/>
<noUsageStatistics>true</noUsageStatistics>
</hudson>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?xml version='1.0' encoding='UTF-8'?>
<slave>
<temporaryOfflineCause class="hudson.slaves.OfflineCause$UserCause">
<timestamp>1479196265920</timestamp>
<description>
<holder>
<owner>hudson.slaves.Messages</owner>
</holder>
<key>SlaveComputer.DisconnectedBy</key>
<args>
<string>username</string>
<string> : msg</string>
</args>
</description>
<user>
<fullName>username</fullName>
<properties>
<jenkins.security.ApiTokenProperty>
<apiToken>wPfVKd4HGJzRoEpazbTu35nXXfI34cguPjm+5JPO7pZDFLFgpFLviQsS3NdJndax</apiToken>
</jenkins.security.ApiTokenProperty>
<hudson.tasks.Mailer_-UserProperty/>
<hudson.model.MyViewsProperty>
<views>
<hudson.model.AllView>
<owner class="hudson.model.MyViewsProperty" reference="../../.."/>
<name>All</name>
<filterExecutors>false</filterExecutors>
<filterQueue>false</filterQueue>
<properties class="hudson.model.View$PropertyList"/>
</hudson.model.AllView>
</views>
</hudson.model.MyViewsProperty>
<hudson.model.PaneStatusProperties>
<collapsed/>
</hudson.model.PaneStatusProperties>
<hudson.search.UserSearchProperty>
<insensitiveSearch>false</insensitiveSearch>
</hudson.search.UserSearchProperty>
</properties>
</user>
</temporaryOfflineCause>
<name>deserialized</name>
<description>dummy</description>
<remoteFS>...</remoteFS>
<numExecutors>1</numExecutors>
<mode>NORMAL</mode>
<retentionStrategy class="hudson.slaves.RetentionStrategy$2">
<DESCRIPTOR>
<outer-class reference="../.."/>
</DESCRIPTOR>
</retentionStrategy>
<launcher/>
<label></label>
<nodeProperties/>
<userId>SYSTEM</userId>
</slave>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version='1.0' encoding='UTF-8'?>
<user>
<fullName>username</fullName>
<id>username</id>
<description></description>
<properties>
<jenkins.security.ApiTokenProperty>
<apiToken>qykj8q6EqvMg9LPu+lCqLiXBZvEVdCTWoYJwmicXgH+yh1ZUm85iHe29grd+g3QG</apiToken>
</jenkins.security.ApiTokenProperty>
<com.cloudbees.plugins.credentials.UserCredentialsProvider_-UserCredentialsProperty plugin="credentials@1.18">
<domainCredentialsMap class="hudson.util.CopyOnWriteMap$Hash"/>
</com.cloudbees.plugins.credentials.UserCredentialsProvider_-UserCredentialsProperty>
<hudson.tasks.Mailer_-UserProperty>
<emailAddress></emailAddress>
</hudson.tasks.Mailer_-UserProperty>
<hudson.model.MyViewsProperty>
<primaryViewName></primaryViewName>
<views>
<hudson.model.AllView>
<owner class="hudson.model.MyViewsProperty" reference="../../.."/>
<name>All</name>
<filterExecutors>false</filterExecutors>
<filterQueue>false</filterQueue>
<properties class="hudson.model.View$PropertyList"/>
</hudson.model.AllView>
</views>
</hudson.model.MyViewsProperty>
<hudson.model.PaneStatusProperties>
<collapsed/>
</hudson.model.PaneStatusProperties>
<org.jenkinsci.main.modules.cli.auth.ssh.UserPropertyImpl>
<authorizedKeys></authorizedKeys>
</org.jenkinsci.main.modules.cli.auth.ssh.UserPropertyImpl>
<hudson.search.UserSearchProperty>
<insensitiveSearch>false</insensitiveSearch>
</hudson.search.UserSearchProperty>
</properties>
</user>

0 comments on commit 3cd946c

Please sign in to comment.