Skip to content

Commit

Permalink
Added query to get all roles for principal (AthenZ#1034)
Browse files Browse the repository at this point in the history
  • Loading branch information
OferLevi85 authored Jul 15, 2020
1 parent 79bd600 commit bfce9e2
Show file tree
Hide file tree
Showing 18 changed files with 745 additions and 8 deletions.
32 changes: 32 additions & 0 deletions clients/go/zms/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,38 @@ func (client ZMSClient) GetDomainRoleMembers(domainName DomainName) (*DomainRole
}
}

func (client ZMSClient) GetPrincipalRoles(principal EntityName, domainName DomainName) (*DomainRoleMember, error) {
var data *DomainRoleMember
url := client.URL + "/role" + encodeParams(encodeStringParam("principal", string(principal), ""), encodeStringParam("domain", string(domainName), ""))
resp, err := client.httpGet(url, nil)
if err != nil {
return data, err
}
defer resp.Body.Close()
switch resp.StatusCode {
case 200:
err = json.NewDecoder(resp.Body).Decode(&data)
if err != nil {
return data, err
}
return data, nil
default:
var errobj rdl.ResourceError
contentBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return data, err
}
json.Unmarshal(contentBytes, &errobj)
if errobj.Code == 0 {
errobj.Code = resp.StatusCode
}
if errobj.Message == "" {
errobj.Message = string(contentBytes)
}
return data, errobj
}
}

func (client ZMSClient) PutMembership(domainName DomainName, roleName EntityName, memberName MemberName, auditRef string, membership *Membership) error {
headers := map[string]string{
"Y-Audit-Ref": auditRef,
Expand Down
13 changes: 13 additions & 0 deletions clients/go/zms/zms_schema.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion clients/java/zms/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<description>ZMS Java Client Library</description>

<properties>
<code.coverage.min>0.42</code.coverage.min>
<code.coverage.min>0.95</code.coverage.min>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,30 @@ public DomainRoleMembers getDomainRoleMembers(String domainName) {

}

public DomainRoleMember getPrincipalRoles(String principal, String domainName) {
WebTarget target = base.path("/role");
if (principal != null) {
target = target.queryParam("principal", principal);
}
if (domainName != null) {
target = target.queryParam("domain", domainName);
}
Invocation.Builder invocationBuilder = target.request("application/json");
if (credsHeader != null) {
invocationBuilder = credsHeader.startsWith("Cookie.") ? invocationBuilder.cookie(credsHeader.substring(7),
credsToken) : invocationBuilder.header(credsHeader, credsToken);
}
Response response = invocationBuilder.get();
int code = response.getStatus();
switch (code) {
case 200:
return response.readEntity(DomainRoleMember.class);
default:
throw new ResourceException(code, response.readEntity(ResourceError.class));
}

}

public Membership putMembership(String domainName, String roleName, String memberName, String auditRef, Membership membership) {
WebTarget target = base.path("/domain/{domainName}/role/{roleName}/member/{memberName}")
.resolveTemplate("domainName", domainName)
Expand Down
18 changes: 18 additions & 0 deletions core/zms/src/main/java/com/yahoo/athenz/zms/ZMSSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,24 @@ private static Schema build() {
.exception("UNAUTHORIZED", "ResourceError", "")
;

sb.resource("DomainRoleMember", "GET", "/role")
.comment("Fetch all the roles across domains by either calling or specified principal")
.name("getPrincipalRoles")
.queryParam("principal", "principal", "EntityName", null, "If not present, will return roles for the user making the call")
.queryParam("domain", "domainName", "DomainName", null, "If not present, will return roles from all domains")
.auth("", "", true)
.expected("OK")
.exception("BAD_REQUEST", "ResourceError", "")

.exception("FORBIDDEN", "ResourceError", "")

.exception("NOT_FOUND", "ResourceError", "")

.exception("TOO_MANY_REQUESTS", "ResourceError", "")

.exception("UNAUTHORIZED", "ResourceError", "")
;

sb.resource("Membership", "PUT", "/domain/{domainName}/role/{roleName}/member/{memberName}")
.comment("Add the specified user to the role's member list. If the role is neither auditEnabled nor selfserve, then it will use authorize (\"update\", \"{domainName}:role.{roleName}\") otherwise membership will be sent for approval to either designated delegates ( in case of auditEnabled roles ) or to domain admins ( in case of selfserve roles )")
.pathParam("domainName", "DomainName", "name of the domain")
Expand Down
16 changes: 15 additions & 1 deletion core/zms/src/main/rdl/Role.rdli
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,20 @@ resource DomainRoleMembers GET "/domain/{domainName}/member" {
}
}

// Fetch all the roles across domains by either calling or specified principal
resource DomainRoleMember GET "/role?principal={principal}&domain={domainName}" (name=getPrincipalRoles) {
EntityName principal (optional); //If not present, will return roles for the user making the call
DomainName domainName (optional); //If not present, will return roles from all domains
authenticate;
exceptions {
ResourceError BAD_REQUEST;
ResourceError NOT_FOUND;
ResourceError FORBIDDEN;
ResourceError UNAUTHORIZED;
ResourceError TOO_MANY_REQUESTS;
}
}

//Add the specified user to the role's member list.
//If the role is neither auditEnabled nor selfserve, then it will use authorize ("update", "{domainName}:role.{roleName}")
//otherwise membership will be sent for approval to either designated delegates ( in case of auditEnabled roles ) or to
Expand Down Expand Up @@ -289,4 +303,4 @@ resource Role PUT "/domain/{domainName}/role/{roleName}/review" (name=PutRoleRev
ResourceError CONFLICT;
ResourceError TOO_MANY_REQUESTS;
}
}
}
11 changes: 9 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,18 @@
<goal>check</goal>
</goals>
<configuration>
<excludes>
<exclude>*Test</exclude>
<exclude>**/*ZMSRDLGeneratedClient.class</exclude>
<exclude>**/ZMSResources.class</exclude>
<exclude>**/ZMSHandler.class</exclude>
<exclude>**/ZMSConsts.class</exclude>
<exclude>**/FileConnection.class</exclude>
<exclude>**/FileObjectStore.class</exclude>
</excludes>
<rules>
<rules>
<rule>
<element>PACKAGE</element>
<excludes>*Test</excludes>
<limits>
<limit>
<counter>LINE</counter>
Expand Down
6 changes: 3 additions & 3 deletions servers/zms/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,9 @@
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/ZMSResources.*</exclude>
<exclude>**/FileConnection.*</exclude>
<exclude>**/FileObjectStore.*</exclude>
<exclude>**/ZMSResources.class</exclude>
<exclude>**/FileConnection.class</exclude>
<exclude>**/FileObjectStore.class</exclude>
</excludes>
</configuration>
</plugin>
Expand Down
6 changes: 6 additions & 0 deletions servers/zms/src/main/java/com/yahoo/athenz/zms/DBService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2034,6 +2034,12 @@ DomainRoleMembers listDomainRoleMembers(String domainName) {
}
}

