All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hudson.util.FormFieldValidator Maven / Gradle / Ivy

package hudson.util;

import static hudson.Util.fixEmpty;
import hudson.EnvVars;
import hudson.FilePath;
import hudson.Util;
import hudson.model.AbstractProject;
import hudson.model.Hudson;
import hudson.security.Permission;
import hudson.security.AccessControlled;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;

import javax.servlet.ServletException;

import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

/**
 * Base class that provides the framework for doing on-the-fly form field validation.
 *
 * 

* The {@link #check()} method is to be implemented by derived classes to perform * the validation. See hudson-behavior.js 'validated' CSS class and 'checkUrl' attribute. * * @author Kohsuke Kawaguchi */ public abstract class FormFieldValidator { public static final Permission CHECK = Hudson.ADMINISTER; protected final StaplerRequest request; protected final StaplerResponse response; /** * Permission to check, or null if this check doesn't require any permission. */ private final Permission permission; /** * The object to which the permission is checked against. */ private final AccessControlled subject; /** * @param adminOnly * Pass true to only let admin users to run the check. This is necessary * for security reason, so that unauthenticated user cannot obtain sensitive * information or run a process that may have side-effect. */ protected FormFieldValidator(StaplerRequest request, StaplerResponse response, boolean adminOnly) { this(request, response, adminOnly?Hudson.getInstance():null, adminOnly?CHECK:null); } protected FormFieldValidator(StaplerRequest request, StaplerResponse response, Permission permission) { this(request,response,Hudson.getInstance(),permission); } protected FormFieldValidator(StaplerRequest request, StaplerResponse response, AccessControlled subject, Permission permission) { this.request = request; this.response = response; this.subject = subject; this.permission = permission; } /** * Runs the validation code. */ public final void process() throws IOException, ServletException { if(permission!=null) subject.checkPermission(permission); check(); } protected abstract void check() throws IOException, ServletException; /** * Gets the parameter as a file. */ protected final File getFileParameter(String paramName) { return new File(Util.fixNull(request.getParameter(paramName))); } /** * Sends out an HTML fragment that indicates a success. */ public void ok() throws IOException, ServletException { response.setContentType("text/html"); response.getWriter().print("

"); } /** * Sends out a string error message that indicates an error. * * @param message * Human readable message to be sent. error(null) * can be used as ok(). */ public void error(String message) throws IOException, ServletException { errorWithMarkup(message==null?null:Util.escape(message)); } public void warning(String message) throws IOException, ServletException { warningWithMarkup(message==null?null:Util.escape(message)); } /** * Sends out a string error message that indicates an error, * by formatting it with {@link String#format(String, Object[])} */ public void error(String format, Object... args) throws IOException, ServletException { error(String.format(format,args)); } public void warning(String format, Object... args) throws IOException, ServletException { warning(String.format(format,args)); } /** * Sends out an HTML fragment that indicates an error. * *

* This method must be used with care to avoid cross-site scripting * attack. * * @param message * Human readable message to be sent. error(null) * can be used as ok(). */ public void errorWithMarkup(String message) throws IOException, ServletException { _errorWithMarkup(message,"error"); } public void warningWithMarkup(String message) throws IOException, ServletException { _errorWithMarkup(message,"warning"); } private void _errorWithMarkup(String message, String cssClass) throws IOException, ServletException { if(message==null) { ok(); } else { response.setContentType("text/html;charset=UTF-8"); // 1x16 spacer needed for IE since it doesn't support min-height response.getWriter().print("

"+ message+"
"); } } /** * Convenient base class for checking the validity of URLs */ public static abstract class URLCheck extends FormFieldValidator { public URLCheck(StaplerRequest request, StaplerResponse response) { // can be used to check the existence of any file in file system // or other HTTP URLs inside firewall, so limit this to the admin. super(request, response, true); } /** * Opens the given URL and reads text content from it. * This method honors Content-type header. */ protected BufferedReader open(URL url) throws IOException { // use HTTP content type to find out the charset. URLConnection con = url.openConnection(); if (con == null) { // XXX is this even permitted by URL.openConnection? throw new IOException(url.toExternalForm()); } return new BufferedReader( new InputStreamReader(con.getInputStream(),getCharset(con))); } /** * Finds the string literal from the given reader. * @return * true if found, false otherwise. */ protected boolean findText(BufferedReader in, String literal) throws IOException { String line; while((line=in.readLine())!=null) if(line.indexOf(literal)!=-1) return true; return false; } /** * Calls the {@link #error(String)} method with a reasonable error message. * Use this method when the {@link #open(URL)} or {@link #findText(BufferedReader, String)} fails. * * @param url * Pass in the URL that was connected. Used for error diagnosis. */ protected void handleIOException(String url, IOException e) throws IOException, ServletException { // any invalid URL comes here if(e.getMessage().equals(url)) // Sun JRE (and probably others too) often return just the URL in the error. error("Unable to connect "+url); else error(e.getMessage()); } /** * Figures out the charset from the content-type header. */ private String getCharset(URLConnection con) { for( String t : con.getContentType().split(";") ) { t = t.trim().toLowerCase(); if(t.startsWith("charset=")) return t.substring(8); } // couldn't find it. HTML spec says default is US-ASCII, // but UTF-8 is a better choice since // (1) it's compatible with US-ASCII // (2) a well-written web applications tend to use UTF-8 return "UTF-8"; } } /** * Checks if the given value is an URL to some Hudson's top page. * @since 1.192 */ public static class HudsonURL extends URLCheck { public HudsonURL(StaplerRequest request, StaplerResponse response) { super(request, response); } protected void check() throws IOException, ServletException { String value = fixEmpty(request.getParameter("value")); if(value==null) {// nothing entered yet ok(); return; } if(!value.endsWith("/")) value+='/'; try { URL url = new URL(value); HttpURLConnection con = (HttpURLConnection)url.openConnection(); con.connect(); if(con.getResponseCode()!=200 || con.getHeaderField("X-Hudson")==null) { error(value+" is not Hudson ("+con.getResponseMessage()+")"); return; } ok(); } catch (IOException e) { handleIOException(value,e); } } } /** * Checks the file mask (specified in the 'value' query parameter) against * the current workspace. * @since 1.90. */ public static class WorkspaceFileMask extends FormFieldValidator { private final boolean errorIfNotExist; public WorkspaceFileMask(StaplerRequest request, StaplerResponse response) { this(request, response, true); } public WorkspaceFileMask(StaplerRequest request, StaplerResponse response, boolean errorIfNotExist) { super(request, response, false); this.errorIfNotExist = errorIfNotExist; } protected void check() throws IOException, ServletException { String value = fixEmpty(request.getParameter("value")); AbstractProject p = Hudson.getInstance().getItemByFullName(request.getParameter("job"),AbstractProject.class); if(value==null || p==null) { ok(); // none entered yet, or something is seriously wrong return; } try { FilePath ws = getBaseDirectory(p); if(ws==null || !ws.exists()) {// no workspace. can't check ok(); return; } String msg = ws.validateAntFileMask(value); if(errorIfNotExist) error(msg); else warning(msg); } catch (InterruptedException e) { ok(); // coundn't check } } /** * The base directory from which the path name is resolved. */ protected FilePath getBaseDirectory(AbstractProject p) { return p.getWorkspace(); } } /** * Checks a valid directory name (specified in the 'value' query parameter) against * the current workspace. * @since 1.116. */ public static class WorkspaceDirectory extends WorkspaceFilePath { public WorkspaceDirectory(StaplerRequest request, StaplerResponse response, boolean errorIfNotExist) { super(request, response, errorIfNotExist, false); } public WorkspaceDirectory(StaplerRequest request, StaplerResponse response) { this(request, response, true); } } /** * Checks a valid file name or directory (specified in the 'value' query parameter) against * the current workspace. * @since 1.160 */ public static class WorkspaceFilePath extends FormFieldValidator { private final boolean errorIfNotExist; private final boolean expectingFile; public WorkspaceFilePath(StaplerRequest request, StaplerResponse response, boolean errorIfNotExist, boolean expectingFile) { super(request, response, false); this.errorIfNotExist = errorIfNotExist; this.expectingFile = expectingFile; } protected void check() throws IOException, ServletException { String value = fixEmpty(request.getParameter("value")); AbstractProject p = getProject(); if(value==null || p==null) { ok(); // none entered yet, or something is seriously wrong return; } if(value.contains("*")) { // a common mistake is to use wildcard error("Wildcard is not allowed here"); return; } try { FilePath ws = getBaseDirectory(p); if(ws==null) {// can't check ok(); return; } if(!ws.exists()) {// no workspace. can't check ok(); return; } if(ws.child(value).exists()) { if (expectingFile) { if(!ws.child(value).isDirectory()) ok(); else error(value+" is not a file"); } else { if(ws.child(value).isDirectory()) ok(); else error(value+" is not a directory"); } } else { String msg = "No such "+(expectingFile?"file":"directory")+": " + value; if(errorIfNotExist) error(msg); else warning(msg); } } catch (InterruptedException e) { ok(); // coundn't check } } /** * The base directory from which the path name is resolved. */ protected FilePath getBaseDirectory(AbstractProject p) { return p.getWorkspace(); } protected AbstractProject getProject() { return Hudson.getInstance().getItemByFullName(request.getParameter("job"),AbstractProject.class); } } /** * Checks a valid executable binary (specified in the 'value' query parameter). * It has to be either given as a full path to the executable, or else * it will be searched in PATH. * *

* This file also handles ".exe" omission in Windows --- I thought Windows * has actually more generic mechanism for the executable extension omission, * so perhaps this needs to be extended to handle that correctly. More info * needed. * * @since 1.124 */ public static class Executable extends FormFieldValidator { public Executable(StaplerRequest request, StaplerResponse response) { super(request, response, true); } protected void check() throws IOException, ServletException { String exe = fixEmpty(request.getParameter("value")); if(exe==null) { ok(); // nothing entered yet return; } if(exe.indexOf(File.separatorChar)>=0) { // this is full path File f = new File(exe); if(f.exists()) { checkExecutable(f); return; } File fexe = new File(exe+".exe"); if(fexe.exists()) { checkExecutable(fexe); return; } error("There's no such file: "+exe); } else { // look in PATH String path = EnvVars.masterEnvVars.get("PATH"); String tokenizedPath = ""; String delimiter = null; if(path!=null) { for (String _dir : Util.tokenize(path.replace("\\", "\\\\"),File.pathSeparator)) { if (delimiter == null) { delimiter = ", "; } else { tokenizedPath += delimiter; } tokenizedPath += _dir.replace('\\', '/'); File dir = new File(_dir); File f = new File(dir,exe); if(f.exists()) { checkExecutable(f); return; } File fexe = new File(dir,exe+".exe"); if(fexe.exists()) { checkExecutable(fexe); return; } } tokenizedPath += "."; } else { tokenizedPath = "unavailable."; } // didn't find it error("There's no such executable "+exe+" in PATH: "+tokenizedPath); } } /** * Provides an opportunity for derived classes to do additional checks on the executable. */ protected void checkExecutable(File exe) throws IOException, ServletException { ok(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy