You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@archiva.apache.org by ma...@apache.org on 2017/03/10 22:51:07 UTC
[1/8] archiva-redback-core git commit: Implementing Header
verification for REST API calls
Repository: archiva-redback-core
Updated Branches:
refs/heads/master 78d822d14 -> d9146ce8a
Implementing Header verification for REST API calls
Implementing header verification techniques mentioned in:
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Verifying_Same_Origin_with_Standard_Headers
Project: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/commit/95f1b3e4
Tree: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/tree/95f1b3e4
Diff: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/diff/95f1b3e4
Branch: refs/heads/master
Commit: 95f1b3e4307fb7d407ad0cb83869af2564a1aae8
Parents: 78d822d
Author: Martin Stockhammer <ma...@apache.org>
Authored: Mon Jan 30 20:37:15 2017 +0100
Committer: Martin Stockhammer <ma...@apache.org>
Committed: Mon Jan 30 20:37:15 2017 +0100
----------------------------------------------------------------------
.../archiva/redback/config-defaults.properties | 6 +
.../RequestValidationInterceptor.java | 213 +++++++++++++++++++
.../main/resources/META-INF/spring-context.xml | 1 +
.../rest/services/AbstractRestServicesTest.java | 5 +
.../RequestValidationInterceptorTest.java | 162 ++++++++++++++
.../redback/rest/services/UserServiceTest.java | 3 +
.../mock/MockContainerRequestContext.java | 175 +++++++++++++++
.../services/mock/MockUserConfiguration.java | 105 +++++++++
8 files changed, 670 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/95f1b3e4/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties
----------------------------------------------------------------------
diff --git a/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties b/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties
index 402aaf9..9fa02ce 100644
--- a/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties
+++ b/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties
@@ -133,4 +133,10 @@ ldap.bind.authenticator.enabled=false
user.manager.impl=jdo
+# REST security settings
+# REST base url is for avoiding CSRF attacks
+# If it is not set or empty it tries to determine the base url automatically
+rest.baseUrl=
+# If true, requests without Origin or Referer Header are denied
+rest.csrf.absentorigin.deny=true
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/95f1b3e4/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java
new file mode 100644
index 0000000..4300baf
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java
@@ -0,0 +1,213 @@
+package org.apache.archiva.redback.rest.services.interceptors;
+/*
+ * 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.
+ */
+
+
+import org.apache.archiva.redback.configuration.UserConfiguration;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Message;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Created by Martin Stockhammer on 19.01.17.
+ *
+ * This interceptor tries to check if requests come from a valid origin and
+ * are not generated by another site on behalf of the real client.
+ *
+ * We are using some of the techniques mentioned in
+ * https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
+ *
+ * Try to find Origin and Referer of the request.
+ * Match them to the target address, that may be either statically configured or is determined
+ * by the Host/X-Forwarded-For Header.
+ *
+ *
+ */
+@Provider
+@Service( "requestValidationInterceptor#rest" )
+public class RequestValidationInterceptor extends AbstractInterceptor implements ContainerRequestFilter {
+
+
+ private static final String X_FORWARDED_PROTO = "X-Forwarded-Proto";
+ private static final String X_FORWARDED_HOST = "X-Forwarded-Host";
+ private static final String ORIGIN = "Origin";
+ private static final String REFERER = "Referer";
+ private static final String CFG_REST_BASE_URL = "rest.baseUrl";
+ private static final String CFG_REST_CSRF_ABSENTORIGIN_DENY = "rest.csrffilter.absentorigin.deny";
+ private static final String CFG_REST_CSRF_ENABLED = "rest.csrffilter.enabled";
+
+ private final Logger log = LoggerFactory.getLogger( getClass() );
+
+ private boolean enabled = true;
+ private boolean useStaticUrl = false;
+ private boolean denyAbsentHeaders = true;
+ private URL baseUrl;
+ private HttpServletRequest httpRequest = null;
+
+ private UserConfiguration config;
+
+ @Inject
+ public RequestValidationInterceptor(@Named( value = "userConfiguration#default" )
+ UserConfiguration config) {
+ this.config = config;
+ }
+
+ @PostConstruct
+ public void init() {
+ String baseUrlStr = config.getString(CFG_REST_BASE_URL, "");
+ if (!"".equals(baseUrlStr.trim())) {
+ try {
+ baseUrl = new URL(baseUrlStr);
+ useStaticUrl = true;
+ } catch (MalformedURLException ex) {
+ log.error("Configured baseUrl (rest.baseUrl={}) is invalid. Message: {}", baseUrlStr, ex.getMessage());
+ }
+ } else {
+ useStaticUrl = false;
+ }
+ denyAbsentHeaders = config.getBoolean(CFG_REST_CSRF_ABSENTORIGIN_DENY,true);
+ enabled = config.getBoolean(CFG_REST_CSRF_ENABLED,true);
+ if (!enabled) {
+ log.info("CSRF Filter is disabled by configuration");
+ }
+ }
+
+ @Override
+ public void filter(ContainerRequestContext containerRequestContext) throws IOException {
+ if (enabled) {
+ HttpServletRequest request = getRequest();
+ URL targetUrl = getTargetUrl(request);
+ if (targetUrl == null) {
+ log.error("Could not verify target URL.");
+ containerRequestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
+ }
+ if (!checkSourceRequestHeader(targetUrl, request)) {
+ log.warn("HTTP Header check failed. Assuming CSRF attack.");
+ containerRequestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
+ }
+ }
+ }
+
+ private HttpServletRequest getRequest() {
+ if (httpRequest!=null) {
+ return httpRequest;
+ } else {
+ Message message = JAXRSUtils.getCurrentMessage();
+ return getHttpServletRequest(message);
+ }
+ }
+
+ private URL getTargetUrl(HttpServletRequest request) {
+ if (useStaticUrl) {
+ return baseUrl;
+ } else {
+ URL requestUrl;
+ try {
+ requestUrl = new URL(request.getRequestURL().toString());
+ } catch (MalformedURLException ex) {
+ log.error("Bad Request URL {}, Message: {}", request.getRequestURL(), ex.getMessage());
+ return null;
+ }
+ String xforwarded = request.getHeader(X_FORWARDED_HOST);
+ String xforwardedProto = request.getHeader(X_FORWARDED_PROTO);
+ if (xforwardedProto==null) {
+ xforwardedProto=requestUrl.getProtocol();
+ }
+ if (xforwarded!=null) {
+ try {
+ return new URL(xforwardedProto+"://"+xforwarded);
+ } catch (MalformedURLException ex) {
+ log.warn("X-Forwarded-Host Header is malformed: {}", ex.getMessage());
+ }
+ }
+ return requestUrl;
+ }
+ }
+
+ private int getPort(final URL url) {
+ return url.getPort() > 0 ? url.getPort() : url.getDefaultPort();
+ }
+
+ private boolean checkSourceRequestHeader(final URL targetUrl, final HttpServletRequest request) {
+ boolean headerFound=false;
+ String origin = request.getHeader(ORIGIN);
+ if (origin!=null) {
+ try {
+ URL originUrl = new URL(origin);
+ headerFound=true;
+ log.debug("Origin Header URL found: {}", originUrl);
+ if (!targetUrl.getProtocol().equals(originUrl.getProtocol())) {
+ log.warn("Origin Header Protocol does not match originUrl={}, targetUrl={}", originUrl, targetUrl);
+ return false;
+ }
+ if (!targetUrl.getHost().equals(originUrl.getHost())) {
+ log.warn("Origin Header Host does not match originUrl={}, targetUrl={}",originUrl,targetUrl);
+ return false;
+ }
+ int originPort = getPort(originUrl);
+ int targetPort = getPort(targetUrl);
+ if (targetPort != originPort) {
+ log.warn("Origin Header Port does not match originUrl={}, targetUrl={}",originUrl,targetUrl);
+ return false;
+ }
+ } catch (MalformedURLException ex) {
+ log.warn("Bad URL in Origin HTTP-Header: {}. Message: {}",origin, ex.getMessage());
+ return false;
+ }
+ }
+ String referer = request.getHeader(REFERER);
+ if (referer!=null) {
+ try {
+ URL refererUrl = new URL(referer);
+ headerFound=true;
+ log.debug("Referer Header URL found: {}",refererUrl);
+ if (!targetUrl.getHost().equals(refererUrl.getHost())) {
+ log.warn("Referer Header Host does not match refererUrl={}, targetUrl={}",refererUrl,targetUrl);
+ return false;
+ }
+ } catch (MalformedURLException ex) {
+ log.warn("Bad URL in Referer HTTP-Header: {}, Message: {}", referer, ex.getMessage());
+ return false;
+ }
+ }
+ if (!headerFound && denyAbsentHeaders) {
+ log.warn("Neither Origin nor Referer header found. Request is denied.");
+ return false;
+ }
+ return true;
+ }
+
+ public void setHttpRequest(HttpServletRequest request) {
+ this.httpRequest = request;
+ }
+}
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/95f1b3e4/redback-integrations/redback-rest/redback-rest-services/src/main/resources/META-INF/spring-context.xml
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/resources/META-INF/spring-context.xml b/redback-integrations/redback-rest/redback-rest-services/src/main/resources/META-INF/spring-context.xml
index 98188f4..4e7b815 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/main/resources/META-INF/spring-context.xml
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/resources/META-INF/spring-context.xml
@@ -65,6 +65,7 @@
<ref bean="permissionInterceptor#rest"/>
<ref bean="redbackServiceExceptionMapper"/>
<ref bean="passwordRuleViolationExceptionMapper"/>
+ <ref bean="requestValidationInterceptor#rest" />
</jaxrs:providers>
</jaxrs:server>
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/95f1b3e4/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/AbstractRestServicesTest.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/AbstractRestServicesTest.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/AbstractRestServicesTest.java
index 806f322..0cab072 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/AbstractRestServicesTest.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/AbstractRestServicesTest.java
@@ -175,6 +175,7 @@ public abstract class AbstractRestServicesTest
{
WebClient.client( service ).header( "Authorization", authzHeader );
}
+ WebClient.client(service).header("Referer","http://localhost");
WebClient.client( service ).accept( MediaType.APPLICATION_JSON_TYPE );
WebClient.client( service ).type( MediaType.APPLICATION_JSON_TYPE );
@@ -196,6 +197,8 @@ public abstract class AbstractRestServicesTest
{
WebClient.client( service ).header( "Authorization", authzHeader );
}
+ WebClient.client( service ).header("Referer","http://localhost/");
+
WebClient.client( service ).accept( MediaType.APPLICATION_JSON_TYPE );
WebClient.client( service ).type( MediaType.APPLICATION_JSON_TYPE );
@@ -216,6 +219,7 @@ public abstract class AbstractRestServicesTest
{
WebClient.client( service ).header( "Authorization", authzHeader );
}
+ WebClient.client( service ).header("Referer","http://localhost/");
WebClient.client( service ).accept( MediaType.APPLICATION_JSON_TYPE );
WebClient.client( service ).type( MediaType.APPLICATION_JSON_TYPE );
@@ -238,6 +242,7 @@ public abstract class AbstractRestServicesTest
{
WebClient.client( service ).header( "Authorization", authzHeader );
}
+ WebClient.client( service ).header("Referer","http://localhost/");
WebClient.client( service ).accept( MediaType.APPLICATION_JSON_TYPE );
WebClient.client( service ).type( MediaType.APPLICATION_JSON_TYPE );
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/95f1b3e4/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/RequestValidationInterceptorTest.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/RequestValidationInterceptorTest.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/RequestValidationInterceptorTest.java
new file mode 100644
index 0000000..c88492a
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/RequestValidationInterceptorTest.java
@@ -0,0 +1,162 @@
+package org.apache.archiva.redback.rest.services;
+/*
+ * 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.
+ */
+
+
+import junit.framework.TestCase;
+import org.apache.archiva.redback.configuration.UserConfigurationException;
+import org.apache.archiva.redback.rest.services.interceptors.RequestValidationInterceptor;
+import org.apache.archiva.redback.rest.services.mock.MockContainerRequestContext;
+import org.apache.archiva.redback.rest.services.mock.MockUserConfiguration;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.springframework.mock.web.MockHttpServletRequest;
+
+import java.io.IOException;
+
+
+
+/**
+ * Created by Martin Stockhammer on 21.01.17.
+ *
+ * Unit Test for RequestValidationInterceptor.
+ *
+ */
+@RunWith(JUnit4.class)
+public class RequestValidationInterceptorTest extends TestCase {
+
+
+
+ @Test
+ public void validateRequestWithoutHeader() throws UserConfigurationException, IOException {
+ MockUserConfiguration cfg = new MockUserConfiguration();
+ RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ interceptor.setHttpRequest(request);
+ interceptor.init();
+ MockContainerRequestContext ctx = new MockContainerRequestContext();
+ interceptor.filter(ctx);
+ assertTrue(ctx.isAborted());
+ }
+
+ @Test
+ public void validateRequestWithOrigin() throws UserConfigurationException, IOException {
+ MockUserConfiguration cfg = new MockUserConfiguration();
+ RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
+ MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
+ request.setServerName("test.archiva.org");
+ request.addHeader("Origin","http://test.archiva.org/myservlet");
+ interceptor.setHttpRequest(request);
+ interceptor.init();
+ MockContainerRequestContext ctx = new MockContainerRequestContext();
+ interceptor.filter(ctx);
+ assertFalse(ctx.isAborted());
+ }
+
+ @Test
+ public void validateRequestWithBadOrigin() throws UserConfigurationException, IOException {
+ MockUserConfiguration cfg = new MockUserConfiguration();
+ RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
+ MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
+ request.setServerName("test.archiva.org");
+ request.addHeader("Origin","http://test2.archiva.org/myservlet");
+ interceptor.setHttpRequest(request);
+ interceptor.init();
+ MockContainerRequestContext ctx = new MockContainerRequestContext();
+ interceptor.filter(ctx);
+ assertTrue(ctx.isAborted());
+ }
+
+ @Test
+ public void validateRequestWithReferer() throws UserConfigurationException, IOException {
+ MockUserConfiguration cfg = new MockUserConfiguration();
+ RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
+ MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
+ request.setServerName("test.archiva.org");
+ request.addHeader("Referer","http://test.archiva.org/myservlet2");
+ interceptor.setHttpRequest(request);
+ interceptor.init();
+ MockContainerRequestContext ctx = new MockContainerRequestContext();
+ interceptor.filter(ctx);
+ assertFalse(ctx.isAborted());
+ }
+
+ @Test
+ public void validateRequestWithBadReferer() throws UserConfigurationException, IOException {
+ MockUserConfiguration cfg = new MockUserConfiguration();
+ RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
+ MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
+ request.setServerName("test.archiva.org");
+ request.addHeader("Referer","http://test3.archiva.org/myservlet2");
+ interceptor.setHttpRequest(request);
+ interceptor.init();
+ MockContainerRequestContext ctx = new MockContainerRequestContext();
+ interceptor.filter(ctx);
+ assertTrue(ctx.isAborted());
+ }
+
+ @Test
+ public void validateRequestWithOriginAndReferer() throws UserConfigurationException, IOException {
+ MockUserConfiguration cfg = new MockUserConfiguration();
+ RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
+ MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
+ request.setServerName("test.archiva.org");
+ request.addHeader("Origin","http://test.archiva.org/myservlet");
+ request.addHeader("Referer","http://test.archiva.org/myservlet2");
+ interceptor.setHttpRequest(request);
+ interceptor.init();
+ MockContainerRequestContext ctx = new MockContainerRequestContext();
+ interceptor.filter(ctx);
+ assertFalse(ctx.isAborted());
+ }
+
+
+ @Test
+ public void validateRequestWithOriginAndStaticUrl() throws UserConfigurationException, IOException {
+ MockUserConfiguration cfg = new MockUserConfiguration();
+ cfg.addValue("rest.baseUrl","http://test.archiva.org");
+ RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
+ MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
+ request.setServerName("test4.archiva.org");
+ request.addHeader("Origin","http://test.archiva.org/myservlet");
+ interceptor.setHttpRequest(request);
+ interceptor.init();
+ MockContainerRequestContext ctx = new MockContainerRequestContext();
+ interceptor.filter(ctx);
+ assertFalse(ctx.isAborted());
+ }
+
+ @Test
+ public void validateRequestWithBadOriginAndStaticUrl() throws UserConfigurationException, IOException {
+ MockUserConfiguration cfg = new MockUserConfiguration();
+ cfg.addValue("rest.baseUrl","http://mytest.archiva.org");
+ RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
+ MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
+ request.setServerName("mytest.archiva.org");
+ request.addHeader("Origin","http://test.archiva.org/myservlet");
+ interceptor.setHttpRequest(request);
+ interceptor.init();
+ MockContainerRequestContext ctx = new MockContainerRequestContext();
+ interceptor.filter(ctx);
+ assertTrue(ctx.isAborted());
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/95f1b3e4/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/UserServiceTest.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/UserServiceTest.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/UserServiceTest.java
index b5a80d2..a1d6f57 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/UserServiceTest.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/UserServiceTest.java
@@ -93,6 +93,9 @@ public class UserServiceTest
throws Exception
{
+ UserService userService = getUserService();
+ WebClient.client( userService ).header( "Origin", "http://localhost/myrequest");
+
try
{
getFakeCreateAdminService().testAuthzWithoutKarmasNeededButAuthz();
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/95f1b3e4/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/mock/MockContainerRequestContext.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/mock/MockContainerRequestContext.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/mock/MockContainerRequestContext.java
new file mode 100644
index 0000000..ae98a8f
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/mock/MockContainerRequestContext.java
@@ -0,0 +1,175 @@
+package org.apache.archiva.redback.rest.services.mock;
+/*
+ * 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.
+ */
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Created by Martin Stockhammer on 21.01.17.
+ *
+ * RequestContext used in unit tests.
+ */
+public class MockContainerRequestContext implements ContainerRequestContext {
+
+ private boolean aborted = false;
+
+ @Override
+ public Object getProperty(String s) {
+ return null;
+ }
+
+ @Override
+ public Collection<String> getPropertyNames() {
+ return null;
+ }
+
+ @Override
+ public void setProperty(String s, Object o) {
+
+ }
+
+ @Override
+ public void removeProperty(String s) {
+
+ }
+
+ @Override
+ public UriInfo getUriInfo() {
+ return null;
+ }
+
+ @Override
+ public void setRequestUri(URI uri) {
+
+ }
+
+ @Override
+ public void setRequestUri(URI uri, URI uri1) {
+
+ }
+
+ @Override
+ public Request getRequest() {
+ return null;
+ }
+
+
+ @Override
+ public String getMethod() {
+ return null;
+ }
+
+ @Override
+ public void setMethod(String s) {
+
+ }
+
+ @Override
+ public MultivaluedMap<String, String> getHeaders() {
+ return null;
+ }
+
+ @Override
+ public String getHeaderString(String s) {
+ return null;
+ }
+
+ @Override
+ public Date getDate() {
+ return null;
+ }
+
+ @Override
+ public Locale getLanguage() {
+ return null;
+ }
+
+ @Override
+ public int getLength() {
+ return 0;
+ }
+
+ @Override
+ public MediaType getMediaType() {
+ return null;
+ }
+
+ @Override
+ public List<MediaType> getAcceptableMediaTypes() {
+ return null;
+ }
+
+ @Override
+ public List<Locale> getAcceptableLanguages() {
+ return null;
+ }
+
+ @Override
+ public Map<String, Cookie> getCookies() {
+ return null;
+ }
+
+ @Override
+ public boolean hasEntity() {
+ return false;
+ }
+
+ @Override
+ public InputStream getEntityStream() {
+ return null;
+ }
+
+ @Override
+ public void setEntityStream(InputStream inputStream) {
+
+ }
+
+ @Override
+ public SecurityContext getSecurityContext() {
+ return null;
+ }
+
+ @Override
+ public void setSecurityContext(SecurityContext securityContext) {
+
+ }
+
+ @Override
+ public void abortWith(Response response) {
+ this.aborted=true;
+ }
+
+ public boolean isAborted() {
+ return aborted;
+ }
+}
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/95f1b3e4/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/mock/MockUserConfiguration.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/mock/MockUserConfiguration.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/mock/MockUserConfiguration.java
new file mode 100644
index 0000000..6eaaed8
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/mock/MockUserConfiguration.java
@@ -0,0 +1,105 @@
+package org.apache.archiva.redback.rest.services.mock;
+/*
+ * 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.
+ */
+
+
+import org.apache.archiva.redback.configuration.UserConfiguration;
+import org.apache.archiva.redback.configuration.UserConfigurationException;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by Martin Stockhammer on 21.01.17.
+ *
+ * User configuration implementation to be used in unit tests.
+ *
+ */
+public class MockUserConfiguration implements UserConfiguration {
+
+ private Map<String, String> values = new java.util.HashMap<String,String>();
+
+ @SuppressWarnings("SameParameterValue")
+ public void addValue(String key, String value) {
+ values.put(key,value);
+ }
+
+ @Override
+ public void initialize() throws UserConfigurationException {
+
+ }
+
+ @Override
+ public String getString(String key) {
+ return values.get(key);
+ }
+
+ @Override
+ public String getString(String key, String defaultValue) {
+ if (values.containsKey(key)) {
+ return values.get(key);
+ } else {
+ return defaultValue;
+ }
+ }
+
+ @Override
+ public int getInt(String key) {
+ return getInt(key, -1);
+ }
+
+ @Override
+ public int getInt(String key, int defaultValue) {
+ if (values.containsKey(key)) {
+ return Integer.parseInt(values.get(key));
+ } else {
+ return defaultValue;
+ }
+ }
+
+ @Override
+ public boolean getBoolean(String key) {
+ return getBoolean(key, false);
+ }
+
+ @Override
+ public boolean getBoolean(String key, boolean defaultValue) {
+ if (values.containsKey(key)) {
+ return Boolean.parseBoolean(values.get(key));
+ } else {
+ return defaultValue;
+ }
+ }
+
+ @Override
+ public List<String> getList(String key) {
+ return null;
+ }
+
+ @Override
+ public String getConcatenatedList(String key, String defaultValue) {
+ return null;
+ }
+
+ @Override
+ public Collection<String> getKeys() {
+ return values.keySet();
+ }
+}
[5/8] archiva-redback-core git commit: Adding validationToken to
toString method
Posted by ma...@apache.org.
Adding validationToken to toString method
Project: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/commit/461b7e98
Tree: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/tree/461b7e98
Diff: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/diff/461b7e98
Branch: refs/heads/master
Commit: 461b7e98f8648518f4dc9e1759aad0dcf3e18fc9
Parents: a6ceb38
Author: Martin Stockhammer <ma...@apache.org>
Authored: Sun Feb 12 17:12:41 2017 +0100
Committer: Martin Stockhammer <ma...@apache.org>
Committed: Sun Feb 12 17:12:41 2017 +0100
----------------------------------------------------------------------
.../main/java/org/apache/archiva/redback/rest/api/model/User.java | 1 +
1 file changed, 1 insertion(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/461b7e98/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/User.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/User.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/User.java
index 2f3e93e..66287df 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/User.java
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/User.java
@@ -308,6 +308,7 @@ public class User
", assignedRoles=" + assignedRoles +
", readOnly=" + readOnly +
", userManagerId='" + userManagerId + '\'' +
+ ", validationToken='" + validationToken + '\'' +
'}';
}
[6/8] archiva-redback-core git commit: Adding log statements
Posted by ma...@apache.org.
Adding log statements
Project: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/commit/98c98b39
Tree: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/tree/98c98b39
Diff: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/diff/98c98b39
Branch: refs/heads/master
Commit: 98c98b3979f35aef0435ca779e5bf73ed22ab3af
Parents: 461b7e9
Author: Martin Stockhammer <ma...@apache.org>
Authored: Sun Feb 12 18:44:55 2017 +0100
Committer: Martin Stockhammer <ma...@apache.org>
Committed: Sun Feb 12 18:44:55 2017 +0100
----------------------------------------------------------------------
.../archiva/redback/rest/services/DefaultLoginService.java | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/98c98b39/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLoginService.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLoginService.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLoginService.java
index 3bc30bb..33160b4 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLoginService.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLoginService.java
@@ -141,12 +141,15 @@ public class DefaultLoginService
String userName = loginRequest.getUsername(), password = loginRequest.getPassword();
PasswordBasedAuthenticationDataSource authDataSource =
new PasswordBasedAuthenticationDataSource( userName, password );
+ log.debug("Login for {}",userName);
try
{
SecuritySession securitySession = securitySystem.authenticate( authDataSource );
+ log.debug("Security session {}", securitySession);
if ( securitySession.getAuthenticationResult().isAuthenticated() )
{
org.apache.archiva.redback.users.User user = securitySession.getUser();
+ log.debug("user {} authenticated", user.getUsername());
if ( !user.isValidated() )
{
log.info( "user {} not validated", user.getUsername() );
@@ -156,7 +159,10 @@ public class DefaultLoginService
restUser.setReadOnly( securitySystem.userManagerReadOnly() );
// validationToken only set during login
try {
- restUser.setValidationToken(securitySystem.getTokenManager().encryptToken(user.getUsername(), tokenLifetime));
+ String validationToken = securitySystem.getTokenManager().encryptToken(user.getUsername(), tokenLifetime);
+ restUser.setValidationToken(validationToken);
+ log.debug("Validation Token set {}",validationToken);
+
} catch (EncryptionFailedException e) {
log.error("Validation token could not be created "+e.getMessage());
}
[7/8] archiva-redback-core git commit: Adding token validation to
interceptor
Posted by ma...@apache.org.
Adding token validation to interceptor
Project: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/commit/6a6b7bb9
Tree: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/tree/6a6b7bb9
Diff: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/diff/6a6b7bb9
Branch: refs/heads/master
Commit: 6a6b7bb93993aaca72bba723d094b76c578b05c3
Parents: 98c98b3
Author: Martin Stockhammer <ma...@apache.org>
Authored: Sun Feb 12 20:47:14 2017 +0100
Committer: Martin Stockhammer <ma...@apache.org>
Committed: Sun Feb 12 20:47:14 2017 +0100
----------------------------------------------------------------------
.../archiva/redback/config-defaults.properties | 6 +-
.../RequestValidationInterceptor.java | 103 ++++++++++++++++++-
.../RequestValidationInterceptorTest.java | 20 +++-
3 files changed, 124 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/6a6b7bb9/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties
----------------------------------------------------------------------
diff --git a/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties b/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties
index 9fa02ce..4b3e6fa 100644
--- a/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties
+++ b/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties
@@ -136,7 +136,11 @@ user.manager.impl=jdo
# REST security settings
# REST base url is for avoiding CSRF attacks
+# Enable CSRF filtering
+rest.csrffilter.enabled=true
# If it is not set or empty it tries to determine the base url automatically
rest.baseUrl=
# If true, requests without Origin or Referer Header are denied
-rest.csrf.absentorigin.deny=true
\ No newline at end of file
+rest.csrffilter.absentorigin.deny=true
+# If true, the validation of the CSRF tokens will be disabled
+rest.csrffilter.disableTokenValidation=false
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/6a6b7bb9/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java
index 182d23a..a9afede 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java
@@ -19,7 +19,17 @@ package org.apache.archiva.redback.rest.services.interceptors;
*/
+import org.apache.archiva.redback.authentication.AuthenticationException;
+import org.apache.archiva.redback.authentication.AuthenticationResult;
+import org.apache.archiva.redback.authentication.InvalidTokenException;
+import org.apache.archiva.redback.authentication.TokenData;
+import org.apache.archiva.redback.authentication.TokenManager;
+import org.apache.archiva.redback.authorization.RedbackAuthorization;
import org.apache.archiva.redback.configuration.UserConfiguration;
+import org.apache.archiva.redback.integration.filter.authentication.basic.HttpBasicAuthentication;
+import org.apache.archiva.redback.policy.AccountLockedException;
+import org.apache.archiva.redback.policy.MustChangePasswordException;
+import org.apache.archiva.redback.users.User;
import org.apache.cxf.jaxrs.utils.JAXRSUtils;
import org.apache.cxf.message.Message;
import org.slf4j.Logger;
@@ -60,15 +70,18 @@ public class RequestValidationInterceptor extends AbstractInterceptor implements
private static final String X_FORWARDED_PROTO = "X-Forwarded-Proto";
private static final String X_FORWARDED_HOST = "X-Forwarded-Host";
+ private static final String X_XSRF_TOKEN = "X-XSRF-TOKEN";
private static final String ORIGIN = "Origin";
private static final String REFERER = "Referer";
- private static final String CFG_REST_BASE_URL = "rest.baseUrl";
- private static final String CFG_REST_CSRF_ABSENTORIGIN_DENY = "rest.csrffilter.absentorigin.deny";
- private static final String CFG_REST_CSRF_ENABLED = "rest.csrffilter.enabled";
+ public static final String CFG_REST_BASE_URL = "rest.baseUrl";
+ public static final String CFG_REST_CSRF_ABSENTORIGIN_DENY = "rest.csrffilter.absentorigin.deny";
+ public static final String CFG_REST_CSRF_ENABLED = "rest.csrffilter.enabled";
+ public static final String CFG_REST_CSRF_DISABLE_TOKEN_VALIDATION = "rest.csrffilter.disableTokenValidation";
private final Logger log = LoggerFactory.getLogger( getClass() );
private boolean enabled = true;
+ private boolean checkToken = true;
private boolean useStaticUrl = false;
private boolean denyAbsentHeaders = true;
private URL baseUrl;
@@ -77,6 +90,14 @@ public class RequestValidationInterceptor extends AbstractInterceptor implements
private UserConfiguration config;
@Inject
+ @Named( value = "httpAuthenticator#basic" )
+ private HttpBasicAuthentication httpAuthenticator;
+
+ @Inject
+ @Named( value = "tokenManager#default")
+ TokenManager tokenManager;
+
+ @Inject
public RequestValidationInterceptor(@Named( value = "userConfiguration#default" )
UserConfiguration config) {
this.config = config;
@@ -100,6 +121,7 @@ public class RequestValidationInterceptor extends AbstractInterceptor implements
if (!enabled) {
log.info("CSRF Filter is disabled by configuration");
}
+ checkToken = !config.getBoolean(CFG_REST_CSRF_DISABLE_TOKEN_VALIDATION, false);
}
@Override
@@ -110,14 +132,60 @@ public class RequestValidationInterceptor extends AbstractInterceptor implements
if (targetUrl == null) {
log.error("Could not verify target URL.");
containerRequestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
+ return;
}
if (!checkSourceRequestHeader(targetUrl, request)) {
log.warn("HTTP Header check failed. Assuming CSRF attack.");
containerRequestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
+ return;
+ }
+
+ if (checkToken) {
+ checkValidationToken(containerRequestContext, request);
}
}
}
+ private void checkValidationToken(ContainerRequestContext containerRequestContext, HttpServletRequest request) {
+ Message message = JAXRSUtils.getCurrentMessage();
+ RedbackAuthorization redbackAuthorization = getRedbackAuthorization(message);
+ // We check only services that are restricted
+ if (!redbackAuthorization.noRestriction()) {
+ String tokenString = request.getHeader(X_XSRF_TOKEN);
+ if (tokenString==null || tokenString.length()==0) {
+ log.warn("No validation token header found: {}",X_XSRF_TOKEN);
+ containerRequestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
+ return;
+ }
+
+ try {
+ TokenData td = tokenManager.decryptToken(tokenString);
+ AuthenticationResult auth = getAuthenticationResult(message, request);
+ if (auth==null) {
+ log.error("Not authentication data found");
+ containerRequestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
+ return;
+ }
+ User loggedIn = auth.getUser();
+ if (loggedIn==null) {
+ log.error("User not logged in");
+ containerRequestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
+ return;
+ }
+ String username = loggedIn.getUsername();
+ if (!td.isValid() || !td.getUser().equals(username)) {
+ log.error("Invalid data in validation token header {} for user {}: isValid={}, username={}",
+ X_XSRF_TOKEN, username, td.isValid(), td.getUser());
+ containerRequestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
+ }
+ } catch (InvalidTokenException e) {
+ log.error("Token validation failed {}", e.getMessage());
+ containerRequestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
+ }
+ }
+ log.debug("Token validated");
+ }
+
private HttpServletRequest getRequest() {
if (httpRequest!=null) {
return httpRequest;
@@ -215,4 +283,33 @@ public class RequestValidationInterceptor extends AbstractInterceptor implements
public void setHttpRequest(HttpServletRequest request) {
this.httpRequest = request;
}
+
+ private AuthenticationResult getAuthenticationResult(Message message, HttpServletRequest request) {
+ AuthenticationResult authenticationResult = message.get(AuthenticationResult.class);
+
+ log.debug("authenticationResult from message: {}", authenticationResult);
+ if ( authenticationResult == null )
+ {
+ try
+ {
+ authenticationResult =
+ httpAuthenticator.getAuthenticationResult( request, getHttpServletResponse( message ) );
+
+ log.debug( "authenticationResult from request: {}", authenticationResult );
+ }
+ catch ( AuthenticationException e )
+ {
+ log.debug( "failed to authenticate for path {}", message.get( Message.REQUEST_URI ) );
+ }
+ catch ( AccountLockedException e )
+ {
+ log.debug( "account locked for path {}", message.get( Message.REQUEST_URI ) );
+ }
+ catch ( MustChangePasswordException e )
+ {
+ log.debug( "must change password for path {}", message.get( Message.REQUEST_URI ) );
+ }
+ }
+ return authenticationResult;
+ }
}
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/6a6b7bb9/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/RequestValidationInterceptorTest.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/RequestValidationInterceptorTest.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/RequestValidationInterceptorTest.java
index c88492a..27ab531 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/RequestValidationInterceptorTest.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/RequestValidationInterceptorTest.java
@@ -20,10 +20,12 @@ package org.apache.archiva.redback.rest.services;
import junit.framework.TestCase;
+import org.apache.archiva.redback.authentication.TokenManager;
import org.apache.archiva.redback.configuration.UserConfigurationException;
import org.apache.archiva.redback.rest.services.interceptors.RequestValidationInterceptor;
import org.apache.archiva.redback.rest.services.mock.MockContainerRequestContext;
import org.apache.archiva.redback.rest.services.mock.MockUserConfiguration;
+import org.apache.archiva.redback.system.SecuritySystem;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -36,7 +38,7 @@ import java.io.IOException;
/**
* Created by Martin Stockhammer on 21.01.17.
*
- * Unit Test for RequestValidationInterceptor.
+ * Unit Test for RequestValidationInterceptor. The unit tests are all without token validation.
*
*/
@RunWith(JUnit4.class)
@@ -46,7 +48,9 @@ public class RequestValidationInterceptorTest extends TestCase {
@Test
public void validateRequestWithoutHeader() throws UserConfigurationException, IOException {
+ TokenManager tm = new TokenManager();
MockUserConfiguration cfg = new MockUserConfiguration();
+ cfg.addValue(RequestValidationInterceptor.CFG_REST_CSRF_DISABLE_TOKEN_VALIDATION,"true");
RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
MockHttpServletRequest request = new MockHttpServletRequest();
interceptor.setHttpRequest(request);
@@ -58,7 +62,9 @@ public class RequestValidationInterceptorTest extends TestCase {
@Test
public void validateRequestWithOrigin() throws UserConfigurationException, IOException {
+ TokenManager tm = new TokenManager();
MockUserConfiguration cfg = new MockUserConfiguration();
+ cfg.addValue(RequestValidationInterceptor.CFG_REST_CSRF_DISABLE_TOKEN_VALIDATION,"true");
RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
request.setServerName("test.archiva.org");
@@ -72,7 +78,9 @@ public class RequestValidationInterceptorTest extends TestCase {
@Test
public void validateRequestWithBadOrigin() throws UserConfigurationException, IOException {
+ TokenManager tm = new TokenManager();
MockUserConfiguration cfg = new MockUserConfiguration();
+ cfg.addValue(RequestValidationInterceptor.CFG_REST_CSRF_DISABLE_TOKEN_VALIDATION,"true");
RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
request.setServerName("test.archiva.org");
@@ -86,7 +94,9 @@ public class RequestValidationInterceptorTest extends TestCase {
@Test
public void validateRequestWithReferer() throws UserConfigurationException, IOException {
+ TokenManager tm = new TokenManager();
MockUserConfiguration cfg = new MockUserConfiguration();
+ cfg.addValue(RequestValidationInterceptor.CFG_REST_CSRF_DISABLE_TOKEN_VALIDATION,"true");
RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
request.setServerName("test.archiva.org");
@@ -100,7 +110,9 @@ public class RequestValidationInterceptorTest extends TestCase {
@Test
public void validateRequestWithBadReferer() throws UserConfigurationException, IOException {
+ TokenManager tm = new TokenManager();
MockUserConfiguration cfg = new MockUserConfiguration();
+ cfg.addValue(RequestValidationInterceptor.CFG_REST_CSRF_DISABLE_TOKEN_VALIDATION,"true");
RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
request.setServerName("test.archiva.org");
@@ -114,7 +126,9 @@ public class RequestValidationInterceptorTest extends TestCase {
@Test
public void validateRequestWithOriginAndReferer() throws UserConfigurationException, IOException {
+ TokenManager tm = new TokenManager();
MockUserConfiguration cfg = new MockUserConfiguration();
+ cfg.addValue(RequestValidationInterceptor.CFG_REST_CSRF_DISABLE_TOKEN_VALIDATION,"true");
RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
request.setServerName("test.archiva.org");
@@ -132,6 +146,8 @@ public class RequestValidationInterceptorTest extends TestCase {
public void validateRequestWithOriginAndStaticUrl() throws UserConfigurationException, IOException {
MockUserConfiguration cfg = new MockUserConfiguration();
cfg.addValue("rest.baseUrl","http://test.archiva.org");
+ cfg.addValue(RequestValidationInterceptor.CFG_REST_CSRF_DISABLE_TOKEN_VALIDATION,"true");
+ TokenManager tm = new TokenManager();
RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
request.setServerName("test4.archiva.org");
@@ -147,6 +163,8 @@ public class RequestValidationInterceptorTest extends TestCase {
public void validateRequestWithBadOriginAndStaticUrl() throws UserConfigurationException, IOException {
MockUserConfiguration cfg = new MockUserConfiguration();
cfg.addValue("rest.baseUrl","http://mytest.archiva.org");
+ cfg.addValue(RequestValidationInterceptor.CFG_REST_CSRF_DISABLE_TOKEN_VALIDATION,"true");
+ TokenManager tm = new TokenManager();
RequestValidationInterceptor interceptor = new RequestValidationInterceptor(cfg);
MockHttpServletRequest request = new MockHttpServletRequest("GET","/api/v1/userService");
request.setServerName("mytest.archiva.org");
[3/8] archiva-redback-core git commit: Adding validation token
generator got login
Posted by ma...@apache.org.
Adding validation token generator got login
Project: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/commit/de5816ad
Tree: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/tree/de5816ad
Diff: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/diff/de5816ad
Branch: refs/heads/master
Commit: de5816adf044d94245bb9e97249b2d27e6507bce
Parents: e9bc481
Author: Martin Stockhammer <ma...@apache.org>
Authored: Sat Feb 11 20:20:24 2017 +0100
Committer: Martin Stockhammer <ma...@apache.org>
Committed: Sun Feb 12 14:02:01 2017 +0100
----------------------------------------------------------------------
.../EncryptionFailedException.java | 47 ++++
.../authentication/InvalidTokenException.java | 48 ++++
.../redback/authentication/SimpleTokenData.java | 88 +++++++
.../redback/authentication/TokenData.java | 66 +++++
.../redback/authentication/TokenManager.java | 248 +++++++++++++++++++
.../main/resources/META-INF/spring-context.xml | 4 +-
.../authentication/TokenManagerTest.java | 153 ++++++++++++
.../archiva/redback/rest/api/model/User.java | 16 ++
.../rest/services/DefaultLoginService.java | 12 +
.../redback/system/DefaultSecuritySystem.java | 15 ++
.../archiva/redback/system/SecuritySystem.java | 8 +
11 files changed, 704 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/de5816ad/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/EncryptionFailedException.java
----------------------------------------------------------------------
diff --git a/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/EncryptionFailedException.java b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/EncryptionFailedException.java
new file mode 100644
index 0000000..83c1079
--- /dev/null
+++ b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/EncryptionFailedException.java
@@ -0,0 +1,47 @@
+package org.apache.archiva.redback.authentication;
+
+/*
+ * 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.
+ */
+
+/**
+ *
+ * Exception used by the token manager.
+ *
+ * Created by Martin Stockhammer on 11.02.17.
+ */
+public class EncryptionFailedException extends Exception {
+
+ private static final long serialVersionUID = -2275802156651048276L;
+
+ public EncryptionFailedException() {
+ super();
+ }
+
+ public EncryptionFailedException(String message) {
+ super(message);
+ }
+
+ public EncryptionFailedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public EncryptionFailedException(Throwable cause) {
+ super(cause);
+ }
+}
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/de5816ad/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/InvalidTokenException.java
----------------------------------------------------------------------
diff --git a/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/InvalidTokenException.java b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/InvalidTokenException.java
new file mode 100644
index 0000000..b3af182
--- /dev/null
+++ b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/InvalidTokenException.java
@@ -0,0 +1,48 @@
+package org.apache.archiva.redback.authentication;
+
+/*
+ * 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.
+ */
+
+/**
+ * Exception used by the token manager.
+ *
+ * Created by Martin Stockhammer on 11.02.17.
+ */
+public class InvalidTokenException extends Exception {
+
+ private static final long serialVersionUID = -1148088610607667870L;
+
+ public InvalidTokenException() {
+ super();
+ }
+
+ public InvalidTokenException(String message) {
+ super(message);
+ }
+
+ public InvalidTokenException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public InvalidTokenException(Throwable cause) {
+ super(cause);
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/de5816ad/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/SimpleTokenData.java
----------------------------------------------------------------------
diff --git a/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/SimpleTokenData.java b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/SimpleTokenData.java
new file mode 100644
index 0000000..e3302bf
--- /dev/null
+++ b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/SimpleTokenData.java
@@ -0,0 +1,88 @@
+package org.apache.archiva.redback.authentication;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ *
+ * Simple Token information class that contains a username and a lifetime.
+ *
+ * The class is not able to detect time manipulations. It is assumed that the
+ * current time of the system is correct.
+ *
+ * This class is immutable.
+ *
+ * Created by Martin Stockhammer on 03.02.17.
+ */
+public final class SimpleTokenData implements Serializable, TokenData {
+
+
+ private static final long serialVersionUID = 5907745449771921813L;
+
+ private final String user;
+ private final Date created;
+ private final Date validBefore;
+ private final long nonce;
+
+
+ /**
+ * Creates a new token info instance for the given user.
+ * The lifetime in milliseconds defines the invalidation date by
+ * adding the lifetime to the current time of instantiation.
+ *
+ * @param user The user name
+ * @param lifetime The number of milliseconds after that the token is invalid
+ * @param nonce Should be a random number and different for each instance.
+ */
+ public SimpleTokenData(final String user, final long lifetime, final long nonce) {
+ this.user=user;
+ this.created=new Date();
+ this.validBefore =new Date(created.getTime()+lifetime);
+ this.nonce = nonce;
+ }
+
+ @Override
+ public final String getUser() {
+ return user;
+ }
+
+ @Override
+ public final Date created() {
+ return created;
+ }
+
+ @Override
+ public final Date validBefore() {
+ return validBefore;
+ }
+
+ @Override
+ public final long getNonce() {
+ return nonce;
+ }
+
+ @Override
+ public boolean isValid() {
+ return (new Date().getTime())<validBefore.getTime();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/de5816ad/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenData.java
----------------------------------------------------------------------
diff --git a/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenData.java b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenData.java
new file mode 100644
index 0000000..f641f3a
--- /dev/null
+++ b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenData.java
@@ -0,0 +1,66 @@
+package org.apache.archiva.redback.authentication;
+
+/*
+ * 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.
+ */
+
+import java.util.Date;
+
+/**
+ *
+ * This contains the token payload that is used for verification of tokens.
+ *
+ * Created by Martin Stockhammer on 11.02.17.
+ */
+public interface TokenData {
+
+ /**
+ * Returns the user name.
+ *
+ * @return The username property.
+ */
+ String getUser();
+
+ /**
+ * The date the token was created.
+ *
+ * @return The creation date.
+ */
+ Date created();
+
+ /**
+ * The date after that the token is invalid.
+ *
+ * @return The invalidation date.
+ */
+ Date validBefore();
+
+ /**
+ * The nonce that is stored in the token.
+ *
+ * @return The nonce.
+ */
+ long getNonce();
+
+ /**
+ * Returns true, if the token is valid.
+ *
+ * @return True, if valid, otherwise false.
+ */
+ boolean isValid();
+}
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/de5816ad/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java
----------------------------------------------------------------------
diff --git a/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java
new file mode 100644
index 0000000..08538bd
--- /dev/null
+++ b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java
@@ -0,0 +1,248 @@
+package org.apache.archiva.redback.authentication;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.codec.binary.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+
+/**
+ *
+ * Class that manages tokens that are encrypted with a dynamic key. The tokens
+ * are converted into BASE64 strings.
+ *
+ * Each token contains information about username,
+ *
+ * Created by Martin Stockhammer on 03.02.17.
+ */
+@Service("tokenManager#jce")
+public class TokenManager {
+
+ private static final SecureRandom rd = new SecureRandom();
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private String algorithm = "AES/ECB/PKCS5Padding";
+ private int keySize = -1;
+ private Cipher deCipher;
+ private Cipher enCipher;
+
+ boolean paddingUsed = true;
+
+
+ @PostConstruct
+ public void initialize() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, EncryptionFailedException, InvalidAlgorithmParameterException {
+ log.debug("Initializing key for token generator");
+ try {
+ enCipher = Cipher.getInstance(algorithm);
+ deCipher = Cipher.getInstance(algorithm);
+ String[] keyAlg = enCipher.getAlgorithm().split("/");
+ if (keyAlg.length<1) {
+ throw new EncryptionFailedException("Initialization of key failed. Not algorithm found.");
+ }
+ String encryptionAlgorithm = keyAlg[0];
+ KeyGenerator keyGen = KeyGenerator.getInstance(encryptionAlgorithm);
+ if (keySize>0) {
+ keyGen.init(keySize);
+ }
+ if (keyAlg.length==3 && keyAlg[2].equals("NoPadding")) {
+ paddingUsed=false;
+ }
+ SecretKey secretKey = keyGen.generateKey();
+ enCipher.init(Cipher.ENCRYPT_MODE, secretKey);
+ // We have to provide the IV depending on the algorithm used
+ // CBC needs an IV, ECB not.
+ if (enCipher.getIV()==null) {
+ deCipher.init(Cipher.DECRYPT_MODE, secretKey);
+ } else {
+ deCipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(enCipher.getIV()));
+ }
+ } catch (NoSuchAlgorithmException e) {
+ log.error("Error occurred during key initialization. Requested algorithm not available. "+e.getMessage());
+ throw e;
+ } catch (NoSuchPaddingException e) {
+ log.error("Error occurred during key initialization. Requested padding not available. "+e.getMessage());
+ throw e;
+ } catch (InvalidKeyException e) {
+ log.error("The key is not valid.");
+ throw e;
+ } catch (InvalidAlgorithmParameterException e) {
+ log.error("Invalid encryption parameters.");
+ throw e;
+ }
+ }
+
+
+ @SuppressWarnings("SameParameterValue")
+ public String encryptToken(String user, long lifetime) throws EncryptionFailedException {
+ return encryptToken(new SimpleTokenData(user, lifetime, createNonce()));
+ }
+
+ public String encryptToken(TokenData tokenData) throws EncryptionFailedException {
+ try {
+ return encode(encrypt(tokenData));
+ } catch (IOException e) {
+ log.error("Error during object conversion: "+e.getMessage());
+ throw new EncryptionFailedException(e);
+ } catch (BadPaddingException e) {
+ log.error("Padding invalid");
+ throw new EncryptionFailedException(e);
+ } catch (IllegalBlockSizeException e) {
+ log.error("Block size invalid");
+ throw new EncryptionFailedException(e);
+ }
+ }
+
+ public TokenData decryptToken(String token) throws InvalidTokenException {
+ try {
+ return decrypt(decode(token));
+ } catch (IOException ex) {
+ log.error("Error during data read. " + ex.getMessage());
+ throw new InvalidTokenException(ex);
+ } catch (ClassNotFoundException ex) {
+ log.error("Token data invalid.");
+ throw new InvalidTokenException(ex);
+ } catch (BadPaddingException ex) {
+ log.error("The encrypted token has the wrong padding.");
+ throw new InvalidTokenException(ex);
+ } catch (IllegalBlockSizeException ex) {
+ log.error("The encrypted token has the wrong block size.");
+ throw new InvalidTokenException(ex);
+ }
+ }
+
+ private long createNonce() {
+ return rd.nextLong();
+ }
+
+ protected byte[] encrypt(TokenData info) throws IOException, BadPaddingException, IllegalBlockSizeException {
+ return doEncrypt(convertToByteArray(info));
+ }
+
+ protected byte[] doEncrypt(byte[] data) throws BadPaddingException, IllegalBlockSizeException {
+ byte[] encData;
+ if (!paddingUsed && (data.length % enCipher.getBlockSize())!=0) {
+ int blocks = data.length / enCipher.getBlockSize();
+ encData = Arrays.copyOf(data, enCipher.getBlockSize()*(blocks+1));
+ } else {
+ encData = data;
+ }
+ return enCipher.doFinal(encData);
+ }
+
+ protected TokenData decrypt(byte[] token) throws BadPaddingException, IllegalBlockSizeException, IOException, ClassNotFoundException {
+ Object result = convertFromByteArray(doDecrypt(token));
+ if (!(result instanceof TokenData)) {
+ throw new InvalidClassException("No TokenData found in decrypted token");
+ }
+ return (TokenData)result;
+ }
+
+ protected byte[] doDecrypt(byte[] encryptedData) throws BadPaddingException, IllegalBlockSizeException {
+ return deCipher.doFinal(encryptedData);
+ }
+
+ private String encode(byte[] token) {
+ return Base64.encodeBase64String(token);
+ }
+
+ private byte[] decode(String token) {
+ return Base64.decodeBase64(token);
+ }
+
+
+ private Object convertFromByteArray(byte[] byteObject) throws IOException,
+ ClassNotFoundException {
+ ByteArrayInputStream bais;
+ ObjectInputStream in;
+ bais = new ByteArrayInputStream(byteObject);
+ in = new ObjectInputStream(bais);
+ Object o = in.readObject();
+ in.close();
+ return o;
+
+ }
+
+
+ private byte[] convertToByteArray(Object complexObject) throws IOException {
+ ByteArrayOutputStream baos;
+ ObjectOutputStream out;
+ baos = new ByteArrayOutputStream();
+ out = new ObjectOutputStream(baos);
+ out.writeObject(complexObject);
+ out.close();
+ return baos.toByteArray();
+ }
+
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ /**
+ * Sets the encryption algorithm and resets the key size. You may change the key size after
+ * calling this method.
+ * Additionally run the initialize() method after setting algorithm and keysize.
+ *
+ *
+ * @param algorithm The encryption algorithm to use.
+ */
+ public void setAlgorithm(String algorithm) {
+ if (!this.algorithm.equals(algorithm)) {
+ this.algorithm = algorithm;
+ this.keySize=-1;
+ }
+ }
+
+ public int getKeySize() {
+ return keySize;
+ }
+
+ /**
+ * Sets the key size for the encryption. This method must be called after
+ * setting the algorithm. The keysize will be reset by calling <code>setAlgorithm()</code>
+ *
+ * The key size must be valid for the given algorithm.
+ *
+ * @param keySize The size of the encryption key
+ */
+ public void setKeySize(int keySize) {
+ this.keySize = keySize;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/de5816ad/redback-authentication/redback-authentication-api/src/main/resources/META-INF/spring-context.xml
----------------------------------------------------------------------
diff --git a/redback-authentication/redback-authentication-api/src/main/resources/META-INF/spring-context.xml b/redback-authentication/redback-authentication-api/src/main/resources/META-INF/spring-context.xml
index a07857c..d2aab37 100644
--- a/redback-authentication/redback-authentication-api/src/main/resources/META-INF/spring-context.xml
+++ b/redback-authentication/redback-authentication-api/src/main/resources/META-INF/spring-context.xml
@@ -30,5 +30,7 @@
<context:annotation-config />
<context:component-scan
base-package="org.apache.archiva.redback.authentication"/>
-
+
+ <alias name="tokenManager#jce" alias="tokenManager#default" />
+
</beans>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/de5816ad/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java
----------------------------------------------------------------------
diff --git a/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java b/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java
new file mode 100644
index 0000000..5200956
--- /dev/null
+++ b/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java
@@ -0,0 +1,153 @@
+package org.apache.archiva.redback.authentication;
+
+/*
+ * 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.
+ */
+
+import org.junit.Test;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test the TokenManager implementation. Uses no spring dependencies.
+ *
+ * Created by Martin Stockhammer on 11.02.17.
+ */
+public class TokenManagerTest {
+
+ @Test
+ public void initialize() throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, EncryptionFailedException, InvalidAlgorithmParameterException {
+ TokenManager tokenManager = new TokenManager();
+ tokenManager.initialize();
+ }
+
+ @Test
+ public void encryptToken() throws Exception {
+ TokenManager tokenManager = new TokenManager();
+ tokenManager.initialize();
+ assertEquals(tokenManager.getAlgorithm(),"AES/ECB/PKCS5Padding");
+ assertEquals(tokenManager.getKeySize(), -1);
+ String token = tokenManager.encryptToken("testuser01",1000);
+ assertNotNull(token);
+ assertTrue("Token size too low",token.length()>300);
+
+ }
+
+ @Test
+ public void decryptToken() throws Exception {
+ TokenManager tokenManager = new TokenManager();
+ tokenManager.initialize();
+ String token = tokenManager.encryptToken("testuser00003",1000);
+ assertNotNull(token);
+ assertTrue("Token size too low",token.length()>300);
+ TokenData tokenData = tokenManager.decryptToken(token);
+ assertNotNull(tokenData);
+ assertEquals("testuser00003", tokenData.getUser());
+ assertTrue(tokenData.isValid());
+
+ }
+
+ @Test
+ public void decryptExpiredToken() throws Exception {
+ TokenManager tokenManager = new TokenManager();
+ tokenManager.initialize();
+ SimpleTokenData sToken = new SimpleTokenData("testuser00003", 0, 1345455);
+ String token = tokenManager.encryptToken(sToken);
+ assertNotNull(token);
+ assertTrue("Token size too low",token.length()>300);
+ TokenData tokenData = tokenManager.decryptToken(token);
+ assertNotNull(tokenData);
+ assertEquals("testuser00003", tokenData.getUser());
+ assertFalse(tokenData.isValid());
+
+ }
+
+ @Test(expected = InvalidTokenException.class)
+ public void decryptInvalidToken() throws Exception {
+ TokenManager tokenManager = new TokenManager();
+ tokenManager.initialize();
+ SimpleTokenData sToken = new SimpleTokenData("testuser00003", 0, 1345455);
+ String token = tokenManager.encryptToken(sToken);
+ assertNotNull(token);
+ assertTrue("Token size too low",token.length()>300);
+ tokenManager.initialize();
+ tokenManager.decryptToken(token);
+
+ }
+
+ @Test
+ public void decryptTokenWithDifferentAlgorithm() throws Exception {
+ TokenManager tokenManager = new TokenManager();
+ tokenManager.setAlgorithm("DES/ECB/PKCS5Padding");
+ tokenManager.initialize();
+ String token = tokenManager.encryptToken("testuser00005",2000);
+ assertNotNull(token);
+ assertTrue("Token size too low",token.length()>300);
+ TokenData tokenData = tokenManager.decryptToken(token);
+ assertNotNull(tokenData);
+ assertEquals("testuser00005", tokenData.getUser());
+ assertTrue(tokenData.isValid());
+
+
+ tokenManager.setAlgorithm("DES/ECB/NoPadding");
+ tokenManager.initialize();
+ token = tokenManager.encryptToken("testuser00006",2000);
+ assertNotNull(token);
+ assertTrue("Token size too low",token.length()>300);
+ tokenData = tokenManager.decryptToken(token);
+ assertNotNull(tokenData);
+ assertEquals("testuser00006", tokenData.getUser());
+ assertTrue(tokenData.isValid());
+
+ }
+
+ @Test
+ public void nativeEncryption() throws EncryptionFailedException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidTokenException, BadPaddingException, IllegalBlockSizeException {
+ TokenManager tokenManager = new TokenManager();
+ tokenManager.setAlgorithm("DES/CBC/PKCS5Padding");
+ tokenManager.setKeySize(56);
+ tokenManager.initialize();
+ byte[] data = { 1, 5, 12, 18, 124, 44, 88, -28, -44};
+ byte[] token = tokenManager.doEncrypt(data);
+ assertNotNull(token);
+ byte[] tokenData = tokenManager.doDecrypt(token);
+ assertNotNull(tokenData);
+ assertArrayEquals(data, tokenData);
+
+
+ tokenManager.setAlgorithm("AES/CBC/NoPadding");
+ tokenManager.setKeySize(128);
+ tokenManager.initialize();
+ token = tokenManager.doEncrypt(data);
+ assertNotNull(token);
+ // Without padding the decrypted value is a multiple of the block size.
+ tokenData = Arrays.copyOf(tokenManager.doDecrypt(token), data.length);
+ assertNotNull(tokenData);
+ assertArrayEquals(data, tokenData);
+
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/de5816ad/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/User.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/User.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/User.java
index 1b6d50c..2f3e93e 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/User.java
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/User.java
@@ -80,6 +80,14 @@ public class User
*/
private String userManagerId;
+ /**
+ * for request validation
+ *
+ * @since 2.2
+ */
+ private String validationToken;
+
+
public User()
{
// no op
@@ -272,6 +280,14 @@ public class User
this.userManagerId = userManagerId;
}
+ public String getValidationToken() {
+ return validationToken;
+ }
+
+ public void setValidationToken(String validationToken) {
+ this.validationToken = validationToken;
+ }
+
@Override
public String toString()
{
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/de5816ad/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLoginService.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLoginService.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLoginService.java
index ee3cc47..3bc30bb 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLoginService.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLoginService.java
@@ -22,7 +22,9 @@ package org.apache.archiva.redback.rest.services;
import org.apache.archiva.redback.authentication.AuthenticationConstants;
import org.apache.archiva.redback.authentication.AuthenticationException;
import org.apache.archiva.redback.authentication.AuthenticationFailureCause;
+import org.apache.archiva.redback.authentication.EncryptionFailedException;
import org.apache.archiva.redback.authentication.PasswordBasedAuthenticationDataSource;
+import org.apache.archiva.redback.authentication.TokenManager;
import org.apache.archiva.redback.integration.filter.authentication.HttpAuthenticator;
import org.apache.archiva.redback.keys.AuthenticationKey;
import org.apache.archiva.redback.keys.KeyManager;
@@ -75,6 +77,9 @@ public class DefaultLoginService
@Context
private HttpServletRequest httpServletRequest;
+ // validation token lifetime: 3 hours
+ long tokenLifetime = 1000*3600*3;
+
@Inject
public DefaultLoginService( SecuritySystem securitySystem,
@Named( "httpAuthenticator#basic" ) HttpAuthenticator httpAuthenticator )
@@ -149,6 +154,13 @@ public class DefaultLoginService
}
User restUser = buildRestUser( user );
restUser.setReadOnly( securitySystem.userManagerReadOnly() );
+ // validationToken only set during login
+ try {
+ restUser.setValidationToken(securitySystem.getTokenManager().encryptToken(user.getUsername(), tokenLifetime));
+ } catch (EncryptionFailedException e) {
+ log.error("Validation token could not be created "+e.getMessage());
+ }
+
// here create an http session
httpAuthenticator.authenticate( authDataSource, httpServletRequest.getSession( true ) );
return restUser;
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/de5816ad/redback-system/src/main/java/org/apache/archiva/redback/system/DefaultSecuritySystem.java
----------------------------------------------------------------------
diff --git a/redback-system/src/main/java/org/apache/archiva/redback/system/DefaultSecuritySystem.java b/redback-system/src/main/java/org/apache/archiva/redback/system/DefaultSecuritySystem.java
index 2f90c79..811903e 100644
--- a/redback-system/src/main/java/org/apache/archiva/redback/system/DefaultSecuritySystem.java
+++ b/redback-system/src/main/java/org/apache/archiva/redback/system/DefaultSecuritySystem.java
@@ -19,10 +19,12 @@ package org.apache.archiva.redback.system;
* under the License.
*/
+import com.sun.javafx.fxml.expression.Expression;
import org.apache.archiva.redback.authentication.AuthenticationDataSource;
import org.apache.archiva.redback.authentication.AuthenticationException;
import org.apache.archiva.redback.authentication.AuthenticationManager;
import org.apache.archiva.redback.authentication.AuthenticationResult;
+import org.apache.archiva.redback.authentication.TokenManager;
import org.apache.archiva.redback.authorization.AuthorizationDataSource;
import org.apache.archiva.redback.authorization.AuthorizationException;
import org.apache.archiva.redback.authorization.AuthorizationResult;
@@ -69,6 +71,10 @@ public class DefaultSecuritySystem
private KeyManager keyManager;
@Inject
+ @Named( value = "tokenManager#default")
+ private TokenManager tokenManager;
+
+ @Inject
private UserSecurityPolicy policy;
// ----------------------------------------------------------------------------
@@ -305,4 +311,13 @@ public class DefaultSecuritySystem
return userManager.isReadOnly();
}
+ @Override
+ public TokenManager getTokenManager() {
+ return tokenManager;
+ }
+
+ public void setTokenManager(TokenManager tokenManager) {
+ this.tokenManager = tokenManager;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/de5816ad/redback-system/src/main/java/org/apache/archiva/redback/system/SecuritySystem.java
----------------------------------------------------------------------
diff --git a/redback-system/src/main/java/org/apache/archiva/redback/system/SecuritySystem.java b/redback-system/src/main/java/org/apache/archiva/redback/system/SecuritySystem.java
index c7b049d..f12868a 100644
--- a/redback-system/src/main/java/org/apache/archiva/redback/system/SecuritySystem.java
+++ b/redback-system/src/main/java/org/apache/archiva/redback/system/SecuritySystem.java
@@ -19,6 +19,7 @@ package org.apache.archiva.redback.system;
* under the License.
*/
+import org.apache.archiva.redback.authentication.TokenManager;
import org.apache.archiva.redback.policy.AccountLockedException;
import org.apache.archiva.redback.policy.MustChangePasswordException;
import org.apache.archiva.redback.policy.UserSecurityPolicy;
@@ -111,5 +112,12 @@ public interface SecuritySystem
* @since 2.1
*/
boolean userManagerReadOnly();
+
+ /**
+ * Returns the token manager implementation.
+ *
+ * @since 2.2
+ */
+ TokenManager getTokenManager();
}
[4/8] archiva-redback-core git commit: Using random iv vector to
generate unique tokens for each login
Posted by ma...@apache.org.
Using random iv vector to generate unique tokens for each login
Project: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/commit/a6ceb384
Tree: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/tree/a6ceb384
Diff: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/diff/a6ceb384
Branch: refs/heads/master
Commit: a6ceb384697b06d361c3323e24501cc7f2475900
Parents: de5816a
Author: Martin Stockhammer <ma...@apache.org>
Authored: Sun Feb 12 17:09:15 2017 +0100
Committer: Martin Stockhammer <ma...@apache.org>
Committed: Sun Feb 12 17:09:15 2017 +0100
----------------------------------------------------------------------
.../redback/authentication/TokenManager.java | 115 +++++++++++++++----
.../authentication/TokenManagerTest.java | 7 +-
2 files changed, 94 insertions(+), 28 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/a6ceb384/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java
----------------------------------------------------------------------
diff --git a/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java
index 08538bd..e37c1d4 100644
--- a/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java
+++ b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java
@@ -20,6 +20,7 @@ package org.apache.archiva.redback.authentication;
*/
import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@@ -57,12 +58,12 @@ import java.util.Arrays;
@Service("tokenManager#jce")
public class TokenManager {
- private static final SecureRandom rd = new SecureRandom();
+ private final ThreadLocal<SecureRandom> rd = new ThreadLocal<SecureRandom>();
private final Logger log = LoggerFactory.getLogger(getClass());
- private String algorithm = "AES/ECB/PKCS5Padding";
+ private String algorithm = "AES/CBC/PKCS5Padding";
private int keySize = -1;
- private Cipher deCipher;
- private Cipher enCipher;
+ private int ivSize = -1;
+ private SecretKey secretKey;
boolean paddingUsed = true;
@@ -71,8 +72,8 @@ public class TokenManager {
public void initialize() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, EncryptionFailedException, InvalidAlgorithmParameterException {
log.debug("Initializing key for token generator");
try {
- enCipher = Cipher.getInstance(algorithm);
- deCipher = Cipher.getInstance(algorithm);
+ rd.set(new SecureRandom());
+ Cipher enCipher = Cipher.getInstance(algorithm);
String[] keyAlg = enCipher.getAlgorithm().split("/");
if (keyAlg.length<1) {
throw new EncryptionFailedException("Initialization of key failed. Not algorithm found.");
@@ -85,14 +86,14 @@ public class TokenManager {
if (keyAlg.length==3 && keyAlg[2].equals("NoPadding")) {
paddingUsed=false;
}
- SecretKey secretKey = keyGen.generateKey();
+ this.secretKey = keyGen.generateKey();
enCipher.init(Cipher.ENCRYPT_MODE, secretKey);
// We have to provide the IV depending on the algorithm used
// CBC needs an IV, ECB not.
if (enCipher.getIV()==null) {
- deCipher.init(Cipher.DECRYPT_MODE, secretKey);
+ ivSize=-1;
} else {
- deCipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(enCipher.getIV()));
+ ivSize=enCipher.getIV().length;
}
} catch (NoSuchAlgorithmException e) {
log.error("Error occurred during key initialization. Requested algorithm not available. "+e.getMessage());
@@ -103,14 +104,9 @@ public class TokenManager {
} catch (InvalidKeyException e) {
log.error("The key is not valid.");
throw e;
- } catch (InvalidAlgorithmParameterException e) {
- log.error("Invalid encryption parameters.");
- throw e;
}
}
-
- @SuppressWarnings("SameParameterValue")
public String encryptToken(String user, long lifetime) throws EncryptionFailedException {
return encryptToken(new SimpleTokenData(user, lifetime, createNonce()));
}
@@ -127,6 +123,18 @@ public class TokenManager {
} catch (IllegalBlockSizeException e) {
log.error("Block size invalid");
throw new EncryptionFailedException(e);
+ } catch (NoSuchPaddingException e) {
+ log.error("Padding not available "+algorithm);
+ throw new EncryptionFailedException(e);
+ } catch (InvalidKeyException e) {
+ log.error("Bad encryption key");
+ throw new EncryptionFailedException(e);
+ } catch (NoSuchAlgorithmException e) {
+ log.error("Bad encryption algorithm "+algorithm);
+ throw new EncryptionFailedException(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ log.error("Invalid encryption parameters");
+ throw new EncryptionFailedException(e);
}
}
@@ -145,29 +153,63 @@ public class TokenManager {
} catch (IllegalBlockSizeException ex) {
log.error("The encrypted token has the wrong block size.");
throw new InvalidTokenException(ex);
+ } catch (NoSuchPaddingException e) {
+ log.error("Padding not available "+algorithm);
+ throw new InvalidTokenException(e);
+ } catch (InvalidKeyException e) {
+ log.error("Invalid decryption key");
+ throw new InvalidTokenException(e);
+ } catch (NoSuchAlgorithmException e) {
+ log.error("Encryption algorithm not available "+algorithm);
+ throw new InvalidTokenException(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ log.error("Invalid encryption parameters");
+ throw new InvalidTokenException(e);
}
}
private long createNonce() {
- return rd.nextLong();
+ if (rd.get()==null) {
+ rd.set(new SecureRandom());
+ }
+ return rd.get().nextLong();
+ }
+
+ protected byte[] encrypt(TokenData info) throws IOException, BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException {
+ return doEncrypt(convertToByteArray(info), info.getNonce());
}
- protected byte[] encrypt(TokenData info) throws IOException, BadPaddingException, IllegalBlockSizeException {
- return doEncrypt(convertToByteArray(info));
+ private byte[] getIv(long nonce) {
+ byte[] iv = new byte[ivSize];
+ SecureRandom sr = getRandomGenerator();
+ sr.setSeed(nonce);
+ sr.nextBytes(iv);
+ return iv;
}
- protected byte[] doEncrypt(byte[] data) throws BadPaddingException, IllegalBlockSizeException {
+ protected byte[] doEncrypt(byte[] data, long nonce) throws BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
+ Cipher cipher = getEnCipher();
byte[] encData;
- if (!paddingUsed && (data.length % enCipher.getBlockSize())!=0) {
- int blocks = data.length / enCipher.getBlockSize();
- encData = Arrays.copyOf(data, enCipher.getBlockSize()*(blocks+1));
+ byte[] iv;
+ if (ivSize>0) {
+ iv = getIv(nonce);
+ cipher.init(Cipher.ENCRYPT_MODE,this.secretKey,new IvParameterSpec(iv));
+ } else {
+ iv = new byte[0];
+ cipher.init(Cipher.ENCRYPT_MODE,this.secretKey);
+ }
+ if (!paddingUsed && (data.length % cipher.getBlockSize())!=0) {
+ int blocks = data.length / cipher.getBlockSize();
+ encData = Arrays.copyOf(data, cipher.getBlockSize()*(blocks+1));
} else {
encData = data;
}
- return enCipher.doFinal(encData);
+ byte[] encrypted = cipher.doFinal(encData);
+ // prepending the iv to the token
+ return ArrayUtils.addAll(iv,encrypted);
}
- protected TokenData decrypt(byte[] token) throws BadPaddingException, IllegalBlockSizeException, IOException, ClassNotFoundException {
+ protected TokenData decrypt(byte[] token) throws BadPaddingException, IllegalBlockSizeException, IOException, ClassNotFoundException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException {
Object result = convertFromByteArray(doDecrypt(token));
if (!(result instanceof TokenData)) {
throw new InvalidClassException("No TokenData found in decrypted token");
@@ -175,8 +217,31 @@ public class TokenManager {
return (TokenData)result;
}
- protected byte[] doDecrypt(byte[] encryptedData) throws BadPaddingException, IllegalBlockSizeException {
- return deCipher.doFinal(encryptedData);
+ protected byte[] doDecrypt(byte[] encryptedData) throws BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
+ Cipher cipher = getDeCipher();
+ if (ivSize>0) {
+ byte[] iv = Arrays.copyOfRange(encryptedData,0,ivSize);
+ cipher.init(Cipher.DECRYPT_MODE,this.secretKey,new IvParameterSpec(iv));
+ return cipher.doFinal(encryptedData,ivSize,encryptedData.length-ivSize);
+ } else {
+ cipher.init(Cipher.DECRYPT_MODE,this.secretKey);
+ return cipher.doFinal(encryptedData);
+ }
+ }
+
+ private SecureRandom getRandomGenerator() {
+ if (rd.get()==null) {
+ rd.set(new SecureRandom());
+ }
+ return rd.get();
+ }
+
+ private Cipher getEnCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
+ return Cipher.getInstance(algorithm);
+ }
+
+ private Cipher getDeCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
+ return Cipher.getInstance(algorithm);
}
private String encode(byte[] token) {
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/a6ceb384/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java
----------------------------------------------------------------------
diff --git a/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java b/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java
index 5200956..1a07033 100644
--- a/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java
+++ b/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java
@@ -27,6 +27,7 @@ import javax.crypto.NoSuchPaddingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
import java.util.Arrays;
import static org.junit.Assert.*;
@@ -48,7 +49,7 @@ public class TokenManagerTest {
public void encryptToken() throws Exception {
TokenManager tokenManager = new TokenManager();
tokenManager.initialize();
- assertEquals(tokenManager.getAlgorithm(),"AES/ECB/PKCS5Padding");
+ assertEquals(tokenManager.getAlgorithm(),"AES/CBC/PKCS5Padding");
assertEquals(tokenManager.getKeySize(), -1);
String token = tokenManager.encryptToken("testuser01",1000);
assertNotNull(token);
@@ -131,7 +132,7 @@ public class TokenManagerTest {
tokenManager.setKeySize(56);
tokenManager.initialize();
byte[] data = { 1, 5, 12, 18, 124, 44, 88, -28, -44};
- byte[] token = tokenManager.doEncrypt(data);
+ byte[] token = tokenManager.doEncrypt(data, new SecureRandom().nextLong());
assertNotNull(token);
byte[] tokenData = tokenManager.doDecrypt(token);
assertNotNull(tokenData);
@@ -141,7 +142,7 @@ public class TokenManagerTest {
tokenManager.setAlgorithm("AES/CBC/NoPadding");
tokenManager.setKeySize(128);
tokenManager.initialize();
- token = tokenManager.doEncrypt(data);
+ token = tokenManager.doEncrypt(data, new SecureRandom().nextLong());
assertNotNull(token);
// Without padding the decrypted value is a multiple of the block size.
tokenData = Arrays.copyOf(tokenManager.doDecrypt(token), data.length);
[2/8] archiva-redback-core git commit: Adding port check for Referer
header
Posted by ma...@apache.org.
Adding port check for Referer header
Project: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/commit/e9bc4818
Tree: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/tree/e9bc4818
Diff: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/diff/e9bc4818
Branch: refs/heads/master
Commit: e9bc4818844bf684e4fd16e3a5adc99fe9eb9f96
Parents: 95f1b3e
Author: Martin Stockhammer <ma...@apache.org>
Authored: Mon Jan 30 22:42:33 2017 +0100
Committer: Martin Stockhammer <ma...@apache.org>
Committed: Mon Jan 30 22:42:33 2017 +0100
----------------------------------------------------------------------
.../interceptors/RequestValidationInterceptor.java | 7 ++++++-
.../redback/rest/services/AbstractRestServicesTest.java | 10 +++++-----
2 files changed, 11 insertions(+), 6 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/e9bc4818/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java
index 4300baf..182d23a 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java
@@ -161,6 +161,7 @@ public class RequestValidationInterceptor extends AbstractInterceptor implements
private boolean checkSourceRequestHeader(final URL targetUrl, final HttpServletRequest request) {
boolean headerFound=false;
String origin = request.getHeader(ORIGIN);
+ int targetPort = getPort(targetUrl);
if (origin!=null) {
try {
URL originUrl = new URL(origin);
@@ -175,7 +176,6 @@ public class RequestValidationInterceptor extends AbstractInterceptor implements
return false;
}
int originPort = getPort(originUrl);
- int targetPort = getPort(targetUrl);
if (targetPort != originPort) {
log.warn("Origin Header Port does not match originUrl={}, targetUrl={}",originUrl,targetUrl);
return false;
@@ -195,6 +195,11 @@ public class RequestValidationInterceptor extends AbstractInterceptor implements
log.warn("Referer Header Host does not match refererUrl={}, targetUrl={}",refererUrl,targetUrl);
return false;
}
+ int refererPort = getPort(refererUrl);
+ if (targetPort != refererPort) {
+ log.warn("Referer Header Port does not match refererUrl={}, targetUrl={}",refererUrl,targetUrl);
+ return false;
+ }
} catch (MalformedURLException ex) {
log.warn("Bad URL in Referer HTTP-Header: {}, Message: {}", referer, ex.getMessage());
return false;
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/e9bc4818/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/AbstractRestServicesTest.java
----------------------------------------------------------------------
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/AbstractRestServicesTest.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/AbstractRestServicesTest.java
index 0cab072..4b565a2 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/AbstractRestServicesTest.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/AbstractRestServicesTest.java
@@ -158,6 +158,7 @@ public abstract class AbstractRestServicesTest
protected UserService getUserService()
{
+
return getUserService( null );
}
@@ -175,7 +176,7 @@ public abstract class AbstractRestServicesTest
{
WebClient.client( service ).header( "Authorization", authzHeader );
}
- WebClient.client(service).header("Referer","http://localhost");
+ WebClient.client(service).header("Referer","http://localhost:"+port);
WebClient.client( service ).accept( MediaType.APPLICATION_JSON_TYPE );
WebClient.client( service ).type( MediaType.APPLICATION_JSON_TYPE );
@@ -197,8 +198,7 @@ public abstract class AbstractRestServicesTest
{
WebClient.client( service ).header( "Authorization", authzHeader );
}
- WebClient.client( service ).header("Referer","http://localhost/");
-
+ WebClient.client(service).header("Referer","http://localhost:"+port);
WebClient.client( service ).accept( MediaType.APPLICATION_JSON_TYPE );
WebClient.client( service ).type( MediaType.APPLICATION_JSON_TYPE );
@@ -219,7 +219,7 @@ public abstract class AbstractRestServicesTest
{
WebClient.client( service ).header( "Authorization", authzHeader );
}
- WebClient.client( service ).header("Referer","http://localhost/");
+ WebClient.client(service).header("Referer","http://localhost:"+port);
WebClient.client( service ).accept( MediaType.APPLICATION_JSON_TYPE );
WebClient.client( service ).type( MediaType.APPLICATION_JSON_TYPE );
@@ -242,7 +242,7 @@ public abstract class AbstractRestServicesTest
{
WebClient.client( service ).header( "Authorization", authzHeader );
}
- WebClient.client( service ).header("Referer","http://localhost/");
+ WebClient.client(service).header("Referer","http://localhost:"+port);
WebClient.client( service ).accept( MediaType.APPLICATION_JSON_TYPE );
WebClient.client( service ).type( MediaType.APPLICATION_JSON_TYPE );
[8/8] archiva-redback-core git commit: Fixing bad import
Posted by ma...@apache.org.
Fixing bad import
Project: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/commit/d9146ce8
Tree: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/tree/d9146ce8
Diff: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/diff/d9146ce8
Branch: refs/heads/master
Commit: d9146ce8abc7ac5c4abf252096383b05b1c0ec16
Parents: 6a6b7bb
Author: Martin Stockhammer <ma...@apache.org>
Authored: Thu Mar 9 20:07:03 2017 +0100
Committer: Martin Stockhammer <ma...@apache.org>
Committed: Thu Mar 9 20:07:03 2017 +0100
----------------------------------------------------------------------
.../org/apache/archiva/redback/system/DefaultSecuritySystem.java | 1 -
1 file changed, 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/d9146ce8/redback-system/src/main/java/org/apache/archiva/redback/system/DefaultSecuritySystem.java
----------------------------------------------------------------------
diff --git a/redback-system/src/main/java/org/apache/archiva/redback/system/DefaultSecuritySystem.java b/redback-system/src/main/java/org/apache/archiva/redback/system/DefaultSecuritySystem.java
index 811903e..3761394 100644
--- a/redback-system/src/main/java/org/apache/archiva/redback/system/DefaultSecuritySystem.java
+++ b/redback-system/src/main/java/org/apache/archiva/redback/system/DefaultSecuritySystem.java
@@ -19,7 +19,6 @@ package org.apache.archiva.redback.system;
* under the License.
*/
-import com.sun.javafx.fxml.expression.Expression;
import org.apache.archiva.redback.authentication.AuthenticationDataSource;
import org.apache.archiva.redback.authentication.AuthenticationException;
import org.apache.archiva.redback.authentication.AuthenticationManager;