DomainRoleMember getPrincipalRoles(String principal, String domainName) {
try (ObjectStoreConnection con = store.getConnection(true, false)) {
return con.getPrincipalRoles(principal, domainName);
}
}

Role getRole(String domainName, String roleName, Boolean auditLog, Boolean expand, Boolean pending) {

try (ObjectStoreConnection con = store.getConnection(true, false)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public interface ZMSHandler {
Membership getMembership(ResourceContext context, String domainName, String roleName, String memberName, String expiration);
DomainRoleMembers getOverdueReview(ResourceContext context, String domainName);
DomainRoleMembers getDomainRoleMembers(ResourceContext context, String domainName);
DomainRoleMember getPrincipalRoles(ResourceContext context, String principal, String domainName);
void putMembership(ResourceContext context, String domainName, String roleName, String memberName, String auditRef, Membership membership);
void deleteMembership(ResourceContext context, String domainName, String roleName, String memberName, String auditRef);
void deletePendingMembership(ResourceContext context, String domainName, String roleName, String memberName, String auditRef);
Expand Down
34 changes: 34 additions & 0 deletions servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import com.yahoo.rdl.UUID;
import com.yahoo.rdl.Validator;
import com.yahoo.rdl.Validator.Result;
import org.eclipse.jetty.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -2956,6 +2957,39 @@ public DomainRoleMembers getDomainRoleMembers(ResourceContext ctx, String domain
return roleMembers;
}

@Override
public DomainRoleMember getPrincipalRoles(ResourceContext context, String principal, String domainName) {
final String caller = "getPrincipalRoles";
metric.increment(ZMSConsts.HTTP_GET);
logPrincipal(context);

if (StringUtil.isEmpty(principal)) {
// If principal not specified, get roles for current user
principal = ((RsrcCtxWrapper) context).principal().getFullName();
}
validateRequest(context.request(), caller);
validate(principal, TYPE_ENTITY_NAME, caller);

// for consistent handling of all requests, we're going to convert
// all incoming object values into lower case (e.g. domain, role,
// policy, service, etc name)

principal = principal.toLowerCase();

if (!StringUtil.isEmpty(domainName)) {
validate(domainName, TYPE_DOMAIN_NAME, caller);
domainName = domainName.toLowerCase();
}

metric.increment(ZMSConsts.HTTP_REQUEST, null);
metric.increment(caller, null);
Object timerMetric = metric.startTiming("getPrincipalRoles_timing", null);

DomainRoleMember roleMember = dbService.getPrincipalRoles(principal, domainName);
metric.stopTiming(timerMetric, null, null);
return roleMember;
}

@Override
public Role getRole(ResourceContext ctx, String domainName, String roleName,
Boolean auditLog, Boolean expand, Boolean pending) {
Expand Down
28 changes: 28 additions & 0 deletions servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSResources.java
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,34 @@ public DomainRoleMembers getDomainRoleMembers(@PathParam("domainName") String do
}
}

@GET
@Path("/role")
@Produces(MediaType.APPLICATION_JSON)
public DomainRoleMember getPrincipalRoles(@QueryParam("principal") String principal, @QueryParam("domain") String domainName) {
try {
ResourceContext context = this.delegate.newResourceContext(this.request, this.response);
context.authenticate();
return this.delegate.getPrincipalRoles(context, principal, domainName);
} catch (ResourceException e) {
int code = e.getCode();
switch (code) {
case ResourceException.BAD_REQUEST:
throw typedException(code, e, ResourceError.class);
case ResourceException.FORBIDDEN:
throw typedException(code, e, ResourceError.class);
case ResourceException.NOT_FOUND:
throw typedException(code, e, ResourceError.class);
case ResourceException.TOO_MANY_REQUESTS:
throw typedException(code, e, ResourceError.class);
case ResourceException.UNAUTHORIZED:
throw typedException(code, e, ResourceError.class);
default:
System.err.println("*** Warning: undeclared exception (" + code + ") for resource getPrincipalRoles");
throw typedException(code, e, ResourceError.class);
}
}
}

@PUT
@Path("/domain/{domainName}/role/{roleName}/member/{memberName}")
@Consumes(MediaType.APPLICATION_JSON)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public interface ObjectStoreConnection extends Closeable {
boolean confirmRoleMember(String domainName, String roleName, RoleMember roleMember, String principal, String auditRef);

DomainRoleMembers listDomainRoleMembers(String domainName);
DomainRoleMember getPrincipalRoles(String principal, String domainName);
List<PrincipalRole> listRolesWithUserAuthorityRestrictions();

// Policy commands
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.yahoo.athenz.zms.utils.ZMSUtils;
import com.yahoo.rdl.JSON;
import com.yahoo.rdl.Timestamp;
import org.eclipse.jetty.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -1639,6 +1640,52 @@ public DomainRoleMembers listDomainRoleMembers(String domainName) {
return listReviewRoleMembersWithFilter(domainName, (roleMember -> true), "listDomainRoleMembers");
}

@Override
public DomainRoleMember getPrincipalRoles(String principal, String domainName) {
DomainRoleMember domainRoleMember = new DomainRoleMember();
domainRoleMember.setMemberName(principal);
domainRoleMember.setMemberRoles(new ArrayList<>());

if (!StringUtil.isEmpty(domainName)) {
getDomainRolesForPrincipal(principal, domainRoleMember, domainName);
} else {
String[] fnames = getDomainList();
for (String domain : fnames) {
getDomainRolesForPrincipal(principal, domainRoleMember, domain);
}
}

return domainRoleMember;
}

private void getDomainRolesForPrincipal(String principal, DomainRoleMember domainRoleMember, String domain) {
DomainStruct dom = getDomainStruct(domain);
if (dom == null) {
return;
}

for (Role role: dom.getRoles().values()) {
List<RoleMember> roleMembers = role.getRoleMembers();
if (roleMembers == null) {
continue;
}
for (RoleMember roleMember : roleMembers) {
if (!roleMember.getMemberName().equals(principal)) {
continue;
}

MemberRole memberRole = new MemberRole();
memberRole.setMemberName(principal);
memberRole.setDomainName(domain);
memberRole.setReviewReminder(roleMember.getReviewReminder());
memberRole.setExpiration(roleMember.getExpiration());
memberRole.setRoleName(role.getName());
memberRole.setSystemDisabled(roleMember.getSystemDisabled());
domainRoleMember.getMemberRoles().add(memberRole);
}
}
}

@Override
public DomainRoleMembers listOverdueReviewRoleMembers(String domainName) {
return listReviewRoleMembersWithFilter(domainName, (roleMember -> {
Expand Down
Loading

0 comments on commit bfce9e2

Please sign in to comment.