summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorten Tokle <mortent@verizonmedia.com>2021-06-21 08:20:35 +0200
committerMorten Tokle <mortent@verizonmedia.com>2021-06-21 08:54:01 +0200
commit2f5549df2cae55109dbb5a52beeb9c414cb8bd09 (patch)
tree6fddf76fdeba52ce82b21b7cabbab43e9d445391
parent04ae3583cb45466bd87e0b23032951740e0ed090 (diff)
Only approve allowed operators
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzAccessControlService.java12
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClientMock.java6
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzGroup.java41
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/DefaultZmsClient.java17
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClient.java3
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/MembershipEntity.java47
6 files changed, 115 insertions, 11 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzAccessControlService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzAccessControlService.java
index 233759f47a7..0be32165916 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzAccessControlService.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzAccessControlService.java
@@ -3,6 +3,7 @@
package com.yahoo.vespa.hosted.controller.api.integration.athenz;
import com.yahoo.vespa.athenz.api.AthenzDomain;
+import com.yahoo.vespa.athenz.api.AthenzGroup;
import com.yahoo.vespa.athenz.api.AthenzRole;
import com.yahoo.vespa.athenz.api.AthenzUser;
import com.yahoo.vespa.athenz.client.zms.ZmsClient;
@@ -14,18 +15,25 @@ import java.util.stream.Collectors;
public class AthenzAccessControlService implements AccessControlService {
+ private static final String ALLOWED_OPERATOR_GROUPNAME = "vespa-team";
private static final String DATAPLANE_ACCESS_ROLENAME = "operator-data-plane";
private final ZmsClient zmsClient;
private final AthenzRole dataPlaneAccessRole;
+ private final AthenzGroup vespaTeam;
public AthenzAccessControlService(ZmsClient zmsClient, AthenzDomain domain) {
this.zmsClient = zmsClient;
this.dataPlaneAccessRole = new AthenzRole(domain, DATAPLANE_ACCESS_ROLENAME);
+ this.vespaTeam = new AthenzGroup(domain, ALLOWED_OPERATOR_GROUPNAME);
}
@Override
public boolean approveDataPlaneAccess(AthenzUser user, Instant expiry) {
+ // Can only approve team members, other members must be manually approved
+ if(!isVespaTeamMember(user)) {
+ throw new IllegalArgumentException(String.format("User %s requires manual approval, please contact Vespa team", user.getName()));
+ }
List<AthenzUser> users = zmsClient.listPendingRoleApprovals(dataPlaneAccessRole);
if (users.contains(user)) {
zmsClient.approvePendingRoleMembership(dataPlaneAccessRole, user, expiry);
@@ -42,4 +50,8 @@ public class AthenzAccessControlService implements AccessControlService {
.map(AthenzUser.class::cast)
.collect(Collectors.toList());
}
+
+ public boolean isVespaTeamMember(AthenzUser user) {
+ return zmsClient.getGroupMembership(vespaTeam, user);
+ }
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClientMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClientMock.java
index deeecf217e7..ed84a9b0a76 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClientMock.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClientMock.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.api.integration.athenz;
import com.yahoo.vespa.athenz.api.AthenzDomain;
+import com.yahoo.vespa.athenz.api.AthenzGroup;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzResourceName;
import com.yahoo.vespa.athenz.api.AthenzRole;
@@ -98,6 +99,11 @@ public class ZmsClientMock implements ZmsClient {
}
@Override
+ public boolean getGroupMembership(AthenzGroup group, AthenzIdentity identity) {
+ return false;
+ }
+
+ @Override
public List<AthenzDomain> getDomainList(String prefix) {
log("getDomainList()");
return new ArrayList<>(athenz.domains.keySet());
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzGroup.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzGroup.java
new file mode 100644
index 00000000000..2608af381a2
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzGroup.java
@@ -0,0 +1,41 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+package com.yahoo.vespa.athenz.api;
+
+import java.util.Objects;
+
+public class AthenzGroup {
+ private final AthenzDomain domain;
+ private final String groupName;
+
+ public AthenzGroup(AthenzDomain domain, String groupName) {
+ this.domain = domain;
+ this.groupName = groupName;
+ }
+
+ public AthenzGroup(String domain, String groupName) {
+ this.domain = new AthenzDomain(domain);
+ this.groupName = groupName;
+ }
+
+ public AthenzDomain domain() {
+ return domain;
+ }
+
+ public String groupName() {
+ return groupName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AthenzGroup that = (AthenzGroup) o;
+ return Objects.equals(domain, that.domain) && Objects.equals(groupName, that.groupName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(domain, groupName);
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/DefaultZmsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/DefaultZmsClient.java
index f73ac9c3535..5817eb0c8d2 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/DefaultZmsClient.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/DefaultZmsClient.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.athenz.client.zms;
import com.yahoo.vespa.athenz.api.AthenzDomain;
+import com.yahoo.vespa.athenz.api.AthenzGroup;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzResourceName;
import com.yahoo.vespa.athenz.api.AthenzRole;
@@ -112,7 +113,7 @@ public class DefaultZmsClient extends ClientBase implements ZmsClient {
@Override
public void addRoleMember(AthenzRole role, AthenzIdentity member) {
URI uri = zmsUrl.resolve(String.format("domain/%s/role/%s/member/%s", role.domain().getName(), role.roleName(), member.getFullName()));
- MembershipEntity membership = new MembershipEntity(member.getFullName(), true, role.roleName(), null);
+ MembershipEntity membership = new MembershipEntity.RoleMembershipEntity(member.getFullName(), true, role.roleName(), null);
HttpUriRequest request = RequestBuilder.put(uri)
.setEntity(toJsonStringEntity(membership))
.build();
@@ -133,6 +134,18 @@ public class DefaultZmsClient extends ClientBase implements ZmsClient {
.setUri(uri)
.build();
return execute(request, response -> {
+ MembershipEntity membership = readEntity(response, MembershipEntity.GroupMembershipEntity.class);
+ return membership.isMember;
+ });
+ }
+
+ @Override
+ public boolean getGroupMembership(AthenzGroup group, AthenzIdentity identity) {
+ URI uri = zmsUrl.resolve(String.format("domain/%s/group/%s/member/%s", group.domain().getName(), group.groupName(), identity.getFullName()));
+ HttpUriRequest request = RequestBuilder.get()
+ .setUri(uri)
+ .build();
+ return execute(request, response -> {
MembershipEntity membership = readEntity(response, MembershipEntity.class);
return membership.isMember;
});
@@ -223,7 +236,7 @@ public class DefaultZmsClient extends ClientBase implements ZmsClient {
@Override
public void approvePendingRoleMembership(AthenzRole athenzRole, AthenzUser athenzUser, Instant expiry) {
URI uri = zmsUrl.resolve(String.format("domain/%s/role/%s/member/%s/decision", athenzRole.domain().getName(), athenzRole.roleName(), athenzUser.getFullName()));
- MembershipEntity membership = new MembershipEntity(athenzUser.getFullName(), true, athenzRole.roleName(), Long.toString(expiry.getEpochSecond()));
+ MembershipEntity membership = new MembershipEntity.RoleMembershipEntity(athenzUser.getFullName(), true, athenzRole.roleName(), Long.toString(expiry.getEpochSecond()));
HttpUriRequest request = RequestBuilder.put()
.setUri(uri)
.setEntity(toJsonStringEntity(membership))
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClient.java
index 15e8ba77850..245078e3679 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClient.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClient.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.athenz.client.zms;
import com.yahoo.vespa.athenz.api.AthenzDomain;
+import com.yahoo.vespa.athenz.api.AthenzGroup;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzResourceName;
import com.yahoo.vespa.athenz.api.AthenzRole;
@@ -36,6 +37,8 @@ public interface ZmsClient extends AutoCloseable {
boolean getMembership(AthenzRole role, AthenzIdentity identity);
+ boolean getGroupMembership(AthenzGroup group, AthenzIdentity identity);
+
List<AthenzDomain> getDomainList(String prefix);
boolean hasAccess(AthenzResourceName resource, String action, AthenzIdentity identity);
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/MembershipEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/MembershipEntity.java
index d0672473776..33acf0e1c90 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/MembershipEntity.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/MembershipEntity.java
@@ -16,17 +16,14 @@ import com.fasterxml.jackson.annotation.JsonProperty;
public class MembershipEntity {
public final String memberName;
public final boolean isMember;
- public final String roleName;
public final String expiration;
@JsonCreator
public MembershipEntity(@JsonProperty("memberName") String memberName,
@JsonProperty("isMember") boolean isMember,
- @JsonProperty("roleName") String roleName,
@JsonProperty("expiration") String expiration) {
this.memberName = memberName;
this.isMember = isMember;
- this.roleName = roleName;
this.expiration = expiration;
}
@@ -40,13 +37,45 @@ public class MembershipEntity {
return isMember;
}
- @JsonGetter("roleName")
- public String roleName() {
- return roleName;
- }
-
@JsonGetter("expiration")
public String expiration() {
return expiration;
}
-}
+
+ public static class RoleMembershipEntity extends MembershipEntity {
+ public final String roleName;
+
+ @JsonCreator
+ public RoleMembershipEntity(@JsonProperty("memberName") String memberName,
+ @JsonProperty("isMember") boolean isMember,
+ @JsonProperty("roleName") String roleName,
+ @JsonProperty("expiration") String expiration) {
+ super(memberName, isMember, expiration);
+ this.roleName = roleName;
+ }
+
+ @JsonGetter("roleName")
+ public String roleName() {
+ return roleName;
+ }
+
+ }
+
+ public static class GroupMembershipEntity extends MembershipEntity {
+ public final String groupName;
+
+ @JsonCreator
+ public GroupMembershipEntity(@JsonProperty("memberName") String memberName,
+ @JsonProperty("isMember") boolean isMember,
+ @JsonProperty("groupName") String groupName,
+ @JsonProperty("expiration") String expiration) {
+ super(memberName, isMember, expiration);
+ this.groupName = groupName;
+ }
+
+ @JsonGetter("groupName")
+ public String roleName() {
+ return groupName;
+ }
+ }
+} \ No newline at end of file