Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement domain group members api #2641

Merged
merged 1 commit into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions clients/go/zms/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2201,6 +2201,38 @@ func (client ZMSClient) PutResourceGroupOwnership(domainName DomainName, groupNa
}
}

func (client ZMSClient) GetDomainGroupMembers(domainName DomainName) (*DomainGroupMembers, error) {
var data *DomainGroupMembers
url := client.URL + "/domain/" + fmt.Sprint(domainName) + "/group/member"
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 := io.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) GetPolicyList(domainName DomainName, limit *int32, skip string) (*PolicyList, error) {
var data *PolicyList
url := client.URL + "/domain/" + fmt.Sprint(domainName) + "/policy" + encodeParams(encodeOptionalInt32Param("limit", limit), encodeStringParam("skip", string(skip), ""))
Expand Down
11 changes: 11 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.

19 changes: 19 additions & 0 deletions clients/java/zms/src/main/java/com/yahoo/athenz/zms/ZMSClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -3759,6 +3759,25 @@ public GroupMembership getGroupMembership(String domainName, String groupName, S
}
}

/**
* Retrieve the list of all members provisioned for a domain
* in groups
*
* @param domainName name of the domain
* @return DomainGroupMembers object that includes the list of members with their groups
* @throws ZMSClientException in case of failure
*/
public DomainGroupMembers getDomainGroupMembers(String domainName) {
updatePrincipal();
try {
return client.getDomainGroupMembers(domainName);
} catch (ResourceException ex) {
throw new ZMSClientException(ex.getCode(), ex.getData());
} catch (Exception ex) {
throw new ZMSClientException(ResourceException.BAD_REQUEST, ex.getMessage());
}
}

