class ZMSImpl implements Authorizer, KeyStore, ZMSHandler {

    @Override
    public void putGroupReview(ResourceContext ctx, String domainName, String groupName, String auditRef, Group group) {

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

        if (readOnlyMode) {
            throw ZMSUtils.requestError(SERVER_READ_ONLY_MESSAGE, caller);
        }

        validateRequest(ctx.request(), caller);

        validate(domainName, TYPE_DOMAIN_NAME, caller);
        validate(groupName, TYPE_ENTITY_NAME, caller);
        validate(group, TYPE_GROUP, 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);
        groupName = groupName.toLowerCase();
        AthenzObject.GROUP.convertToLowerCase(group);

        // verify that request is properly authenticated for this request

        verifyAuthorizedServiceOperation(((RsrcCtxWrapper) ctx).principal().getAuthorizedService(), caller);

        // verify the group name in the URI and request are consistent

        if (!isConsistentGroupName(domainName, groupName, group)) {
            throw ZMSUtils.requestError(caller + ": Inconsistent group names - expected: "
                    + ZMSUtils.groupResourceName(domainName, groupName) + ", actual: "
                    + group.getName(), caller);
        }

        AthenzDomain domain = getAthenzDomain(domainName, false);
        if (domain == null) {
            throw ZMSUtils.notFoundError("No such domain: " + domainName, caller);
        }

        // normalize and remove duplicate members

        normalizeGroupMembers(group);

        // process our request

        dbService.executePutGroupReview(ctx, domainName, groupName, group, auditRef);
    }

    public  ZMSImpl();

}