You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by lm...@apache.org on 2018/06/01 15:00:39 UTC
[2/3] knox git commit: KNOX-1204 - KIP-11 S3 Access through Knox API
KNOX-1204 - KIP-11 S3 Access through Knox API
Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/18e49c4b
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/18e49c4b
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/18e49c4b
Branch: refs/heads/KNOX-1204
Commit: 18e49c4bccb6319ffdc370c313f6fe62fa06d31f
Parents: 43567b0
Author: Larry McCay <lm...@hortonworks.com>
Authored: Fri Mar 30 11:22:21 2018 -0400
Committer: Larry McCay <lm...@hortonworks.com>
Committed: Fri Mar 30 11:22:21 2018 -0400
----------------------------------------------------------------------
...ctIdentityAsserterDeploymentContributor.java | 4 +
.../filter/AbstractIdentityAssertionFilter.java | 27 +--
.../filter/CommonIdentityAssertionFilter.java | 76 ++++++-
.../filter/HadoopGroupProviderFilter.java | 1 -
gateway-service-knoxs3/pom.xml | 29 ++-
.../gateway/service/knoxs3/AWSPolicyModel.java | 60 +++++
.../service/knoxs3/IdentityBrokerResource.java | 133 +++++++++++
.../service/knoxs3/KnoxS3ClientBuilder.java | 227 +++++++++++++++++++
.../service/knoxs3/S3BucketsResource.java | 214 ++++++++++++++++-
....gateway.deploy.ServiceDeploymentContributor | 2 +-
.../service/knoxs3/S3BucketsResourceTest.java | 4 +-
.../security/ClientImpersonatedPrincipal.java | 33 +++
.../knox/gateway/security/SubjectUtils.java | 8 +
.../services/s3client/KnoxS3ClientBuilder.java | 219 ------------------
.../services/s3client/S3PolicyModel.java | 60 -----
15 files changed, 786 insertions(+), 311 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/AbstractIdentityAsserterDeploymentContributor.java
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/AbstractIdentityAsserterDeploymentContributor.java b/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/AbstractIdentityAsserterDeploymentContributor.java
index 8f2adb8..170a5f0 100644
--- a/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/AbstractIdentityAsserterDeploymentContributor.java
+++ b/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/AbstractIdentityAsserterDeploymentContributor.java
@@ -50,6 +50,10 @@ public abstract class AbstractIdentityAsserterDeploymentContributor extends
if (params == null) {
params = new ArrayList<FilterParamDescriptor>();
}
+ // add resource role to params so that we can determine the service specific impersonation
+ // method when configured
+ params.add( resource.createFilterParam().name( "resource.role" ).value(resource.role() ) );
+
Map<String, String> providerParams = provider.getParams();
for(Entry<String, String> entry : providerParams.entrySet()) {
params.add( resource.createFilterParam().name(entry.getKey().toLowerCase()).value(entry.getValue()));
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/AbstractIdentityAssertionFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/AbstractIdentityAssertionFilter.java b/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/AbstractIdentityAssertionFilter.java
index a007ab6..c2ca08d 100644
--- a/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/AbstractIdentityAssertionFilter.java
+++ b/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/AbstractIdentityAssertionFilter.java
@@ -23,6 +23,7 @@ import java.security.Principal;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.Set;
import javax.security.auth.Subject;
@@ -44,6 +45,7 @@ import org.apache.knox.gateway.audit.log4j.audit.AuditConstants;
import org.apache.knox.gateway.filter.security.AbstractIdentityAssertionBase;
import org.apache.knox.gateway.i18n.GatewaySpiResources;
import org.apache.knox.gateway.i18n.resources.ResourcesFactory;
+import org.apache.knox.gateway.security.ClientImpersonatedPrincipal;
import org.apache.knox.gateway.security.GroupPrincipal;
import org.apache.knox.gateway.security.ImpersonatedPrincipal;
import org.apache.knox.gateway.security.PrimaryPrincipal;
@@ -68,7 +70,7 @@ public abstract class AbstractIdentityAssertionFilter extends
}
/**
- * This method returns a Stringp[] of new group principal names to use
+ * This method returns a String[] of new group principal names to use
* based on implementation specific mapping or lookup mechanisms.
* Returning null means that whatever set of GroupPrincipals is in the
* provided Subject is sufficient to use and no additional mapping is required.
@@ -87,7 +89,7 @@ public abstract class AbstractIdentityAssertionFilter extends
*/
public abstract String mapUserPrincipal(String principalName);
- /**
+ /**
* @param wrapper
* @param response
* @param chain
@@ -95,22 +97,21 @@ public abstract class AbstractIdentityAssertionFilter extends
* @param groups
*/
protected void continueChainAsPrincipal(HttpServletRequestWrapper request, ServletResponse response,
- FilterChain chain, String mappedPrincipalName, String[] groups) throws IOException,
- ServletException {
+ FilterChain chain, String mappedPrincipalName, String[] groups) throws IOException, ServletException {
Subject subject = null;
Principal impersonationPrincipal = null;
Principal primaryPrincipal = null;
-
+
// get the current subject and determine whether we need another doAs with
// an impersonatedPrincipal and/or mapped group principals
boolean impersonationNeeded = false;
boolean groupsMapped = false;
-
- // look up the current Java Subject and assosciated group principals
+
+ Set<?> currentGroups = null;
+ // look up the current Java Subject and associated group principals
Subject currentSubject = Subject.getSubject(AccessController.getContext());
- Set<?> currentGroups = currentSubject.getPrincipals(GroupPrincipal.class);
-
- primaryPrincipal = (PrimaryPrincipal) currentSubject.getPrincipals(PrimaryPrincipal.class).toArray()[0];
+ currentGroups = currentSubject.getPrincipals(GroupPrincipal.class);
+
if (primaryPrincipal != null) {
if (!primaryPrincipal.getName().equals(mappedPrincipalName)) {
impersonationNeeded = true;
@@ -126,9 +127,9 @@ public abstract class AbstractIdentityAssertionFilter extends
// TODO: log as appropriate
primaryPrincipal = new PrimaryPrincipal(((HttpServletRequest) request).getUserPrincipal().getName());
}
-
+
groupsMapped = groups != null || !currentGroups.isEmpty();
-
+
if (impersonationNeeded || groupsMapped) {
// gonna need a new subject and doAs
subject = new Subject();
@@ -139,7 +140,7 @@ public abstract class AbstractIdentityAssertionFilter extends
for (Object obj : currentGroups) {
principals.add((Principal)obj);
}
-
+
if (impersonationNeeded) {
impersonationPrincipal = new ImpersonatedPrincipal(mappedPrincipalName);
subject.getPrincipals().add(impersonationPrincipal);
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilter.java b/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilter.java
index 391aa9e..2e3adaf 100644
--- a/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilter.java
+++ b/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilter.java
@@ -32,17 +32,56 @@ import org.apache.knox.gateway.security.principal.SimplePrincipalMapper;
import java.io.IOException;
import java.security.AccessController;
+import java.util.HashMap;
public class CommonIdentityAssertionFilter extends AbstractIdentityAssertionFilter {
private static final String GROUP_PRINCIPAL_MAPPING = "group.principal.mapping";
private static final String PRINCIPAL_MAPPING = "principal.mapping";
private SimplePrincipalMapper mapper = new SimplePrincipalMapper();
+ private HashMap<String, Impersonation> impMap = new HashMap<String, Impersonation>();
+ private String resourceRole = null;
/* (non-Javadoc)
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
+
+ /*
+ <param>
+ <name>path.segment.impersonated.principal</name>
+ <value>IDBROKER;credentials</value>
+ </param>
+
+ <param>
+ <name>header.impersonated.principal</name>
+ <value>*;SM_USER</value>
+ </param>
+
+ <param>
+ <name>query.param.impersonated.principal</name>
+ <value>WEBHDFS,HIVE;doas</value>
+ </param>
+ */
+
+ resourceRole = filterConfig.getInitParameter("resource.role");
+ String pathSegmentImpersonation = filterConfig.getInitParameter("path.segment.impersonated.principal");
+ if (pathSegmentImpersonation != null) {
+ String services = pathSegmentImpersonation.substring(0, pathSegmentImpersonation.indexOf(";"));
+ String[] svcArray = services.split(",");
+ String service = null;
+ int segmentIndex;
+ for (int i = 0; i < svcArray.length; i++) {
+ service = svcArray[i];
+ segmentIndex = pathSegmentImpersonation.indexOf(";");
+ if (segmentIndex != -1) {
+ Impersonation imp = new Impersonation(service, "path.segment",
+ pathSegmentImpersonation.substring(segmentIndex+1));
+ impMap.put(svcArray[i], imp);
+ }
+ }
+ }
+
String principalMapping = filterConfig.getInitParameter(PRINCIPAL_MAPPING);
if (principalMapping == null || principalMapping.isEmpty()) {
principalMapping = filterConfig.getServletContext().getInitParameter(PRINCIPAL_MAPPING);
@@ -78,7 +117,30 @@ public class CommonIdentityAssertionFilter extends AbstractIdentityAssertionFilt
String principalName = getPrincipalName(subject);
- String mappedPrincipalName = mapUserPrincipalBase(principalName);
+ String mappedPrincipalName = null;
+
+ Impersonation imp = impMap.get(resourceRole);
+ if (imp != null) {
+ if (imp.type.equals("path.segment")) {
+ String pathInfo = ((HttpServletRequest)request).getPathInfo();
+ String[] segments = pathInfo.split("/");
+ for (int i = 0; i < segments.length; i++) {
+ if (segments[i].equals(imp.name)) {
+ if (segments.length > i+1) {
+ mappedPrincipalName = segments[i+1];
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (mappedPrincipalName != null && !mappedPrincipalName.equals(principalName)) {
+ // TODO: audit this
+ principalName = mappedPrincipalName;
+ }
+
+ mappedPrincipalName = mapUserPrincipalBase(principalName);
mappedPrincipalName = mapUserPrincipal(mappedPrincipalName);
String[] mappedGroups = mapGroupPrincipals(mappedPrincipalName, subject);
String[] groups = mapGroupPrincipals(mappedPrincipalName, subject);
@@ -140,4 +202,16 @@ public class CommonIdentityAssertionFilter extends AbstractIdentityAssertionFilt
// NOP
return principalName;
}
+
+ private class Impersonation {
+ public String service = null;
+ public String type = null;
+ public String name = null;
+
+ public Impersonation(String service, String type, String name) {
+ this.service = service;
+ this.type = type;
+ this.name = name;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-provider-identity-assertion-hadoop-groups/src/main/java/org/apache/knox/gateway/identityasserter/hadoop/groups/filter/HadoopGroupProviderFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-hadoop-groups/src/main/java/org/apache/knox/gateway/identityasserter/hadoop/groups/filter/HadoopGroupProviderFilter.java b/gateway-provider-identity-assertion-hadoop-groups/src/main/java/org/apache/knox/gateway/identityasserter/hadoop/groups/filter/HadoopGroupProviderFilter.java
index 1459498..6a5b5a4 100644
--- a/gateway-provider-identity-assertion-hadoop-groups/src/main/java/org/apache/knox/gateway/identityasserter/hadoop/groups/filter/HadoopGroupProviderFilter.java
+++ b/gateway-provider-identity-assertion-hadoop-groups/src/main/java/org/apache/knox/gateway/identityasserter/hadoop/groups/filter/HadoopGroupProviderFilter.java
@@ -114,7 +114,6 @@ public class HadoopGroupProviderFilter extends CommonIdentityAssertionFilter {
}
public String mapUserPrincipal(final String principalName) {
- /* return the passed principal */
return principalName;
}
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-service-knoxs3/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-service-knoxs3/pom.xml b/gateway-service-knoxs3/pom.xml
index 37fcc52..d83613c 100644
--- a/gateway-service-knoxs3/pom.xml
+++ b/gateway-service-knoxs3/pom.xml
@@ -22,16 +22,29 @@
<parent>
<groupId>org.apache.knox</groupId>
<artifactId>gateway</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0-SNAPSHOT</version>
</parent>
<groupId>org.apache.knox</groupId>
<artifactId>gateway-service-knoxs3</artifactId>
- <version>1.0.0</version>
- <name>gateway-service-health</name>
+ <version>1.1.0-SNAPSHOT</version>
+ <name>gateway-service-knoxs3</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>com.amazonaws</groupId>
+ <artifactId>aws-java-sdk-bom</artifactId>
+ <version>1.11.106</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
<dependencies>
<dependency>
<groupId>${gateway-group}</groupId>
@@ -72,5 +85,15 @@
<groupId>org.apache.knox</groupId>
<artifactId>gateway-server</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.amazonaws</groupId>
+ <artifactId>aws-java-sdk-s3</artifactId>
+ <version>1.11.106</version>
+ </dependency>
+ <dependency>
+ <groupId>com.amazonaws</groupId>
+ <artifactId>aws-java-sdk-sts</artifactId>
+ <version>1.11.106</version>
+ </dependency>
</dependencies>
</project>
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/AWSPolicyModel.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/AWSPolicyModel.java b/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/AWSPolicyModel.java
new file mode 100644
index 0000000..4b51dc3
--- /dev/null
+++ b/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/AWSPolicyModel.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.service.knoxs3;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.knox.gateway.util.JsonUtils;
+
+public class AWSPolicyModel {
+ HashMap<String, Object> policyModel = new HashMap<String, Object>();
+ ArrayList<String> actionArray = new ArrayList<String>();
+ HashMap<String, Object> statementMap = new HashMap<String, Object>();
+ ArrayList<String> resourcesArray = new ArrayList<String>();
+
+ public AWSPolicyModel() {
+ policyModel.put("Version", "2012-10-17");
+ ArrayList<Map<String, Object>> statement = new ArrayList<Map<String, Object>>();
+ policyModel.put("Statement", statement );
+ statement.add(statementMap);
+ statementMap.put("Action", actionArray );
+ statementMap.put("Resource", resourcesArray);
+ }
+
+ public void setEffect(String effect) {
+ statementMap.put("Effect", effect);
+ }
+
+ public void addAction(String action) {
+ actionArray.add(action);
+ }
+
+ public void addResource(String resource) {
+ resourcesArray.add(resource);
+ }
+
+ public void setResource(String resource) {
+ statementMap.put("Resource", resource);
+ }
+
+ public String toString() {
+ return JsonUtils.renderAsJsonString(policyModel);
+ }
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/IdentityBrokerResource.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/IdentityBrokerResource.java b/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/IdentityBrokerResource.java
new file mode 100644
index 0000000..ed235dd
--- /dev/null
+++ b/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/IdentityBrokerResource.java
@@ -0,0 +1,133 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.service.knoxs3;
+
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.security.ImpersonatedPrincipal;
+import org.apache.knox.gateway.security.PrimaryPrincipal;
+import org.apache.knox.gateway.security.SubjectUtils;
+
+import com.amazonaws.auth.BasicSessionCredentials;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.securitytoken.model.GetFederationTokenResult;
+
+import javax.annotation.PostConstruct;
+import javax.security.auth.Subject;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import java.io.PrintWriter;
+import java.security.AccessController;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.Properties;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+@Path(IdentityBrokerResource.RESOURCE_PATH)
+public class IdentityBrokerResource {
+ private static final String CREDENTIALS_API_PATH = "credentials";
+ private static final String USER_CREDENTIALS_API_PATH = "credentials/{id}";
+ private static KnoxS3ServiceMessages log = MessagesFactory.get(KnoxS3ServiceMessages.class);
+ private static final String VERSION_TAG = "api/v1";
+ static final String RESOURCE_PATH = "/idbroker/" + VERSION_TAG;
+
+ private static final String CONTENT_TYPE = "application/json";
+ private static final String CACHE_CONTROL = "Cache-Control";
+ private static final String NO_CACHE = "must-revalidate,no-cache,no-store";
+
+ private KnoxS3ClientBuilder s3b = new KnoxS3ClientBuilder();
+
+ @Context
+ HttpServletRequest request;
+
+ @Context
+ private HttpServletResponse response;
+
+ @Context
+ ServletContext context;
+
+ @PostConstruct
+ public void init() {
+ s3b.init(getProperties());
+ }
+
+ private Properties getProperties() {
+ Properties props = new Properties();
+ String paramName = null;
+ Enumeration<String> e = context.getInitParameterNames();
+ while (e.hasMoreElements()) {
+ paramName = (String)e.nextElement();
+ if (paramName.startsWith("s3.")) {
+ props.setProperty(paramName, context.getInitParameter(paramName));
+ }
+ }
+
+ return props;
+ }
+
+ @GET
+ @Produces({APPLICATION_JSON})
+ @Path(CREDENTIALS_API_PATH)
+ public Response getCredentials() {
+ return getCredentialsResponse();
+ }
+
+ @GET
+ @Produces({APPLICATION_JSON})
+ @Path(USER_CREDENTIALS_API_PATH)
+ public Response getUserCredentials() {
+ return getCredentialsResponse();
+ }
+
+ private Response getCredentialsResponse() {
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setHeader(CACHE_CONTROL, NO_CACHE);
+ response.setContentType(CONTENT_TYPE);
+ PrintWriter writer = null;
+ try {
+ writer = response.getWriter();
+ writer.println(getFederationToken());
+ } catch (Exception e) {
+ log.logException("list", e);
+ return Response.serverError().entity(String.format("Failed to reply correctly due to : %s ", e)).build();
+ } finally {
+ if (writer != null) {
+ writer.close();
+ }
+ }
+ return Response.ok().build();
+ }
+
+ protected String getFederationToken() {
+// Subject subject = Subject.getSubject(AccessController.getContext());
+// String username = getEffectiveUserName(subject);
+ GetFederationTokenResult creds = (GetFederationTokenResult) s3b.getFederationTokenResult();
+ return creds.toString();
+ }
+
+ private String getEffectiveUserName(Subject subject) {
+ return SubjectUtils.getEffectivePrincipalName(subject);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/KnoxS3ClientBuilder.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/KnoxS3ClientBuilder.java b/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/KnoxS3ClientBuilder.java
new file mode 100644
index 0000000..aab399a
--- /dev/null
+++ b/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/KnoxS3ClientBuilder.java
@@ -0,0 +1,227 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.service.knoxs3;
+
+import java.security.AccessController;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.security.auth.Subject;
+
+import org.apache.knox.gateway.security.GroupPrincipal;
+import org.apache.knox.gateway.security.ImpersonatedPrincipal;
+import org.apache.knox.gateway.security.PrimaryPrincipal;
+import org.apache.knox.gateway.security.SubjectUtils;
+
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+import com.amazonaws.auth.BasicSessionCredentials;
+import com.amazonaws.regions.Regions;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.AmazonS3ClientBuilder;
+import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
+import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
+import com.amazonaws.services.securitytoken.model.Credentials;
+import com.amazonaws.services.securitytoken.model.GetFederationTokenRequest;
+import com.amazonaws.services.securitytoken.model.GetFederationTokenResult;
+
+public class KnoxS3ClientBuilder {
+ private Map<String, PolicyConfig> userPolicyConfig = new HashMap<String, PolicyConfig>();
+ private Map<String, PolicyConfig> groupPolicyConfig = new HashMap<String, PolicyConfig>();
+
+ public KnoxS3ClientBuilder() {
+ }
+
+ public AmazonS3 getS3Client() {
+ BasicSessionCredentials sessionCredentials = (BasicSessionCredentials) getCredentials();
+
+ AmazonS3 s3 = AmazonS3ClientBuilder.standard().withRegion(Regions.US_EAST_1)
+ .withCredentials(new AWSStaticCredentialsProvider(sessionCredentials)).build();
+ return s3;
+ }
+
+ /**
+ * Get an opaque Object representation of the credentials.
+ * This method will only be called by callers that are aware
+ * of the actual form of the credentials in the given context
+ * and therefore able to cast it appropriately.
+ * @return opaque object
+ */
+ public Object getCredentials() {
+ BasicSessionCredentials sessionCredentials = getSessionCredentials();
+ if (sessionCredentials == null) {
+ throw new RuntimeException("No S3 credentials available.");
+ }
+ return sessionCredentials;
+ }
+
+ public void init(Properties context) {
+ buildPolicyMaps(context);
+ }
+
+ private void buildPolicyMaps(Properties context) {
+ /*
+ <service>
+ <role>KNOXS3</role>
+ <param>
+ <name>s3.user.policy.action.guest</name>
+ <value>s3:Get*,s3:List*</value>
+ </param>
+ <param>
+ <name>s3.user.policy.resource.guest</name>
+ <value>*</value>
+ </param>
+ <param>
+ <name>s3.group.policy.action.admin</name>
+ <value>*</value>
+ </param>
+ <param>
+ <name>s3.group.policy.resource.admin</name>
+ <value>*</value>
+ </param>
+ </service>
+ */
+
+ String paramName = null;
+ Enumeration<Object> e = context.keys();
+ while (e.hasMoreElements()) {
+ paramName = (String)e.nextElement();
+ if (paramName.startsWith("s3.")) {
+ String[] elements = paramName.split("\\.");
+ if (elements[1].equals("user")) {
+ PolicyConfig policy = userPolicyConfig.get(elements[4]);
+ if (policy == null) {
+ policy = new PolicyConfig();
+ userPolicyConfig.put(elements[4], policy);
+ }
+ if (elements[3].equals("action")) {
+ policy.actions=context.getProperty(paramName);
+ } else {
+ policy.resources=context.getProperty(paramName);
+ }
+ if (policy.actions != null && policy.resources != null) {
+ buildS3PolicyModel(policy);
+ }
+ }else if (elements[1].equals("group")) {
+ PolicyConfig policy = groupPolicyConfig.get(elements[4]);
+ if (policy == null) {
+ policy = new PolicyConfig();
+ groupPolicyConfig.put(elements[4], policy);
+ }
+ if (elements[3].equals("action")) {
+ policy.actions=context.getProperty(paramName);
+ } else {
+ policy.resources=context.getProperty(paramName);
+ }
+ if (policy.actions != null && policy.resources != null) {
+ buildS3PolicyModel(policy);
+ }
+ }
+ }
+ }
+ }
+
+ private void buildS3PolicyModel(PolicyConfig policy) {
+ AWSPolicyModel model = new AWSPolicyModel();
+ model.setEffect("Allow");
+ String[] actions = policy.actions.split(",");
+ for (int i = 0; i < actions.length; i++) {
+ model.addAction(actions[i]);
+ }
+ String[] resources = policy.resources.split(",");
+ if (resources.length > 1) {
+ for (int i = 0; i < resources.length; i++) {
+ model.addResource(resources[i]);
+ }
+ } else {
+ model.setResource(resources[0]);
+ }
+ policy.policy = model.toString();
+ }
+
+ private BasicSessionCredentials getSessionCredentials() {
+ BasicSessionCredentials sessionCredentials = null;
+ try {
+ GetFederationTokenResult result = getFederationTokenResult();
+ Credentials session_creds = result.getCredentials();
+ sessionCredentials = new BasicSessionCredentials(
+ session_creds.getAccessKeyId(),
+ session_creds.getSecretAccessKey(),
+ session_creds.getSessionToken());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return sessionCredentials;
+ }
+
+ public GetFederationTokenResult getFederationTokenResult() {
+ String policy;
+ AWSSecurityTokenService sts_client = AWSSecurityTokenServiceClientBuilder.standard().withRegion(Regions.US_EAST_1).build();
+ String username = null;
+ Subject subject = Subject.getSubject(AccessController.getContext());
+ username = getEffectiveUserName(subject);
+ policy = buildPolicy(username, subject);
+ GetFederationTokenResult result = null;
+ if (policy != null) {
+ GetFederationTokenRequest request = new GetFederationTokenRequest(username).withPolicy(policy);
+ result = sts_client.getFederationToken(request);
+ System.out.println(result.getCredentials());
+ }
+ return result;
+ }
+
+ private String getEffectiveUserName(Subject subject) {
+ return SubjectUtils.getEffectivePrincipalName(subject);
+ }
+
+ private String buildPolicy(String username, Subject subject) {
+ String policy = null;
+ List<String> groupNames = new ArrayList<String>();
+ Object[] groups = subject.getPrincipals(GroupPrincipal.class).toArray();
+ for (int i = 0; i < groups.length; i++) {
+ groupNames.add(
+ ((Principal)groups[0]).getName());
+ }
+
+ PolicyConfig config = userPolicyConfig.get(username);
+ if (config == null) {
+ // check for a group policy match
+ for (String groupName : groupNames) {
+ config = groupPolicyConfig.get(groupName);
+ if (config != null) {
+ // just accept first match for now
+ break;
+ }
+ }
+ }
+ if (config != null) {
+ policy = config.policy;
+ }
+ return policy;
+ }
+
+ private class PolicyConfig {
+ public String actions = null;
+ public String resources = null;
+ public String policy = null;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/S3BucketsResource.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/S3BucketsResource.java b/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/S3BucketsResource.java
index d0b25d7..b261749 100644
--- a/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/S3BucketsResource.java
+++ b/gateway-service-knoxs3/src/main/java/org/apache/knox/gateway/service/knoxs3/S3BucketsResource.java
@@ -18,24 +18,48 @@
package org.apache.knox.gateway.service.knoxs3;
import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.security.ImpersonatedPrincipal;
+import org.apache.knox.gateway.security.PrimaryPrincipal;
+import org.apache.knox.gateway.security.SubjectUtils;
import org.apache.knox.gateway.services.GatewayServices;
import org.apache.knox.gateway.util.JsonUtils;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.Bucket;
+import com.amazonaws.services.s3.model.GetObjectRequest;
+import com.amazonaws.services.s3.model.ListObjectsRequest;
+import com.amazonaws.services.s3.model.ObjectListing;
+import com.amazonaws.services.s3.model.S3Object;
+import com.amazonaws.services.s3.model.S3ObjectSummary;
+
import javax.annotation.PostConstruct;
+import javax.security.auth.Subject;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.StreamingOutput;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
import java.io.PrintWriter;
+import java.io.Writer;
+import java.security.AccessController;
+import java.security.Principal;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
@@ -43,12 +67,16 @@ import java.util.Map;
import java.util.Properties;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.APPLICATION_XML;
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
@Path(S3BucketsResource.RESOURCE_PATH)
public class S3BucketsResource {
private static final String BUCKETS_API_PATH = "buckets";
private static final String BUCKET_API_PATH = "buckets/{id}";
+ private static final String OBJECT_API_PATH = "buckets/{bucket}/{id}";
+ private static final String OBJECTS_API_PATH = "buckets/{bucket}";
private static KnoxS3ServiceMessages log = MessagesFactory.get(KnoxS3ServiceMessages.class);
private static final String VERSION_TAG = "v1";
static final String RESOURCE_PATH = "/knoxs3/" + VERSION_TAG;
@@ -87,14 +115,157 @@ public class S3BucketsResource {
return props;
}
+ @DELETE
+ @Consumes({APPLICATION_XML, APPLICATION_JSON, TEXT_PLAIN})
+ @Produces({APPLICATION_JSON})
+ @Path(OBJECT_API_PATH)
+ public Response deleteObject(@PathParam("bucket") String bucket,
+ @PathParam("id") String id,
+ @Context HttpHeaders headers) {
+ return getDeleteObjectResponse(bucket, id);
+ }
+
+ private Response getDeleteObjectResponse(String bucket, String id) {
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setHeader(CACHE_CONTROL, NO_CACHE);
+ response.setContentType(CONTENT_TYPE);
+ PrintWriter writer = null;
+ try {
+ writer = response.getWriter();
+ doDeleteObject(bucket, id);
+ } catch (Exception ioe) {
+ log.logException("create", ioe);
+ return Response.serverError().entity(String.format("Failed to reply correctly due to : %s ", ioe)).build();
+ } finally {
+ if (writer != null) {
+ writer.close();
+ }
+ }
+ return Response.ok().build();
+ }
+
+ private void doDeleteObject(String bucket, String id) {
+ AmazonS3 s3 = getS3Client();
+ s3.deleteObject(bucket, id);
+ }
+
+ @PUT
+ @Consumes({APPLICATION_XML, APPLICATION_JSON, TEXT_PLAIN})
+ @Produces({APPLICATION_JSON})
+ @Path(OBJECT_API_PATH)
+ public Response createObject(@PathParam("bucket") String bucket,
+ @PathParam("id") String id,
+ @Context HttpHeaders headers, String content) {
+ return getCreateObjectResponse(bucket, id, content);
+ }
+
+ private Response getCreateObjectResponse(String bucket, String id, String content) {
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setHeader(CACHE_CONTROL, NO_CACHE);
+ response.setContentType(CONTENT_TYPE);
+ PrintWriter writer = null;
+ try {
+ writer = response.getWriter();
+ doCreateObject(bucket, id, content);
+ } catch (Exception ioe) {
+ log.logException("create", ioe);
+ return Response.serverError().entity(String.format("Failed to reply correctly due to : %s ", ioe)).build();
+ } finally {
+ if (writer != null) {
+ writer.close();
+ }
+ }
+ return Response.ok().build();
+ }
+
+ private void doCreateObject(String bucket, String id, String content) {
+ AmazonS3 s3 = getS3Client();
+ s3.putObject(bucket, id, content);
+ }
+
@PUT
@Produces({APPLICATION_JSON})
@Path(BUCKET_API_PATH)
public Response createBucket(@PathParam("id") String id) {
+ return getCreateBucketResponse(id);
+ }
+
+ @GET
+ @Produces({APPLICATION_JSON})
+ @Path(OBJECTS_API_PATH)
+ public Response listObjects(@PathParam("bucket") String bucket) {
// use this to get to idbroker service once moved there
GatewayServices services = (GatewayServices) request.getServletContext()
.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
- return getCreateBucketResponse(id);
+ return getListObjectsResponse(bucket);
+ }
+
+ private Response getListObjectsResponse(String bucket) {
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setHeader(CACHE_CONTROL, NO_CACHE);
+ response.setContentType(CONTENT_TYPE);
+ PrintWriter writer = null;
+ try {
+ writer = response.getWriter();
+ writer.println(doGetObjects(bucket));
+ } catch (Exception e) {
+ log.logException("list", e);
+ return Response.serverError().entity(String.format("Failed to reply correctly due to : %s ", e)).build();
+ } finally {
+ if (writer != null) {
+ writer.close();
+ }
+ }
+ return Response.ok().build();
+ }
+
+ private String doGetObjects(String bucket) {
+ AmazonS3 s3 = getS3Client();
+ Map<String, Object> objectMap = new HashMap<String, Object>();
+ ObjectListing objectListing = s3.listObjects(new ListObjectsRequest()
+ .withBucketName(bucket));
+ for (S3ObjectSummary objectSummary : objectListing.getObjectSummaries()) {
+ objectMap.put(objectSummary.getKey(), objectSummary);
+ }
+ return JsonUtils.renderAsJsonString(objectMap);
+ }
+
+ @GET
+ @Produces({APPLICATION_JSON})
+ @Path(OBJECT_API_PATH)
+ public Response listObjects(@PathParam("bucket") String bucket,
+ @PathParam("id") String id) {
+ return getObjectResponse(bucket, id);
+ }
+
+ private Response getObjectResponse(String bucket, String id) {
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setHeader(CACHE_CONTROL, NO_CACHE);
+ response.setContentType(CONTENT_TYPE);
+
+ S3Object o = doGetObject(bucket, id);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(o.getObjectContent()));
+ StreamingOutput stream = new StreamingOutput() {
+ @Override
+ public void write(OutputStream os) throws IOException,
+ WebApplicationException {
+ Writer writer = new BufferedWriter(new OutputStreamWriter(os));
+ while (true) {
+ String line = reader.readLine();
+ if (line == null) break;
+ writer.write(line);
+ }
+ writer.flush();
+ }
+ };
+ return Response.ok(stream).build();
+ }
+
+ private S3Object doGetObject(String bucket, String id) {
+ AmazonS3 s3 = getS3Client();
+ S3Object object = s3.getObject(new GetObjectRequest(bucket, id));
+// System.out.println("Content-Type: " + object.getObjectMetadata().getContentType());
+ return object;
}
private Response getCreateBucketResponse(String id) {
@@ -117,7 +288,7 @@ public class S3BucketsResource {
}
private void doCreateBucket(String id) {
- AmazonS3 s3 = s3b.getS3Client();
+ AmazonS3 s3 = getS3Client();
s3.createBucket(id);
}
@@ -125,9 +296,6 @@ public class S3BucketsResource {
@Produces({APPLICATION_JSON})
@Path(BUCKET_API_PATH)
public Response deleteBucket(@PathParam("id") String id) {
- // use this to get to idbroker service once moved there
- GatewayServices services = (GatewayServices) request.getServletContext()
- .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
return getDeleteBucketResponse(id);
}
@@ -152,7 +320,7 @@ public class S3BucketsResource {
}
private void doDeleteBucket(String id) {
- AmazonS3 s3 = s3b.getS3Client();
+ AmazonS3 s3 = getS3Client();
s3.deleteBucket(id);
}
@@ -160,9 +328,6 @@ public class S3BucketsResource {
@Produces({APPLICATION_JSON})
@Path(BUCKETS_API_PATH)
public Response listBuckets() {
- // use this to get to idbroker service once moved there
- GatewayServices services = (GatewayServices) request.getServletContext()
- .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
return getListBucketsResponse();
}
@@ -186,14 +351,41 @@ public class S3BucketsResource {
}
private String getBuckets() {
- AmazonS3 s3 = s3b.getS3Client();
+ AmazonS3 s3 = getS3Client();
Map<String, Object> bucketMap = new HashMap<String, Object>();
List<Bucket> buckets = s3.listBuckets();
for (Bucket bucket : buckets) {
- System.out.println(bucket.getName());
bucketMap.put(bucket.getName(), bucket);
}
return JsonUtils.renderAsJsonString(bucketMap);
}
+ protected AmazonS3 getS3Client() {
+ HashMap<String, AmazonS3> registry = (HashMap<String, AmazonS3>)
+ context.getAttribute("s3.client.registry");
+
+ if (registry == null) {
+ registry = new HashMap<String, AmazonS3>();
+ context.setAttribute("s3.client.registry", registry);
+ }
+ Subject subject = Subject.getSubject(AccessController.getContext());
+ String username = getEffectiveUserName(subject);
+ AmazonS3 s3 = registry.get(username);
+ if (s3 == null || expired(s3)) {
+ s3 = s3b.getS3Client();
+ registry.put(username, s3);
+ }
+ return s3;
+ }
+
+ private boolean expired(AmazonS3 s3) {
+ // TODO: change to registering a wrapper that keeps track of expiration time
+ // return true when a new client should be create for the effective user name
+ return false;
+ }
+
+ private String getEffectiveUserName(Subject subject) {
+ return SubjectUtils.getEffectivePrincipalName(subject);
+ }
+
}
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-service-knoxs3/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ServiceDeploymentContributor
----------------------------------------------------------------------
diff --git a/gateway-service-knoxs3/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ServiceDeploymentContributor b/gateway-service-knoxs3/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ServiceDeploymentContributor
index fcfbdd7..14cb164 100644
--- a/gateway-service-knoxs3/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ServiceDeploymentContributor
+++ b/gateway-service-knoxs3/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ServiceDeploymentContributor
@@ -16,4 +16,4 @@
# limitations under the License.
##########################################################################
-org.apache.knox.gateway.service.health.deploy.HealthServiceDeploymentContributor
\ No newline at end of file
+org.apache.knox.gateway.service.knoxs3.deploy.KnoxS3ServiceDeploymentContributor
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-service-knoxs3/src/test/java/org/apache/knox/gateway/service/knoxs3/S3BucketsResourceTest.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxs3/src/test/java/org/apache/knox/gateway/service/knoxs3/S3BucketsResourceTest.java b/gateway-service-knoxs3/src/test/java/org/apache/knox/gateway/service/knoxs3/S3BucketsResourceTest.java
index d75c7ce..ec7f438 100644
--- a/gateway-service-knoxs3/src/test/java/org/apache/knox/gateway/service/knoxs3/S3BucketsResourceTest.java
+++ b/gateway-service-knoxs3/src/test/java/org/apache/knox/gateway/service/knoxs3/S3BucketsResourceTest.java
@@ -61,14 +61,14 @@ public class S3BucketsResourceTest {
System.out.println(JsonUtils.renderAsJsonString(policyModel));
- S3PolicyModel model = new S3PolicyModel();
+ AWSPolicyModel model = new AWSPolicyModel();
model.setEffect("Allow");
model.addAction("s3:Get*");
model.addAction("s3:List*");
model.setResource("*");
System.out.println(model);
- model = new S3PolicyModel();
+ model = new AWSPolicyModel();
model.setEffect("Allow");
model.addAction("s3:Get*");
model.addAction("s3:List*");
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-spi/src/main/java/org/apache/knox/gateway/security/ClientImpersonatedPrincipal.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/security/ClientImpersonatedPrincipal.java b/gateway-spi/src/main/java/org/apache/knox/gateway/security/ClientImpersonatedPrincipal.java
new file mode 100644
index 0000000..81f131c
--- /dev/null
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/security/ClientImpersonatedPrincipal.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.security;
+
+import java.security.Principal;
+
+public class ClientImpersonatedPrincipal implements Principal {
+ private String name = null;
+
+ public ClientImpersonatedPrincipal(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-spi/src/main/java/org/apache/knox/gateway/security/SubjectUtils.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/security/SubjectUtils.java b/gateway-spi/src/main/java/org/apache/knox/gateway/security/SubjectUtils.java
index 3fc51ee..65cf9ea 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/security/SubjectUtils.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/security/SubjectUtils.java
@@ -70,6 +70,14 @@ public class SubjectUtils {
return name;
}
+ /**
+ * Determine the effective user name. The order of precedence is:
+ * "mapped, client impersonated, primary. Rationale is that mapping
+ * is how we take actual end users and map them to in cluster users.
+ * Therefore, mapped users take precedence.
+ * @param subject
+ * @return
+ */
public static String getEffectivePrincipalName(Subject subject) {
String name = null;
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-spi/src/main/java/org/apache/knox/gateway/services/s3client/KnoxS3ClientBuilder.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/s3client/KnoxS3ClientBuilder.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/s3client/KnoxS3ClientBuilder.java
deleted file mode 100644
index cebfba0..0000000
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/s3client/KnoxS3ClientBuilder.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.knox.gateway.service.knoxs3;
-
-import java.security.AccessController;
-import java.security.Principal;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.security.auth.Subject;
-
-import org.apache.knox.gateway.security.GroupPrincipal;
-import org.apache.knox.gateway.security.ImpersonatedPrincipal;
-import org.apache.knox.gateway.security.PrimaryPrincipal;
-
-import com.amazonaws.auth.AWSStaticCredentialsProvider;
-import com.amazonaws.auth.BasicSessionCredentials;
-import com.amazonaws.regions.Regions;
-import com.amazonaws.services.s3.AmazonS3;
-import com.amazonaws.services.s3.AmazonS3ClientBuilder;
-import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
-import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
-import com.amazonaws.services.securitytoken.model.Credentials;
-import com.amazonaws.services.securitytoken.model.GetFederationTokenRequest;
-import com.amazonaws.services.securitytoken.model.GetFederationTokenResult;
-
-public class KnoxS3ClientBuilder {
- private Map<String, PolicyConfig> userPolicyConfig = new HashMap<String, PolicyConfig>();
- private Map<String, PolicyConfig> groupPolicyConfig = new HashMap<String, PolicyConfig>();
-
- public KnoxS3ClientBuilder() {
- }
-
- public AmazonS3 getS3Client() {
- BasicSessionCredentials sessionCredentials = getSessionCredentials();
- if (sessionCredentials == null) {
- throw new RuntimeException("No S3 credentials available.");
- }
-
- AmazonS3 s3 = AmazonS3ClientBuilder.standard().withRegion(Regions.US_EAST_1)
- .withCredentials(new AWSStaticCredentialsProvider(sessionCredentials)).build();
- return s3;
- }
-
- public void init(Properties context) {
- buildPolicyMaps(context);
- }
-
- private void buildPolicyMaps(Properties context) {
- /*
- <service>
- <role>KNOXS3</role>
- <param>
- <name>s3.user.policy.action.guest</name>
- <value>s3:Get*,s3:List*</value>
- </param>
- <param>
- <name>s3.user.policy.resource.guest</name>
- <value>*</value>
- </param>
- <param>
- <name>s3.group.policy.action.admin</name>
- <value>*</value>
- </param>
- <param>
- <name>s3.group.policy.resource.admin</name>
- <value>*</value>
- </param>
- </service>
- */
-
- String paramName = null;
- Enumeration<Object> e = context.keys();
- while (e.hasMoreElements()) {
- paramName = (String)e.nextElement();
- if (paramName.startsWith("s3.")) {
- String[] elements = paramName.split("\\.");
- if (elements[1].equals("user")) {
- PolicyConfig policy = userPolicyConfig.get(elements[4]);
- if (policy == null) {
- policy = new PolicyConfig();
- userPolicyConfig.put(elements[4], policy);
- }
- if (elements[3].equals("action")) {
- policy.actions=context.getProperty(paramName);
- } else {
- policy.resources=context.getProperty(paramName);
- }
- if (policy.actions != null && policy.resources != null) {
- buildS3PolicyModel(policy);
- }
- }else if (elements[1].equals("group")) {
- PolicyConfig policy = groupPolicyConfig.get(elements[4]);
- if (policy == null) {
- policy = new PolicyConfig();
- groupPolicyConfig.put(elements[4], policy);
- }
- if (elements[3].equals("action")) {
- policy.actions=context.getProperty(paramName);
- } else {
- policy.resources=context.getProperty(paramName);
- }
- if (policy.actions != null && policy.resources != null) {
- buildS3PolicyModel(policy);
- }
- }
- }
- }
- }
-
- private void buildS3PolicyModel(PolicyConfig policy) {
- S3PolicyModel model = new S3PolicyModel();
- model.setEffect("Allow");
- String[] actions = policy.actions.split(",");
- for (int i = 0; i < actions.length; i++) {
- model.addAction(actions[i]);
- }
- String[] resources = policy.resources.split(",");
- if (resources.length > 1) {
- for (int i = 0; i < resources.length; i++) {
- model.addResource(resources[i]);
- }
- } else {
- model.setResource(resources[0]);
- }
- policy.policy = model.toString();
- }
-
- private BasicSessionCredentials getSessionCredentials() {
- AWSSecurityTokenService sts_client = AWSSecurityTokenServiceClientBuilder.standard().withRegion(Regions.US_EAST_1).build();
- String policy = "";
- BasicSessionCredentials sessionCredentials = null;
- try {
- String username = null;
- Subject subject = Subject.getSubject(AccessController.getContext());
- username = getEffectiveUserName(subject);
- policy = buildPolicy(username, subject);
-
- if (policy != null) {
- GetFederationTokenRequest request = new GetFederationTokenRequest(username).withPolicy(policy);
- GetFederationTokenResult result = sts_client.getFederationToken(request);
- System.out.println(result.getCredentials());
-
- Credentials session_creds = result.getCredentials();
- sessionCredentials = new BasicSessionCredentials(
- session_creds.getAccessKeyId(),
- session_creds.getSecretAccessKey(),
- session_creds.getSessionToken());
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return sessionCredentials;
- }
-
- private String getEffectiveUserName(Subject subject) {
- String username;
- Principal primaryPrincipal = (Principal)subject.getPrincipals(PrimaryPrincipal.class).toArray()[0];
- Object[] impersonations = subject.getPrincipals(ImpersonatedPrincipal.class).toArray();
- if (impersonations.length > 0) {
- username = ((Principal)impersonations[0]).getName();
- }
- else {
- username = primaryPrincipal.getName();
- }
- return username;
- }
-
- private String buildPolicy(String username, Subject subject) {
- String policy = null;
- List<String> groupNames = new ArrayList<String>();
- Object[] groups = subject.getPrincipals(GroupPrincipal.class).toArray();
- for (int i = 0; i < groups.length; i++) {
- groupNames.add(
- ((Principal)groups[0]).getName());
- }
-
- PolicyConfig config = userPolicyConfig.get(username);
- if (config == null) {
- // check for a group policy match
- for (String groupName : groupNames) {
- config = groupPolicyConfig.get(groupName);
- if (config != null) {
- // just accept first match for now
- break;
- }
- }
- }
- if (config != null) {
- policy = config.policy;
- }
- return policy;
- }
-
- private class PolicyConfig {
- public String actions = null;
- public String resources = null;
- public String policy = null;
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/knox/blob/18e49c4b/gateway-spi/src/main/java/org/apache/knox/gateway/services/s3client/S3PolicyModel.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/s3client/S3PolicyModel.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/s3client/S3PolicyModel.java
deleted file mode 100644
index f7f3084..0000000
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/s3client/S3PolicyModel.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.knox.gateway.service.knoxs3;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.knox.gateway.util.JsonUtils;
-
-public class S3PolicyModel {
- HashMap<String, Object> policyModel = new HashMap<String, Object>();
- ArrayList<String> actionArray = new ArrayList<String>();
- HashMap<String, Object> statementMap = new HashMap<String, Object>();
- ArrayList<String> resourcesArray = new ArrayList<String>();
-
- public S3PolicyModel() {
- policyModel.put("Version", "2012-10-17");
- ArrayList<Map<String, Object>> statement = new ArrayList<Map<String, Object>>();
- policyModel.put("Statement", statement );
- statement.add(statementMap);
- statementMap.put("Action", actionArray );
- statementMap.put("Resource", resourcesArray);
- }
-
- public void setEffect(String effect) {
- statementMap.put("Effect", effect);
- }
-
- public void addAction(String action) {
- actionArray.add(action);
- }
-
- public void addResource(String resource) {
- resourcesArray.add(resource);
- }
-
- public void setResource(String resource) {
- statementMap.put("Resource", resource);
- }
-
- public String toString() {
- return JsonUtils.renderAsJsonString(policyModel);
- }
-}