/**
* Fetch all the groups across domains by either calling or specified principal
* @param principal - Requested principal. If null will return groups for the user making the call
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2105,6 +2105,34 @@ public ResourceGroupOwnership putResourceGroupOwnership(String domainName, Strin
}
}

public DomainGroupMembers getDomainGroupMembers(String domainName) throws URISyntaxException, IOException {
UriTemplateBuilder uriTemplateBuilder = new UriTemplateBuilder(baseUrl, "/domain/{domainName}/group/member")
.resolveTemplate("domainName", domainName);
URIBuilder uriBuilder = new URIBuilder(uriTemplateBuilder.getUri());
HttpUriRequest httpUriRequest = RequestBuilder.get()
.setUri(uriBuilder.build())
.build();
if (credsHeader != null) {
httpUriRequest.addHeader(credsHeader, credsToken);
}
HttpEntity httpResponseEntity = null;
try (CloseableHttpResponse httpResponse = client.execute(httpUriRequest, httpContext)) {
int code = httpResponse.getStatusLine().getStatusCode();
httpResponseEntity = httpResponse.getEntity();
switch (code) {
case 200:
return jsonMapper.readValue(httpResponseEntity.getContent(), DomainGroupMembers.class);
default:
final String errorData = (httpResponseEntity == null) ? null : EntityUtils.toString(httpResponseEntity);
throw (errorData != null && !errorData.isEmpty())
? new ResourceException(code, jsonMapper.readValue(errorData, ResourceError.class))
: new ResourceException(code);
}
} finally {
EntityUtils.consumeQuietly(httpResponseEntity);
}
}

public PolicyList getPolicyList(String domainName, Integer limit, String skip) throws URISyntaxException, IOException {
UriTemplateBuilder uriTemplateBuilder = new UriTemplateBuilder(baseUrl, "/domain/{domainName}/policy")
.resolveTemplate("domainName", domainName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3536,6 +3536,56 @@ public void testGetDomainRoleMembers() throws URISyntaxException, IOException {
}
}

@Test
public void testGetDomainGroupMembers() throws URISyntaxException, IOException {
ZMSClient client = createClient(systemAdminUser);
ZMSRDLGeneratedClient c = Mockito.mock(ZMSRDLGeneratedClient.class);
client.setZMSRDLGeneratedClient(c);

GroupMember memberGroup = new GroupMember();
memberGroup.setGroupName("readers");

List<GroupMember> memberGroups = new ArrayList<>();
memberGroups.add(memberGroup);

DomainGroupMember member = new DomainGroupMember();
member.setMemberName("athenz.api");
member.setMemberGroups(memberGroups);

List<DomainGroupMember> members = new ArrayList<>();
members.add(member);

DomainGroupMembers domainGroupMembers = new DomainGroupMembers();
domainGroupMembers.setMembers(members);

Mockito.when(c.getDomainGroupMembers("athenz"))
.thenReturn(domainGroupMembers)
.thenThrow(new ZMSClientException(401, "fail"))
.thenThrow(new IllegalArgumentException("other-error"));

DomainGroupMembers retMembers = client.getDomainGroupMembers("athenz");
assertNotNull(retMembers);
assertEquals(retMembers.getMembers().get(0).getMemberName(), "athenz.api");

// second time it fails with zms client exception

try {
client.getDomainGroupMembers("athenz");
fail();
} catch (ZMSClientException ex) {
assertEquals(401, ex.getCode());
}

// last time with std exception - resulting in 400

try {
client.getDomainGroupMembers("athenz");
fail();
} catch (ZMSClientException ex) {
assertEquals(400, ex.getCode());
}
}

@Test
public void testGetPrincipalRoles() throws URISyntaxException, IOException {
ZMSClient client = createClient(systemAdminUser);
Expand Down
16 changes: 16 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 @@ -2069,6 +2069,22 @@ private static Schema build() {
.exception("UNAUTHORIZED", "ResourceError", "")
;

sb.resource("DomainGroupMembers", "GET", "/domain/{domainName}/group/member")
.comment("Get list of principals defined in groups in the given domain")
.pathParam("domainName", "DomainName", "name of the domain")
.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("PolicyList", "GET", "/domain/{domainName}/policy")
.comment("List policies provisioned in this namespace.")
.pathParam("domainName", "DomainName", "name of the domain")
Expand Down
13 changes: 13 additions & 0 deletions core/zms/src/main/rdl/Group.rdli
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,16 @@ resource ResourceGroupOwnership PUT "/domain/{domainName}/group/{groupName}/owne
ResourceError TOO_MANY_REQUESTS;
}
}

//Get list of principals defined in groups in the given domain
resource DomainGroupMembers GET "/domain/{domainName}/group/member" {
DomainName domainName; //name of the domain
authenticate;
exceptions {
ResourceError BAD_REQUEST;
ResourceError NOT_FOUND;
ResourceError FORBIDDEN;
ResourceError UNAUTHORIZED;
ResourceError TOO_MANY_REQUESTS;
}
}
13 changes: 13 additions & 0 deletions libs/go/zmscli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,10 @@ func (cli Zms) EvalCommand(params []string) (*string, error) {
if argc == 0 {
return cli.ListDomainRoleMembers(dn)
}
case "list-domain-group-members":
if argc == 0 {
return cli.ListDomainGroupMembers(dn)
}
case "list-group", "list-groups":
return cli.ListGroups(dn)
case "show-group":
Expand Down Expand Up @@ -2253,6 +2257,15 @@ func (cli Zms) HelpSpecificCommand(interactive bool, cmd string) string {
buf.WriteString(" role : name of the role to be deleted\n")
buf.WriteString(" examples:\n")
buf.WriteString(" " + domainExample + " delete-role readers\n")
case "list-domain-group-members":
buf.WriteString(" syntax:\n")
buf.WriteString(" " + domainParam + " list-domain-group-members\n")
buf.WriteString(" parameters:\n")
if !interactive {
buf.WriteString(" domain : name of the domain\n")
}
buf.WriteString(" examples:\n")
buf.WriteString(" " + domainExample + " list-domain-group-members\n")
case "list-group":
buf.WriteString(" syntax:\n")
buf.WriteString(" " + domainParam + " list-group\n")
Expand Down
17 changes: 17 additions & 0 deletions libs/go/zmscli/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -699,3 +699,20 @@ func (cli Zms) SetGroupPrincipalDomainFilter(dn, gn, domainFilter string) (*stri

return cli.dumpByFormat(message, cli.buildYAMLOutput)
}

func (cli Zms) ListDomainGroupMembers(dn string) (*string, error) {
groupMembers, err := cli.Zms.GetDomainGroupMembers(zms.DomainName(dn))
if err != nil {
return nil, err
}

oldYamlConverter := func(res interface{}) (*string, error) {
var buf bytes.Buffer
buf.WriteString("group members:\n")
cli.dumpDomainGroupMembers(&buf, groupMembers, false)
s := buf.String()
return &s, nil
}

return cli.dumpByFormat(groupMembers, oldYamlConverter)
}
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 @@ -3503,6 +3503,12 @@ DomainRoleMembers listDomainRoleMembers(String domainName) {
}
}

DomainGroupMembers listDomainGroupMembers(String domainName) {
try (ObjectStoreConnection con = store.getConnection(true, false)) {
return con.listDomainGroupMembers(domainName);
}
}

DomainRoleMember getPrincipalRoles(String principal, String domainName, Boolean expand) {

DomainRoleMember principalRoles;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public interface ZMSHandler {
Response putGroupReview(ResourceContext context, String domainName, String groupName, String auditRef, Boolean returnObj, String resourceOwner, Group group);
DomainGroupMembership getPendingDomainGroupMembersList(ResourceContext context, String principal, String domainName);
void putResourceGroupOwnership(ResourceContext context, String domainName, String groupName, String auditRef, ResourceGroupOwnership resourceOwnership);
DomainGroupMembers getDomainGroupMembers(ResourceContext context, String domainName);
PolicyList getPolicyList(ResourceContext context, String domainName, Integer limit, String skip);
Policies getPolicies(ResourceContext context, String domainName, Boolean assertions, Boolean includeNonActive, String tagKey, String tagValue);
Policy getPolicy(ResourceContext context, String domainName, String policyName);
Expand Down
19 changes: 19 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 @@ -3901,6 +3901,25 @@ public DomainRoleMembers getDomainRoleMembers(ResourceContext ctx, String domain
return dbService.listDomainRoleMembers(domainName);
}

@Override
public DomainGroupMembers getDomainGroupMembers(ResourceContext ctx, String domainName) {

final String caller = ctx.getApiName();
logPrincipal(ctx);

validateRequest(ctx.request(), caller);
validate(domainName, TYPE_DOMAIN_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)

domainName = domainName.toLowerCase();
setRequestDomain(ctx, domainName);

return dbService.listDomainGroupMembers(domainName);
}

@Override
public DomainRoleMember getPrincipalRoles(ResourceContext context, String principal, String domainName, Boolean expand) {
final String caller = context.getApiName();
Expand Down
34 changes: 34 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 @@ -2166,6 +2166,40 @@ public void putResourceGroupOwnership(
}
}

@GET
@Path("/domain/{domainName}/group/member")
@Produces(MediaType.APPLICATION_JSON)
@Operation(description = "Get list of principals defined in groups in the given domain")
public DomainGroupMembers getDomainGroupMembers(
@Parameter(description = "name of the domain", required = true) @PathParam("domainName") String domainName) {
int code = ResourceException.OK;
ResourceContext context = null;
try {
context = this.delegate.newResourceContext(this.servletContext, this.request, this.response, "getDomainGroupMembers");
context.authenticate();
return this.delegate.getDomainGroupMembers(context, domainName);
} catch (ResourceException e) {
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 getDomainGroupMembers");
throw typedException(code, e, ResourceError.class);
}
} finally {
this.delegate.recordMetrics(context, code);
}
}

@GET
@Path("/domain/{domainName}/policy")
@Produces(MediaType.APPLICATION_JSON)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public interface ObjectStoreConnection extends Closeable {
boolean deletePendingGroupMember(String domainName, String groupName, String member, String principal, String auditRef);
boolean confirmGroupMember(String domainName, String groupName, GroupMember groupMember, String principal, String auditRef);

DomainGroupMembers listDomainGroupMembers(String domainName);
DomainGroupMember getPrincipalGroups(String principal, String domainName);
List<PrincipalGroup> listGroupsWithUserAuthorityRestrictions();

Expand Down
Loading
Loading