package hudson.plugins.secret; import hudson.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.model.Hudson; import hudson.model.Item; import hudson.tasks.BuildWrapper; import hudson.tasks.BuildWrapperDescriptor; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Map; import java.util.UUID; import javax.servlet.ServletException; import org.apache.commons.fileupload.FileItem; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; public class SecretBuildWrapper extends BuildWrapper { public final String var; @DataBoundConstructor public SecretBuildWrapper(String var) { this.var = var; } public @Override Environment setUp(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException { FilePath secrets = build.getBuiltOn().getRootPath().child("secrets"); secrets.mkdirs(); secrets.chmod(/*0700*/448); final FilePath secret = secrets.child(UUID.randomUUID().toString()); secret.unzipFrom(new FileInputStream(new File(build.getProject().getRootDir(), "secret.zip"))); return new Environment() { public @Override void buildEnvVars(Map<String,String> env) { env.put(var, secret.getRemote()); } public @Override boolean tearDown(AbstractBuild build, BuildListener listener) throws IOException, InterruptedException { secret.deleteRecursive(); return true; } }; } @Extension public static class Descriptor extends BuildWrapperDescriptor { public boolean isApplicable(AbstractProject item) { return true; } public @Override String getDisplayName() { return "Build Secret"; } // XXX why doesn't /startUpload/ work automatically? Stapler diagnostics page claims it will... public void doStartUpload(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { rsp.setContentType("text/html"); req.getView(SecretBuildWrapper.class, "startUpload.jelly").forward(req, rsp); } public void doUpload(StaplerRequest req, StaplerResponse rsp, @QueryParameter String job) throws IOException, ServletException { AbstractProject prj = (AbstractProject) Hudson.getInstance().getItem(job); prj.checkPermission(Item.CONFIGURE); FileItem file = req.getFileItem("secret.file"); if (file == null) { throw new ServletException("no file upload"); } byte[] data = file.get(); if (data.length < 4 || data[0] != 'P' || data[1] != 'K' || data[2] != 3 || data[3] != 4) { // XXX more polite error page would be preferable throw new ServletException("not a ZIP file"); } File secretZip = new File(prj.getRootDir(), "secret.zip"); OutputStream os = new FileOutputStream(secretZip); try { os.write(data); } finally { os.close(); } try { Hudson.getInstance().createPath(secretZip.getAbsolutePath()).chmod(/*0600*/384); } catch (InterruptedException x) { throw (IOException) new IOException(x.toString()).initCause(x); } rsp.setContentType("text/html"); rsp.getWriter().println("Uploaded secret ZIP of length " + data.length + "."); } } }