Repository: nutch Updated Branches: refs/heads/2.x 9ecdc9be3 -> 75d846cf3
NUTCH-2308 SSL connection test at TestNutchAPI is implemented. Project: http://git-wip-us.apache.org/repos/asf/nutch/repo Commit: http://git-wip-us.apache.org/repos/asf/nutch/commit/75d846cf Tree: http://git-wip-us.apache.org/repos/asf/nutch/tree/75d846cf Diff: http://git-wip-us.apache.org/repos/asf/nutch/diff/75d846cf Branch: refs/heads/2.x Commit: 75d846cf3998faeffa6edf5a7d7fec2d22c8d4d9 Parents: 9ecdc9b Author: Furkan KAMACI <[email protected]> Authored: Thu Aug 25 23:54:52 2016 +0300 Committer: Furkan KAMACI <[email protected]> Committed: Tue Aug 30 19:02:29 2016 +0300 ---------------------------------------------------------------------- conf/nutch-default.xml | 4 +- src/java/org/apache/nutch/api/NutchServer.java | 10 +- .../nutch/api/resources/AdminResource.java | 8 +- .../nutch/api/resources/ConfigResource.java | 8 +- .../apache/nutch/api/resources/DbResource.java | 4 +- .../apache/nutch/api/resources/JobResource.java | 4 +- .../nutch/api/resources/SeedResource.java | 4 +- .../apache/nutch/api/security/SecurityUtil.java | 108 ------------------- .../nutch/api/security/SecurityUtils.java | 107 ++++++++++++++++++ src/test/nutch-site.xml | 2 +- src/test/nutch-ssl.keystore.jks | Bin 2300 -> 2272 bytes src/test/nutch.cer | Bin 0 -> 921 bytes .../nutch/api/AbstractNutchAPITestBase.java | 89 +++++++++++++-- src/test/org/apache/nutch/api/TestNutchAPI.java | 33 +++--- src/test/testTrustKeyStore | Bin 0 -> 983 bytes 15 files changed, 231 insertions(+), 150 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/conf/nutch-default.xml ---------------------------------------------------------------------- diff --git a/conf/nutch-default.xml b/conf/nutch-default.xml index d2181c5..575ce5d 100644 --- a/conf/nutch-default.xml +++ b/conf/nutch-default.xml @@ -1465,10 +1465,10 @@ <property> <name>restapi.auth.ssl.storepath</name> - <value>etc/nutch-ssl.keystore.jks</value> + <value>nutch-ssl.keystore.jks</value> <description> Key store path for jks file. restapi.auth property should be set to SSL to use this property. - etc/nutch-ssl.keystore.jks is used for restapi.auth.ssl.storepath as default. + nutch-ssl.keystore.jks is used for restapi.auth.ssl.storepath as default. </description> </property> http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/java/org/apache/nutch/api/NutchServer.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/nutch/api/NutchServer.java b/src/java/org/apache/nutch/api/NutchServer.java index e65742d..802bbef 100644 --- a/src/java/org/apache/nutch/api/NutchServer.java +++ b/src/java/org/apache/nutch/api/NutchServer.java @@ -44,7 +44,7 @@ import org.apache.nutch.api.resources.DbResource; import org.apache.nutch.api.resources.JobResource; import org.apache.nutch.api.resources.SeedResource; import org.apache.nutch.api.security.AuthenticationTypeEnum; -import org.apache.nutch.api.security.SecurityUtil; +import org.apache.nutch.api.security.SecurityUtils; import org.restlet.Component; import org.restlet.Context; import org.restlet.Server; @@ -168,7 +168,7 @@ public class NutchServer extends Application { parameters.add("sslContextFactory", "org.restlet.engine.ssl.DefaultSslContextFactory"); String keyStorePath = configManager.get(activeConfId) - .get("restapi.auth.ssl.storepath", "etc/nutch-ssl.keystore.jks"); + .get("restapi.auth.ssl.storepath", "nutch-ssl.keystore.jks"); parameters.add("keyStorePath", keyStorePath); String keyStorePassword = configManager.get(activeConfId) @@ -191,7 +191,7 @@ public class NutchServer extends Application { application.add(this); application.setStatusService(new ErrorStatusService()); childContext.getAttributes().put(NUTCH_SERVER, this); - application.setRoles(SecurityUtil.getRoles(application)); + application.setRoles(SecurityUtils.getRoles(application)); switch (authenticationType) { case NONE: @@ -201,7 +201,7 @@ public class NutchServer extends Application { case BASIC: ChallengeAuthenticator challengeGuard = new ChallengeAuthenticator(null, ChallengeScheme.HTTP_BASIC, "Nutch REST API Realm"); //Create in-memory users with roles - MemoryRealm basicAuthRealm = SecurityUtil.constructRealm(application, configManager, confId); + MemoryRealm basicAuthRealm = SecurityUtils.constructRealm(application, configManager, confId); //Attach verifier to check authentication and enroler to determine roles challengeGuard.setVerifier(basicAuthRealm.getVerifier()); challengeGuard.setEnroler(basicAuthRealm.getEnroler()); @@ -212,7 +212,7 @@ public class NutchServer extends Application { case DIGEST: DigestAuthenticator digestGuard = new DigestAuthenticator(null, "Nutch REST API Realm", "NutchSecretKey"); //Create in-memory users with roles - MemoryRealm digestAuthRealm = SecurityUtil.constructRealm(application, configManager, confId); + MemoryRealm digestAuthRealm = SecurityUtils.constructRealm(application, configManager, confId); digestGuard.setWrappedVerifier((LocalVerifier) digestAuthRealm.getVerifier()); digestGuard.setEnroler(digestAuthRealm.getEnroler()); digestGuard.setNext(application); http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/java/org/apache/nutch/api/resources/AdminResource.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/nutch/api/resources/AdminResource.java b/src/java/org/apache/nutch/api/resources/AdminResource.java index cfbf8d5..5cc0b55 100644 --- a/src/java/org/apache/nutch/api/resources/AdminResource.java +++ b/src/java/org/apache/nutch/api/resources/AdminResource.java @@ -32,7 +32,7 @@ import javax.ws.rs.core.SecurityContext; import org.apache.nutch.api.model.response.NutchStatus; import org.apache.nutch.api.model.response.JobInfo.State; -import org.apache.nutch.api.security.SecurityUtil; +import org.apache.nutch.api.security.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +50,7 @@ public class AdminResource extends AbstractResource { @GET @Path("/") public NutchStatus getNutchStatus(@Context HttpHeaders headers) { - SecurityUtil.allowOnlyAdmin(securityContext); + SecurityUtils.allowOnlyAdmin(securityContext); NutchStatus status = new NutchStatus(); status.setStartDate(new Date(server.getStarted())); status.setConfiguration(configManager.list()); @@ -65,7 +65,7 @@ public class AdminResource extends AbstractResource { @Path("/stop") @Produces(MediaType.TEXT_PLAIN) public String stop(@QueryParam("force") boolean force) { - SecurityUtil.allowOnlyAdmin(securityContext); + SecurityUtils.allowOnlyAdmin(securityContext); if (!server.canStop(force)) { LOG.info("Command 'stop' denied due to unfinished jobs"); return "Can't stop now. There are jobs running. Try force option."; @@ -76,7 +76,7 @@ public class AdminResource extends AbstractResource { } private void scheduleServerStop() { - SecurityUtil.allowOnlyAdmin(securityContext); + SecurityUtils.allowOnlyAdmin(securityContext); LOG.info("Server shutdown scheduled in {} seconds", DELAY_SEC); Thread thread = new Thread() { public void run() { http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/java/org/apache/nutch/api/resources/ConfigResource.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/nutch/api/resources/ConfigResource.java b/src/java/org/apache/nutch/api/resources/ConfigResource.java index bc65826..ea8e577 100644 --- a/src/java/org/apache/nutch/api/resources/ConfigResource.java +++ b/src/java/org/apache/nutch/api/resources/ConfigResource.java @@ -36,7 +36,7 @@ import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.SecurityContext; import org.apache.nutch.api.model.request.NutchConfig; -import org.apache.nutch.api.security.SecurityUtil; +import org.apache.nutch.api.security.SecurityUtils; @Path("/config") public class ConfigResource extends AbstractResource { @@ -68,7 +68,7 @@ public class ConfigResource extends AbstractResource { @DELETE @Path("/{configId}") public void deleteConfig(@PathParam("configId") String configId) { - SecurityUtil.allowOnlyAdmin(securityContext); + SecurityUtils.allowOnlyAdmin(securityContext); configManager.delete(configId); } @@ -77,7 +77,7 @@ public class ConfigResource extends AbstractResource { @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.TEXT_PLAIN) public String createConfig(NutchConfig newConfig) { - SecurityUtil.allowOnlyAdmin(securityContext); + SecurityUtils.allowOnlyAdmin(securityContext); if (newConfig == null) { throw new WebApplicationException(Response.status(Status.BAD_REQUEST) .entity("Nutch configuration cannot be empty!").build()); @@ -89,7 +89,7 @@ public class ConfigResource extends AbstractResource { @Path("/{config}/{property}") public Response update(@PathParam("config") String config, @PathParam("property") String property, @FormParam("value") String value) { - SecurityUtil.allowOnlyAdmin(securityContext); + SecurityUtils.allowOnlyAdmin(securityContext); if (value == null) { throwBadRequestException("Missing property value!"); } http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/java/org/apache/nutch/api/resources/DbResource.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/nutch/api/resources/DbResource.java b/src/java/org/apache/nutch/api/resources/DbResource.java index b137dea..7b8ef46 100644 --- a/src/java/org/apache/nutch/api/resources/DbResource.java +++ b/src/java/org/apache/nutch/api/resources/DbResource.java @@ -30,7 +30,7 @@ import javax.ws.rs.core.SecurityContext; import org.apache.nutch.api.impl.db.DbReader; import org.apache.nutch.api.model.request.DbFilter; import org.apache.nutch.api.model.response.DbQueryResult; -import org.apache.nutch.api.security.SecurityUtil; +import org.apache.nutch.api.security.SecurityUtils; @Path("/db") public class DbResource extends AbstractResource { @@ -43,7 +43,7 @@ public class DbResource extends AbstractResource { @POST @Consumes(MediaType.APPLICATION_JSON) public DbQueryResult runQuery(DbFilter filter) { - SecurityUtil.allowOnlyAdmin(securityContext); + SecurityUtils.allowOnlyAdmin(securityContext); if (filter == null) { throwBadRequestException("Filter cannot be null!"); } http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/java/org/apache/nutch/api/resources/JobResource.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/nutch/api/resources/JobResource.java b/src/java/org/apache/nutch/api/resources/JobResource.java index 2ada981..0f1a540 100644 --- a/src/java/org/apache/nutch/api/resources/JobResource.java +++ b/src/java/org/apache/nutch/api/resources/JobResource.java @@ -32,7 +32,7 @@ import javax.ws.rs.core.SecurityContext; import org.apache.nutch.api.model.request.JobConfig; import org.apache.nutch.api.model.response.JobInfo; import org.apache.nutch.api.model.response.JobInfo.State; -import org.apache.nutch.api.security.SecurityUtil; +import org.apache.nutch.api.security.SecurityUtils; @Path(value = "/job") public class JobResource extends AbstractResource { @@ -72,7 +72,7 @@ public class JobResource extends AbstractResource { @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.TEXT_PLAIN) public String create(JobConfig config) { - SecurityUtil.allowOnlyAdmin(securityContext); + SecurityUtils.allowOnlyAdmin(securityContext); if (config == null) { throwBadRequestException("Job configuration is required!"); } http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/java/org/apache/nutch/api/resources/SeedResource.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/nutch/api/resources/SeedResource.java b/src/java/org/apache/nutch/api/resources/SeedResource.java index d7439e0..7ea1b8d 100644 --- a/src/java/org/apache/nutch/api/resources/SeedResource.java +++ b/src/java/org/apache/nutch/api/resources/SeedResource.java @@ -38,7 +38,7 @@ import javax.ws.rs.core.SecurityContext; import org.apache.commons.collections.CollectionUtils; import org.apache.nutch.api.model.request.SeedList; import org.apache.nutch.api.model.request.SeedUrl; -import org.apache.nutch.api.security.SecurityUtil; +import org.apache.nutch.api.security.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,7 +60,7 @@ public class SeedResource extends AbstractResource { * @return */ public String createSeedFile(SeedList seedList) { - SecurityUtil.allowOnlyAdmin(securityContext); + SecurityUtils.allowOnlyAdmin(securityContext); if (seedList == null) { throw new WebApplicationException(Response.status(Status.BAD_REQUEST) .entity("Seed list cannot be empty!").build()); http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/java/org/apache/nutch/api/security/SecurityUtil.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/nutch/api/security/SecurityUtil.java b/src/java/org/apache/nutch/api/security/SecurityUtil.java deleted file mode 100644 index 734207a..0000000 --- a/src/java/org/apache/nutch/api/security/SecurityUtil.java +++ /dev/null @@ -1,108 +0,0 @@ -/******************************************************************************* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package org.apache.nutch.api.security; - -import org.apache.nutch.api.ConfManager; -import org.apache.nutch.api.resources.ConfigResource; -import org.restlet.ext.jaxrs.JaxRsApplication; -import org.restlet.security.MapVerifier; -import org.restlet.security.MemoryRealm; -import org.restlet.security.Role; -import org.restlet.security.User; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.util.ArrayList; -import java.util.List; - -/** - * Utility class for security operations for NutchServer REST API. - * - */ -public final class SecurityUtil { - - private static final Logger LOG = LoggerFactory.getLogger(SecurityUtil.class); - - /** - * Private constructor to prevent instantiation - */ - private SecurityUtil() { - } - - /** - * Returns roles defined at {@link org.apache.nutch.api.security.AuthorizationRoleEnum} associated with - * {@link org.restlet.ext.jaxrs.JaxRsApplication} type application - * - * @param application {@link org.restlet.ext.jaxrs.JaxRsApplication} type application - * @return roles associated with given {@link org.restlet.ext.jaxrs.JaxRsApplication} type application - */ - public static List<Role> getRoles(JaxRsApplication application) { - List<Role> roles = new ArrayList<>(); - for (AuthorizationRoleEnum authorizationRole : AuthorizationRoleEnum.values()) { - roles.add(new Role(application, authorizationRole.toString())); - } - return roles; - } - - /** - * Constructs realm - * - * @param application {@link org.restlet.ext.jaxrs.JaxRsApplication }application - * @param configManager {@link org.apache.nutch.api.ConfManager} type config manager - * @param confId Configuration id to use from {@link org.apache.nutch.api.ConfManager} type config manager - * @return realm - */ - public static MemoryRealm constructRealm(JaxRsApplication application, ConfManager configManager, String confId){ - MemoryRealm realm = new MemoryRealm(); - MapVerifier mapVerifier = new MapVerifier(); - String[] users = configManager.get(confId).getTrimmedStrings("restapi.auth.users", "admin|admin|admin,user|user|user"); - if (users.length <= 1) { - throw new IllegalStateException("Check users definition of restapi.auth.users at nutch-site.xml "); - } - for (String userconf : users) { - String[] userDetail = userconf.split("\\|"); - if(userDetail.length != 3) { - LOG.error("Check user definition of restapi.auth.users at nutch-site.xml"); - throw new IllegalStateException("Check user definition of restapi.auth.users at nutch-site.xml "); - } - User user = new User(userDetail[0], userDetail[1]); - mapVerifier.getLocalSecrets().put(user.getIdentifier(), user.getSecret()); - realm.getUsers().add(user); - realm.map(user, Role.get(application, userDetail[2])); - LOG.info("User added: {}", userDetail[0]); - } - realm.setVerifier(mapVerifier); - return realm; - } - - /** - * Check for allowing only admin role - * - * @param securityContext to check role of logged-in user - */ - public static void allowOnlyAdmin(SecurityContext securityContext) { - if (securityContext.getAuthenticationScheme() != null - && !securityContext.isUserInRole(AuthorizationRoleEnum.ADMIN.toString())) { - throw new WebApplicationException(Response.status(Response.Status.FORBIDDEN) - .entity("User does not have required " + AuthorizationRoleEnum.ADMIN + " role!").build()); - } - } - -} http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/java/org/apache/nutch/api/security/SecurityUtils.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/nutch/api/security/SecurityUtils.java b/src/java/org/apache/nutch/api/security/SecurityUtils.java new file mode 100644 index 0000000..8fd56f9 --- /dev/null +++ b/src/java/org/apache/nutch/api/security/SecurityUtils.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package org.apache.nutch.api.security; + +import org.apache.nutch.api.ConfManager; +import org.restlet.ext.jaxrs.JaxRsApplication; +import org.restlet.security.MapVerifier; +import org.restlet.security.MemoryRealm; +import org.restlet.security.Role; +import org.restlet.security.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.SecurityContext; +import java.util.ArrayList; +import java.util.List; + +/** + * Utility class for security operations for NutchServer REST API. + * + */ +public final class SecurityUtils { + + private static final Logger LOG = LoggerFactory.getLogger(SecurityUtils.class); + + /** + * Private constructor to prevent instantiation + */ + private SecurityUtils() { + } + + /** + * Returns roles defined at {@link org.apache.nutch.api.security.AuthorizationRoleEnum} associated with + * {@link org.restlet.ext.jaxrs.JaxRsApplication} type application + * + * @param application {@link org.restlet.ext.jaxrs.JaxRsApplication} type application + * @return roles associated with given {@link org.restlet.ext.jaxrs.JaxRsApplication} type application + */ + public static List<Role> getRoles(JaxRsApplication application) { + List<Role> roles = new ArrayList<>(); + for (AuthorizationRoleEnum authorizationRole : AuthorizationRoleEnum.values()) { + roles.add(new Role(application, authorizationRole.toString())); + } + return roles; + } + + /** + * Constructs realm + * + * @param application {@link org.restlet.ext.jaxrs.JaxRsApplication }application + * @param configManager {@link org.apache.nutch.api.ConfManager} type config manager + * @param confId Configuration id to use from {@link org.apache.nutch.api.ConfManager} type config manager + * @return realm + */ + public static MemoryRealm constructRealm(JaxRsApplication application, ConfManager configManager, String confId){ + MemoryRealm realm = new MemoryRealm(); + MapVerifier mapVerifier = new MapVerifier(); + String[] users = configManager.get(confId).getTrimmedStrings("restapi.auth.users", "admin|admin|admin,user|user|user"); + if (users.length < 1) { + throw new IllegalStateException("Check users definition of restapi.auth.users at nutch-site.xml "); + } + for (String userconf : users) { + String[] userDetail = userconf.split("\\|"); + if(userDetail.length != 3) { + LOG.error("Check user definition of restapi.auth.users at nutch-site.xml"); + throw new IllegalStateException("Check user definition of restapi.auth.users at nutch-site.xml "); + } + User user = new User(userDetail[0], userDetail[1]); + mapVerifier.getLocalSecrets().put(user.getIdentifier(), user.getSecret()); + realm.getUsers().add(user); + realm.map(user, Role.get(application, userDetail[2])); + LOG.info("User added: {}", userDetail[0]); + } + realm.setVerifier(mapVerifier); + return realm; + } + + /** + * Check for allowing only admin role + * + * @param securityContext to check role of logged-in user + */ + public static void allowOnlyAdmin(SecurityContext securityContext) { + if (securityContext.getAuthenticationScheme() != null + && !securityContext.isUserInRole(AuthorizationRoleEnum.ADMIN.toString())) { + throw new WebApplicationException(Response.status(Response.Status.FORBIDDEN) + .entity("User does not have required " + AuthorizationRoleEnum.ADMIN + " role!").build()); + } + } + +} http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/test/nutch-site.xml ---------------------------------------------------------------------- diff --git a/src/test/nutch-site.xml b/src/test/nutch-site.xml index 4dbce0c..bdeb562 100644 --- a/src/test/nutch-site.xml +++ b/src/test/nutch-site.xml @@ -55,7 +55,7 @@ <value>nutch-ssl.keystore.jks</value> <description> Key store path for jks file. restapi.auth property should be set to SSL to use this property. - etc/nutch-ssl.keystore.jks is used for restapi.auth.ssl.storepath as default. + nutch-ssl.keystore.jks is used for restapi.auth.ssl.storepath as default. </description> </property> http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/test/nutch-ssl.keystore.jks ---------------------------------------------------------------------- diff --git a/src/test/nutch-ssl.keystore.jks b/src/test/nutch-ssl.keystore.jks index 9d0bd01..4fc4514 100644 Binary files a/src/test/nutch-ssl.keystore.jks and b/src/test/nutch-ssl.keystore.jks differ http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/test/nutch.cer ---------------------------------------------------------------------- diff --git a/src/test/nutch.cer b/src/test/nutch.cer new file mode 100644 index 0000000..a3317b1 Binary files /dev/null and b/src/test/nutch.cer differ http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/test/org/apache/nutch/api/AbstractNutchAPITestBase.java ---------------------------------------------------------------------- diff --git a/src/test/org/apache/nutch/api/AbstractNutchAPITestBase.java b/src/test/org/apache/nutch/api/AbstractNutchAPITestBase.java index 3cdff23..2e4e61c 100644 --- a/src/test/org/apache/nutch/api/AbstractNutchAPITestBase.java +++ b/src/test/org/apache/nutch/api/AbstractNutchAPITestBase.java @@ -27,7 +27,24 @@ import org.restlet.data.ChallengeScheme; import org.restlet.resource.ClientResource; import org.restlet.resource.ResourceException; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.FileNotFoundException; +import java.net.URL; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; /** * Base class for Nutc REST API tests. @@ -154,33 +171,89 @@ public abstract class AbstractNutchAPITestBase { return digestChallengeRequest; } - private void initializeSSLProperties() { - conf.set("restapi.auth.ssl.storepath", "src/test/nutch-ssl.keystore.jks"); + private void initializeSSLProperties() throws FileNotFoundException { + conf.set("restapi.auth.ssl.storepath", getResourcePath("nutch-ssl.keystore.jks")); conf.set("restapi.auth.ssl.storepass", "password"); conf.set("restapi.auth.ssl.keypass", "password"); } + private void loadCertificate() throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException { + String testTrustKeyStorePath = getResourcePath("testTrustKeyStore"); + File file = new File(testTrustKeyStorePath); + InputStream localCertIn = new FileInputStream(file); + + char[] password = "testpassword".toCharArray(); + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(localCertIn, password); + + localCertIn.close(); + + if (ks.containsAlias("nutch")) { + return; + } + + String cerFilePath = getResourcePath("nutch.cer"); + + InputStream certIn = new FileInputStream(cerFilePath); + BufferedInputStream bis = new BufferedInputStream(certIn); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + while (bis.available() > 0) { + Certificate cert = cf.generateCertificate(bis); + ks.setCertificateEntry("nutch", cert); + } + + certIn.close(); + OutputStream out = new FileOutputStream(file); + ks.store(out, password); + out.close(); + } + /** - * Starts the server with given authentication type + * Starts the server with given authentication type. + * Use {@link #startSSLServer()} for a {@link AuthenticationTypeEnum#SSL} authentication type server. * * @param authenticationType authentication type */ public void startServer(AuthenticationTypeEnum authenticationType) { conf.set("restapi.auth", authenticationType.toString()); - if(AuthenticationTypeEnum.SSL.equals(authenticationType)) { - initializeSSLProperties(); - } - RAMConfManager ramConfManager = new RAMConfManager(NutchConfiguration.getUUID(conf), conf); nutchServer = new NutchServer(ramConfManager, NutchConfiguration.getUUID(conf)); nutchServer.start(); } /** - * Stops server + * Starts the SSL server + * + */ + public void startSSLServer() throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException { + conf.set("restapi.auth", AuthenticationTypeEnum.SSL.toString()); + initializeSSLProperties(); + loadCertificate(); + startServer(AuthenticationTypeEnum.SSL); + } + + /** + * Stops the server */ public void stopServer() { nutchServer.stop(true); } + protected String getResourcePath(String fileName) throws FileNotFoundException { + URL resourceFilePath = this.getClass().getClassLoader().getResource(fileName); + if (resourceFilePath == null) { + throw new FileNotFoundException("Resource could not found: " + fileName); + } + return resourceFilePath.getPath(); + } + + protected void rollbackSystemProperty(String key, String initialValue){ + if (initialValue != null) { + System.setProperty(key, initialValue); + } else { + System.clearProperty(key); + } + } + } http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/test/org/apache/nutch/api/TestNutchAPI.java ---------------------------------------------------------------------- diff --git a/src/test/org/apache/nutch/api/TestNutchAPI.java b/src/test/org/apache/nutch/api/TestNutchAPI.java index 56aed92..a4a8fb8 100644 --- a/src/test/org/apache/nutch/api/TestNutchAPI.java +++ b/src/test/org/apache/nutch/api/TestNutchAPI.java @@ -18,10 +18,14 @@ package org.apache.nutch.api; import org.apache.nutch.api.security.AuthenticationTypeEnum; import org.junit.After; -import org.junit.Ignore; import org.junit.Test; import org.restlet.data.ChallengeScheme; +import java.io.IOException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; + /** * Test class for {@link org.apache.nutch.api.NutchServer} */ @@ -71,21 +75,26 @@ public class TestNutchAPI extends AbstractNutchAPITestBase { } /** - * Test SSL for invalid username/password pair, - * authorized username/password pair and insufficient privileged username/password pair + * Test SSL connection */ - @Ignore @Test - public void testSSL() { - startServer(AuthenticationTypeEnum.SSL); - //Check for an invalid username/password pair - testRequest(401, 8081, "xxx", "xxx"); + public void testSSL() throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException { + startSSLServer(); - //Check for an authorized username/password pair - testRequest(200, 8081, "admin", "admin"); + String defaultTrustStore = System.getProperty("javax.net.ssl.trustStore"); + String defaultTrustStoreType = System.getProperty("javax.net.ssl.trustStoreType"); + String defaultTrustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword"); + + System.setProperty("javax.net.ssl.trustStore" , getResourcePath("testTrustKeyStore")); + System.setProperty("javax.net.ssl.trustStoreType", "JKS"); + System.setProperty("javax.net.ssl.trustStorePassword", "testpassword"); + + testRequest(200, 8081); + + rollbackSystemProperty("javax.net.ssl.trustStore", defaultTrustStore); + rollbackSystemProperty("javax.net.ssl.trustStoreType", defaultTrustStoreType); + rollbackSystemProperty("javax.net.ssl.trustStorePassword", defaultTrustStorePassword); - //Check for an insufficient privileged username/password pair - testRequest(403, 8081, "user", "user"); } /** http://git-wip-us.apache.org/repos/asf/nutch/blob/75d846cf/src/test/testTrustKeyStore ---------------------------------------------------------------------- diff --git a/src/test/testTrustKeyStore b/src/test/testTrustKeyStore new file mode 100644 index 0000000..5fdd2fd Binary files /dev/null and b/src/test/testTrustKeyStore differ
