class ZMSImpl implements Authorizer, KeyStore, ZMSHandler {

    @Override
    public void putMembership(ResourceContext ctx, String domainName, String roleName,
            String memberName, String auditRef, Membership membership) {

        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);
        setRequestDomain(ctx, domainName);
        validate(roleName, TYPE_ENTITY_NAME, caller);
        validate(memberName, TYPE_MEMBER_NAME, caller);
        validate(membership, TYPE_MEMBERSHIP, 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();
        roleName = roleName.toLowerCase();
        memberName = memberName.toLowerCase();
        AthenzObject.MEMBERSHIP.convertToLowerCase(membership);

        final Principal principal = ((RsrcCtxWrapper) ctx).principal();

        // verify that request is properly authenticated for this request

        verifyAuthorizedServiceRoleOperation(principal.getAuthorizedService(), caller, roleName);

        // verify that the member name in the URI and object provided match

        if (!memberName.equals(membership.getMemberName())) {
            throw ZMSUtils.requestError("putMembership: Member name in URI and Membership object do not match", caller);
        }

        // role name is optional so we'll verify only if the value is present in the object

        if (membership.getRoleName() != null && !roleName.equals(membership.getRoleName())) {
            throw ZMSUtils.requestError("putMembership: Role name in URI and Membership object do not match", caller);
        }

        // extract our role object to get its attributes

        AthenzDomain domain = getAthenzDomain(domainName, false);
        Role role = getRoleFromDomain(roleName, domain);

        if (role == null) {
            throw ZMSUtils.requestError("Invalid role name specified", caller);
        }

        // create and normalize the role member object

        RoleMember roleMember = new RoleMember();
        roleMember.setMemberName(normalizeDomainAliasUser(memberName));
        roleMember.setPrincipalType(principalType(roleMember.getMemberName()));
        setRoleMemberExpiration(domain, role, roleMember, membership, caller);
        setRoleMemberReview(role, roleMember, membership);

        // check to see if we need to validate the principal

        final String userAuthorityFilter = enforcedUserAuthorityFilter(role.getUserAuthorityFilter(),
                domain.getDomain().getUserAuthorityFilter());
        boolean disallowGroups = ADMIN_ROLE_NAME.equals(roleName);
        validateRoleMemberPrincipal(roleMember.getMemberName(), roleMember.getPrincipalType(), userAuthorityFilter,
                role.getUserAuthorityExpiration(), role.getAuditEnabled(), disallowGroups, caller);

        // authorization check which also automatically updates
        // the active and approved flags for the request

        if (!isAllowedPutMembership(principal, domain, role, roleMember)) {
            throw ZMSUtils.forbiddenError("putMembership: principal is not authorized to add members", caller);
        }

        // add the member to the specified role

        dbService.executePutMembership(ctx, domainName, roleName, roleMember, auditRef, caller);

        // new role member with pending status. Notify approvers

        if (roleMember.getApproved() == Boolean.FALSE) {
            sendMembershipApprovalNotification(domainName, domain.getDomain().getOrg(), roleName,
                    roleMember.getMemberName(), auditRef, principal.getFullName(), role);
        }
    }

    public  ZMSImpl();

}