Mercurial > hg > tc-symbol-server
changeset 39:1586b9eb682e
merged with auth
author | Evgeniy.Koshkin |
---|---|
date | Thu, 29 Aug 2013 19:04:57 +0400 |
parents | b983676b66f9 (current diff) 6f43b1252b32 (diff) |
children | 7f6924aefb24 |
files | |
diffstat | 12 files changed, 167 insertions(+), 103 deletions(-) [+] |
line wrap: on
line diff
--- a/common/src/jetbrains/buildServer/symbols/SymbolsConstants.java Tue Aug 13 15:53:37 2013 +0400 +++ b/common/src/jetbrains/buildServer/symbols/SymbolsConstants.java Thu Aug 29 19:04:57 2013 +0400 @@ -6,10 +6,9 @@ public class SymbolsConstants { public static final String BUILD_FEATURE_TYPE = "symbol-indexer"; - public static final String SOURCES_AUTH_REQUIRED_PARAM_NAME = "symbols.sources-auth-required"; public static final String SOURCES_SERVER_URL_PARAM_NAME = "symbols.sources-server-url"; public static final String SERVER_OWN_URL_PARAM_NAME = "symbols.server-own-url"; - public static final String APP_SYMBOLS = "app/symbols/"; - public static final String APP_SOURCES = "app/sources/"; + public static final String APP_SYMBOLS = "/app/symbols/"; + public static final String APP_SOURCES = "/app/sources/"; }
--- a/server/resources/editSymbolsBuildFeatureParams.jsp Tue Aug 13 15:53:37 2013 +0400 +++ b/server/resources/editSymbolsBuildFeatureParams.jsp Thu Aug 29 19:04:57 2013 +0400 @@ -1,18 +1,9 @@ <%@ include file="/include-internal.jsp"%> -<jsp:useBean id="isGuestEnabled" scope="request" type="java.lang.Boolean"/> - <table class="runnerFormTable"> <tr> <td colspan="2"> <em>Symbols and sources indexing will be performed for all symbol files (.pdb) appeared among build artifacts.</em> </td> </tr> - <tr> - <th>Sources Access:</th> - <td> - <props:checkboxProperty name="symbols.sources-auth-required"/> - <label for="symbols.sources-auth-required">Grand access to the sources to authenticated users only.</label> - </td> - </tr> </table> \ No newline at end of file
--- a/server/resources/symbolServerSettings.jsp Tue Aug 13 15:53:37 2013 +0400 +++ b/server/resources/symbolServerSettings.jsp Thu Aug 29 19:04:57 2013 +0400 @@ -1,44 +1,16 @@ <%@ include file="/include-internal.jsp"%> <jsp:useBean id="pageUrl" type="java.lang.String" scope="request"/> -<jsp:useBean id="isGuestEnabled" scope="request" type="java.lang.Boolean"/> -<jsp:useBean id="publicUrl" scope="request" type="java.lang.String" /> -<jsp:useBean id="privateUrl" scope="request" type="java.lang.String" /> +<jsp:useBean id="appUrl" scope="request" type="java.lang.String" /> <jsp:useBean id="actualServerUrl" scope="request" type="java.lang.String" /> <table class="runnerFormTable"> <tr> - <td colspan="2"> - <em>Use this URL in Visual Studio and WinDbg settings.</em> - </td> - </tr> - <tr> - <th>Authenticated URL:</th> - <td> - <c:set var="url"><c:url value="${actualServerUrl}${privateUrl}"/></c:set> - <div><a href="${url}">${url}</a></div> - <span class="smallNote">Access to the url requires HTTP authentication</span> - </td> - </tr> - <tr> - <th>Public URL:</th> + <th>Symbol Server URL:</th> <td> - <c:choose> - <c:when test="${not isGuestEnabled}"> - <div>Not available.</div> - <span class="smallNote"> - Guest user is disabled. - </span> - <span class="smallNote"> - You need to enable guest user login in TeamCity <a href="<c:url value="/admin/admin.html?item=auth"/>">Authentication Settings</a> for public url to work. - </span> - </c:when> - <c:otherwise> - <c:set var="url"><c:url value="${actualServerUrl}${publicUrl}"/></c:set> - <div><a href="${url}">${url}</a></div> - <span class="smallNote">No authentication is required.</span> - </c:otherwise> - </c:choose> + <c:set var="url"><c:url value="${actualServerUrl}${appUrl}"/></c:set> + <div><a href="${url}">${url}</a></div> + <span class="smallNote">Use this URL in Visual Studio and WinDbg settings.</span> </td> </tr> </table> \ No newline at end of file
--- a/server/src/META-INF/build-server-plugin-symbol-server.xml Tue Aug 13 15:53:37 2013 +0400 +++ b/server/src/META-INF/build-server-plugin-symbol-server.xml Thu Aug 29 19:04:57 2013 +0400 @@ -14,4 +14,6 @@ <bean class="jetbrains.buildServer.symbols.DownloadSymbolsController"/> <bean class="jetbrains.buildServer.symbols.DownloadSourcesController"/> + <bean class="jetbrains.buildServer.symbols.AuthHelper"/> + </beans>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/jetbrains/buildServer/symbols/AuthHelper.java Thu Aug 29 19:04:57 2013 +0400 @@ -0,0 +1,58 @@ +package jetbrains.buildServer.symbols; + +import jetbrains.buildServer.controllers.interceptors.auth.HttpAuthenticationManager; +import jetbrains.buildServer.controllers.interceptors.auth.HttpAuthenticationResult; +import jetbrains.buildServer.serverSide.auth.ServerPrincipal; +import jetbrains.buildServer.serverSide.impl.ServerSettings; +import jetbrains.buildServer.users.SUser; +import jetbrains.buildServer.users.UserModel; +import jetbrains.buildServer.util.Predicate; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author Evgeniy.Koshkin + */ +public class AuthHelper { + + @NotNull private final ServerSettings myServerSettings; + @NotNull private final UserModel myUserModel; + @NotNull private final HttpAuthenticationManager myAuthManager; + + public AuthHelper(@NotNull ServerSettings serverSettings, + @NotNull UserModel userModel, + @NotNull HttpAuthenticationManager authManager) { + myServerSettings = serverSettings; + myUserModel = userModel; + myAuthManager = authManager; + } + + @Nullable + public SUser getAuthenticatedUser(@NotNull HttpServletRequest request, + @NotNull HttpServletResponse response, + @NotNull Predicate<SUser> hasPermissions) throws IOException { + if(myServerSettings.isGuestLoginAllowed()) { + final SUser guestUser = myUserModel.getGuestUser(); + if(hasPermissions.apply(guestUser)) return guestUser; + } + final HttpAuthenticationResult authResult = myAuthManager.processAuthenticationRequest(request, response); + switch (authResult.getType()) { + case NOT_APPLICABLE: + myAuthManager.processUnauthenticatedRequest(request, response); + return null; + case UNAUTHENTICATED: + return null; + } + final ServerPrincipal principal = authResult.getPrincipal(); + final SUser user = myUserModel.findUserAccount(principal.getRealm(), principal.getName()); + if(user == null){ + response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access denied"); + return null; + } + return hasPermissions.apply(user) ? user : null; + } +}
--- a/server/src/jetbrains/buildServer/symbols/DownloadSourcesController.java Tue Aug 13 15:53:37 2013 +0400 +++ b/server/src/jetbrains/buildServer/symbols/DownloadSourcesController.java Thu Aug 29 19:04:57 2013 +0400 @@ -16,8 +16,11 @@ package jetbrains.buildServer.symbols; +import jetbrains.buildServer.controllers.AuthorizationInterceptor; import jetbrains.buildServer.controllers.BaseController; import jetbrains.buildServer.serverSide.SBuildServer; +import jetbrains.buildServer.users.SUser; +import jetbrains.buildServer.util.Predicate; import jetbrains.buildServer.web.openapi.WebControllerManager; import jetbrains.buildServer.web.util.WebUtil; import org.apache.log4j.Logger; @@ -35,12 +38,19 @@ public class DownloadSourcesController extends BaseController { private static final String VALID_URL_PATTERN = ".*/builds/id-\\d*/sources/.*"; - private static final Logger LOG = Logger.getLogger(DownloadSourcesController.class); - public DownloadSourcesController(@NotNull SBuildServer server, @NotNull WebControllerManager controllerManager) { + @NotNull private final AuthHelper myAuthHelper; + + public DownloadSourcesController(@NotNull SBuildServer server, + @NotNull WebControllerManager webManager, + @NotNull AuthorizationInterceptor authInterceptor, + @NotNull AuthHelper authHelper) { super(server); - controllerManager.registerController(SymbolsConstants.APP_SOURCES + "**", this); + myAuthHelper = authHelper; + final String path = SymbolsConstants.APP_SOURCES + "**"; + webManager.registerController(path, this); + authInterceptor.addPathNotRequiringAuth(path); } @Nullable @@ -51,6 +61,14 @@ WebUtil.notFound(request, response, "Url is invalid", null); return null; } + + final SUser user = myAuthHelper.getAuthenticatedUser(request, response, new Predicate<SUser>() { + public boolean apply(SUser user) { + return true; + } + }); + if (user == null) return null; + String restMethodUrl = requestURI.replace("/builds/id-", "/builds/id:").replace("/app/sources/", "/app/rest/"); final String contextPath = request.getContextPath(); if(restMethodUrl.startsWith(contextPath)){
--- a/server/src/jetbrains/buildServer/symbols/DownloadSymbolsController.java Tue Aug 13 15:53:37 2013 +0400 +++ b/server/src/jetbrains/buildServer/symbols/DownloadSymbolsController.java Thu Aug 29 19:04:57 2013 +0400 @@ -1,13 +1,18 @@ package jetbrains.buildServer.symbols; +import jetbrains.buildServer.controllers.AuthorizationInterceptor; import jetbrains.buildServer.controllers.BaseController; import jetbrains.buildServer.serverSide.SBuild; import jetbrains.buildServer.serverSide.SBuildServer; +import jetbrains.buildServer.serverSide.SecurityContextEx; import jetbrains.buildServer.serverSide.artifacts.BuildArtifact; import jetbrains.buildServer.serverSide.artifacts.BuildArtifactsViewMode; +import jetbrains.buildServer.serverSide.auth.Permission; import jetbrains.buildServer.serverSide.metadata.BuildMetadataEntry; import jetbrains.buildServer.serverSide.metadata.MetadataStorage; +import jetbrains.buildServer.users.SUser; import jetbrains.buildServer.util.FileUtil; +import jetbrains.buildServer.util.Predicate; import jetbrains.buildServer.web.openapi.WebControllerManager; import jetbrains.buildServer.web.util.WebUtil; import org.apache.log4j.Logger; @@ -27,27 +32,36 @@ */ public class DownloadSymbolsController extends BaseController { - private static final String APP_SYMBOLS = "/" + SymbolsConstants.APP_SYMBOLS; - private static final String COMPRESSED_FILE_EXTENSION = "pd_"; private static final String FILE_POINTER_FILE_EXTENSION = "ptr"; private static final Logger LOG = Logger.getLogger(DownloadSymbolsController.class); + @NotNull private final SecurityContextEx mySecurityContext; @NotNull private final MetadataStorage myBuildMetadataStorage; + @NotNull private final AuthHelper myAuthHelper; - public DownloadSymbolsController(@NotNull SBuildServer server, @NotNull WebControllerManager controllerManager, @NotNull MetadataStorage buildMetadataStorage) { + public DownloadSymbolsController(@NotNull SBuildServer server, + @NotNull WebControllerManager controllerManager, + @NotNull AuthorizationInterceptor authInterceptor, + @NotNull SecurityContextEx securityContext, + @NotNull MetadataStorage buildMetadataStorage, + @NotNull AuthHelper authHelper) { super(server); + mySecurityContext = securityContext; myBuildMetadataStorage = buildMetadataStorage; - controllerManager.registerController(APP_SYMBOLS + "**", this); + myAuthHelper = authHelper; + final String path = SymbolsConstants.APP_SYMBOLS + "**"; + controllerManager.registerController(path, this); + authInterceptor.addPathNotRequiringAuth(path); } @Nullable @Override - protected ModelAndView doHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws Exception { + protected ModelAndView doHandle(final @NotNull HttpServletRequest request, final @NotNull HttpServletResponse response) throws Exception { final String requestURI = request.getRequestURI(); - if(requestURI.endsWith(APP_SYMBOLS)){ + if(requestURI.endsWith(SymbolsConstants.APP_SYMBOLS)){ response.sendError(HttpServletResponse.SC_OK, "TeamCity Symbol Server available"); return null; } @@ -61,42 +75,60 @@ return null; } - final String valuableUriPart = requestURI.substring(requestURI.indexOf(APP_SYMBOLS) + APP_SYMBOLS.length()); + final String valuableUriPart = requestURI.substring(requestURI.indexOf(SymbolsConstants.APP_SYMBOLS) + SymbolsConstants.APP_SYMBOLS.length()); final int firstDelimiterPosition = valuableUriPart.indexOf('/'); final String fileName = valuableUriPart.substring(0, firstDelimiterPosition); final String signature = valuableUriPart.substring(firstDelimiterPosition + 1, valuableUriPart.indexOf('/', firstDelimiterPosition + 1)); final String guid = signature.substring(0, signature.length() - 1); //last symbol is PEDebugType LOG.debug(String.format("Symbol file requested. File name: %s. Guid: %s.", fileName, guid)); - final BuildArtifact buildArtifact = findArtifact(guid, fileName); - if(buildArtifact == null){ - WebUtil.notFound(request, response, "Symbol file not found", null); - LOG.debug(String.format("Symbol file not found. File name: %s. Guid: %s.", fileName, guid)); - return null; - } + final SUser user = myAuthHelper.getAuthenticatedUser(request, response, new Predicate<SUser>() { + public boolean apply(SUser user) { + final String projectId = findRelatedProjectId(guid); + if(projectId == null) return false; + //TODO: response.sendError(HttpServletResponse.SC_FORBIDDEN, "You have no permissions to download PDB files."); + return user.isPermissionGrantedForProject(projectId, Permission.VIEW_BUILD_RUNTIME_DATA); + } + }); + if (user == null) return null; - BufferedOutputStream output = new BufferedOutputStream(response.getOutputStream()); try { - InputStream input = buildArtifact.getInputStream(); - try { - FileUtil.copyStreams(input, output); - } finally { - FileUtil.close(input); - } - } finally { - FileUtil.close(output); + mySecurityContext.runAs(user, new SecurityContextEx.RunAsAction() { + public void run() throws Throwable { + final BuildArtifact buildArtifact = findArtifact(guid, fileName); + if(buildArtifact == null){ + WebUtil.notFound(request, response, "Symbol file not found", null); + LOG.debug(String.format("Symbol file not found. File name: %s. Guid: %s.", fileName, guid)); + return; + } + + BufferedOutputStream output = new BufferedOutputStream(response.getOutputStream()); + try { + InputStream input = buildArtifact.getInputStream(); + try { + FileUtil.copyStreams(input, output); + } finally { + FileUtil.close(input); + } + } finally { + FileUtil.close(output); + } + } + }); + } catch (Throwable throwable) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, throwable.getMessage()); } return null; } + @Nullable private BuildArtifact findArtifact(String guid, String fileName) { - final Iterator<BuildMetadataEntry> entryIterator = myBuildMetadataStorage.getEntriesByKey(BuildSymbolsIndexProvider.PROVIDER_ID, guid); - if(!entryIterator.hasNext()){ + final BuildMetadataEntry entry = getMetadataEntry(guid); + if(entry == null) { LOG.debug(String.format("No items found in symbol index for guid '%s'", guid)); return null; } - final BuildMetadataEntry entry = entryIterator.next(); final Map<String,String> metadata = entry.getMetadata(); final String storedFileName = metadata.get(BuildSymbolsIndexProvider.FILE_NAME_KEY); final String artifactPath = metadata.get(BuildSymbolsIndexProvider.ARTIFACT_PATH_KEY); @@ -116,4 +148,20 @@ } return buildArtifact; } + + @Nullable + private String findRelatedProjectId(String symbolFileId) { + //TODO: log errorS + final BuildMetadataEntry metadataEntry = getMetadataEntry(symbolFileId); + if(metadataEntry == null) return null; + final SBuild build = myServer.findBuildInstanceById(metadataEntry.getBuildId()); + if(build == null) return null; + return build.getProjectId(); + } + + @Nullable + private BuildMetadataEntry getMetadataEntry(String key){ + final Iterator<BuildMetadataEntry> entryIterator = myBuildMetadataStorage.getEntriesByKey(BuildSymbolsIndexProvider.PROVIDER_ID, key); + return !entryIterator.hasNext() ? null : entryIterator.next(); + } } \ No newline at end of file
--- a/server/src/jetbrains/buildServer/symbols/IndexSymbolsBuildFeature.java Tue Aug 13 15:53:37 2013 +0400 +++ b/server/src/jetbrains/buildServer/symbols/IndexSymbolsBuildFeature.java Thu Aug 29 19:04:57 2013 +0400 @@ -2,17 +2,14 @@ import jetbrains.buildServer.controllers.BaseController; import jetbrains.buildServer.serverSide.BuildFeature; -import jetbrains.buildServer.serverSide.impl.ServerSettings; import jetbrains.buildServer.web.openapi.PluginDescriptor; import jetbrains.buildServer.web.openapi.WebControllerManager; -import jetbrains.buildServer.web.util.WebUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.util.Map; /** * @author Evgeniy.Koshkin @@ -21,18 +18,14 @@ private String myEditParametersUrl; - public IndexSymbolsBuildFeature(final PluginDescriptor pluginDescriptor, final WebControllerManager web, final ServerSettings serverSettings) { + public IndexSymbolsBuildFeature(final PluginDescriptor pluginDescriptor, final WebControllerManager web) { final String jsp = pluginDescriptor.getPluginResourcesPath("editSymbolsBuildFeatureParams.jsp"); final String html = pluginDescriptor.getPluginResourcesPath("symbolIndexerSettings.html"); web.registerController(html, new BaseController() { @Override protected ModelAndView doHandle(final HttpServletRequest request, final HttpServletResponse response) throws Exception { - final ModelAndView modelAndView = new ModelAndView(jsp); - modelAndView.getModel().put("isGuestEnabled", serverSettings.isGuestLoginAllowed()); - modelAndView.getModel().put("actualServerUrl", WebUtil.getRootUrl(request)); - modelAndView.getModel().put("publicFeedUrl", WebUtil.GUEST_AUTH_PREFIX + SymbolsConstants.APP_SYMBOLS); - return modelAndView; + return new ModelAndView(jsp); } }); @@ -61,12 +54,4 @@ public String getEditParametersUrl() { return myEditParametersUrl; } - - @NotNull - @Override - public String describeParameters(@NotNull Map<String, String> params) { - return Boolean.parseBoolean(params.get(SymbolsConstants.SOURCES_AUTH_REQUIRED_PARAM_NAME)) - ? "Access to the indexed sources requires HTTP authentication." - : "No authentication is required to retrieve indexed sources"; - } }
--- a/server/src/jetbrains/buildServer/symbols/SymbolServerSettingsTab.java Tue Aug 13 15:53:37 2013 +0400 +++ b/server/src/jetbrains/buildServer/symbols/SymbolServerSettingsTab.java Thu Aug 29 19:04:57 2013 +0400 @@ -19,7 +19,6 @@ import jetbrains.buildServer.serverSide.auth.AuthUtil; import jetbrains.buildServer.serverSide.auth.Permission; import jetbrains.buildServer.serverSide.auth.SecurityContext; -import jetbrains.buildServer.serverSide.impl.ServerSettings; import jetbrains.buildServer.web.openapi.PagePlaces; import jetbrains.buildServer.web.openapi.PlaceId; import jetbrains.buildServer.web.openapi.PluginDescriptor; @@ -37,11 +36,9 @@ private static final String TAB_ID = "sourceServerSettingsTab"; - @NotNull private final ServerSettings myServerSettings; @NotNull private final SecurityContext mySecurityContext; public SymbolServerSettingsTab(@NotNull final PagePlaces pagePlaces, - @NotNull final ServerSettings serverSettings, @NotNull final SecurityContext context, @NotNull final PluginDescriptor descriptor) { super(pagePlaces, @@ -49,7 +46,6 @@ TAB_ID, descriptor.getPluginResourcesPath("symbolServerSettings.jsp"), "Symbol Server"); - myServerSettings = serverSettings; mySecurityContext = context; register(); } @@ -67,10 +63,8 @@ @Override public void fillModel(@NotNull Map<String, Object> model, @NotNull HttpServletRequest request) { super.fillModel(model, request); - model.put("isGuestEnabled", myServerSettings.isGuestLoginAllowed()); model.put("actualServerUrl", WebUtil.getRootUrl(request)); - model.put("publicUrl", WebUtil.GUEST_AUTH_PREFIX + SymbolsConstants.APP_SYMBOLS); - model.put("privateUrl", WebUtil.HTTP_AUTH_PREFIX + SymbolsConstants.APP_SYMBOLS); + model.put("appUrl", SymbolsConstants.APP_SYMBOLS); } private boolean hasAccess() {
--- a/server/src/jetbrains/buildServer/symbols/SymbolsIndexerParametersPreprocessor.java Tue Aug 13 15:53:37 2013 +0400 +++ b/server/src/jetbrains/buildServer/symbols/SymbolsIndexerParametersPreprocessor.java Thu Aug 29 19:04:57 2013 +0400 @@ -18,7 +18,6 @@ import jetbrains.buildServer.RootUrlHolder; import jetbrains.buildServer.serverSide.*; -import jetbrains.buildServer.web.util.WebUtil; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -40,13 +39,11 @@ final Collection<SBuildFeatureDescriptor> buildFeatures = buildType.getResolvedSettings().getBuildFeatures(); for(SBuildFeatureDescriptor buildFeature : buildFeatures){ if(!buildFeature.getId().equals(SymbolsConstants.BUILD_FEATURE_TYPE)) continue; - boolean isAuthRequiredToGetSources = buildFeature.getParameters().containsKey(SymbolsConstants.SOURCES_AUTH_REQUIRED_PARAM_NAME); String serverOwnUrl = context.getSharedParameters().get(SymbolsConstants.SERVER_OWN_URL_PARAM_NAME); if(serverOwnUrl == null){ serverOwnUrl = myRootUrlHolder.getRootUrl(); } - final String authPrefix = isAuthRequiredToGetSources ? WebUtil.HTTP_AUTH_PREFIX : WebUtil.GUEST_AUTH_PREFIX; - final String sourceServerUrl = String.format("%s%s%s", serverOwnUrl, authPrefix, SymbolsConstants.APP_SOURCES); + final String sourceServerUrl = String.format("%s%s", serverOwnUrl, SymbolsConstants.APP_SOURCES); context.addSharedParameter(SymbolsConstants.SOURCES_SERVER_URL_PARAM_NAME, sourceServerUrl); } }
--- a/tests/src/PdbFilePatcherTest.java Tue Aug 13 15:53:37 2013 +0400 +++ b/tests/src/PdbFilePatcherTest.java Thu Aug 29 19:04:57 2013 +0400 @@ -1,7 +1,7 @@ -import com.intellij.openapi.util.io.FileUtil; import jetbrains.buildServer.BaseTestCase; import jetbrains.buildServer.symbols.PdbFilePatcher; import jetbrains.buildServer.symbols.SrcSrvStreamBuilder; +import jetbrains.buildServer.util.FileUtil; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test;
--- a/tests/src/PdbStrExeTest.java Tue Aug 13 15:53:37 2013 +0400 +++ b/tests/src/PdbStrExeTest.java Thu Aug 29 19:04:57 2013 +0400 @@ -14,11 +14,11 @@ * limitations under the License. */ -import com.intellij.openapi.util.io.FileUtil; import jetbrains.buildServer.BaseTestCase; import jetbrains.buildServer.ExecResult; import jetbrains.buildServer.symbols.tools.PdbStrExe; import jetbrains.buildServer.symbols.tools.PdbStrExeCommands; +import jetbrains.buildServer.util.FileUtil; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test;