func (p *policyd) CheckPolicyRoles(ctx context.Context, domain string, roles []string, action, resource string) ([]string, error) {
	ech := make(chan roleEffect, len(roles))
	cctx, cancel := context.WithCancel(ctx)
	defer cancel()
	go func() {
		defer close(ech)
		wg := new(sync.WaitGroup)
		wg.Add(len(roles))
		rp := p.rolePolicies
		for _, role := range roles {
			dr := fmt.Sprintf("%s:role.%s", domain, role)
			go func(role string, ch chan<- roleEffect) {
				defer wg.Done()
				select {
				case <-cctx.Done():
					ch <- roleEffect{Role: role, Effect: cctx.Err()}
					return
				default:
					asss, ok := rp.Get(dr)
					if !ok {
						return
					}
					for _, ass := range asss.([]*Assertion) {
						glg.Debugf("Checking policy domain: %s, role: %v, action: %s, resource: %s, assertion: %v", domain, roles, action, resource, ass)
						select {
						case <-cctx.Done():
							ch <- roleEffect{Role: role, Effect: cctx.Err()}
							return
						default:
							if strings.EqualFold(ass.ResourceDomain, domain) &&
								ass.ActionRegexp.MatchString(strings.ToLower(action)) &&
								ass.ResourceRegexp.MatchString(strings.ToLower(resource)) {
								ch <- roleEffect{Role: role, Effect: ass.Effect}
								return
							}
						}
					}
				}
			}(role, ech)
		}
		wg.Wait()
	}()
	allowedRoles := make([]string, 0, len(roles))
	for re := range ech {
		if re.Effect != nil {  
			glg.Debugf("check policy domain: %s, role: %v, action: %s, resource: %s, result: %v", domain, roles, action, resource, re.Effect)
			return nil, re.Effect
		}
		allowedRoles = append(allowedRoles, re.Role)
	}
	if len(allowedRoles) > 0 {
		glg.Debugf("check policy domain: %s, role: %v, action: %s, resource: %s, result: %v", domain, roles, action, resource, nil)
		return allowedRoles, nil
	}
	err := errors.Wrap(ErrNoMatch, "no match")
	glg.Debugf("check policy domain: %s, role: %v, action: %s, resource: %s, result: %v", domain, roles, action, resource, err)
	return nil, err
}
CheckPolicyRoles checks the specified request has privilege to access the resources or not returning the allowedRoles
and err. If err is nil then the request is allowed, otherwise the request is rejected.
Only action and resource is supporting wildcard, domain and role is not supporting wildcard.
package policy
import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"
	"github.com/AthenZ/athenz/utils/zpe-updater/util"
	"github.com/kpango/fastime"
	"github.com/kpango/gache"
	"github.com/kpango/glg"
	"github.com/pkg/errors"
	"github.com/yahoojapan/athenz-authorizer/v5/pubkey"
	"golang.org/x/sync/errgroup"
)
type Daemon interface {
	Start(context.Context) <-chan error
	Update(context.Context) error
	CheckPolicy(ctx context.Context, domain string, roles []string, action, resource string) error
	CheckPolicyRoles(ctx context.Context, domain string, roles []string, action, resource string) ([]string, error)
	GetPolicyCache(context.Context) map[string]interface{}
}
type roleEffect struct {
	Role   string
	Effect error
}
type policyd struct {
	rolePolicies gache.Gache
	expiryMargin  time.Duration  
	refreshPeriod time.Duration
	purgePeriod   time.Duration
	retryDelay    time.Duration
	retryAttempts int
	athenzURL     string
	athenzDomains []string
	client   *http.Client
	pkp      pubkey.Provider
	fetchers map[string]Fetcher  
}
func New(opts ...Option) (Daemon, error) {
	p := &policyd{
		rolePolicies: gache.New(),
	}
	for _, opt := range append(defaultOptions, opts...) {
		if err := opt(p); err != nil {
			return nil, errors.Wrap(err, "error create policyd")
		}
	}
	p.fetchers = make(map[string]Fetcher, len(p.athenzDomains))
	for _, domain := range p.athenzDomains {
		f := fetcher{
			domain:        domain,
			expiryMargin:  p.expiryMargin,
			retryDelay:    p.retryDelay,
			retryAttempts: p.retryAttempts,
			athenzURL:     p.athenzURL,
			spVerifier: func(sp *SignedPolicy) error {
				return sp.Verify(p.pkp)
			},
			client: p.client,
		}
		p.fetchers[domain] = &f
	}
	return p, nil
}
func (p *policyd) Start(ctx context.Context) <-chan error {
	glg.Info("Starting policyd updater")
	ech := make(chan error, 100)
	fch := make(chan struct{}, 1)
	go func() {
		defer close(fch)
		defer close(ech)
		ticker := time.NewTicker(p.refreshPeriod)
		for {
			select {
			case <-ctx.Done():
				glg.Info("Stopping policyd updater")
				ticker.Stop()
				ech <- ctx.Err()
				return
			case <-fch:
				if err := p.Update(ctx); err != nil {
					ech <- errors.Wrap(err, "error update policy")
					time.Sleep(p.retryDelay)
					select {
					case fch <- struct{}{}:
					default:
						glg.Warn("failure queue already full")
					}
				}
			case <-ticker.C:
				if err := p.Update(ctx); err != nil {
					ech <- errors.Wrap(err, "error update policy")
					select {
					case fch <- struct{}{}:
					default:
						glg.Warn("failure queue already full")
					}
				}
			}
		}
	}()
	return ech
}
func (p *policyd) Update(ctx context.Context) error {
	jobID := fastime.Now().Unix()
	glg.Infof("[%d] will update policy", jobID)
	eg := errgroup.Group{}
	rp := gache.New()
	for _, fetcher := range p.fetchers {
		f := fetcher  
		select {
		case <-ctx.Done():
			glg.Info("Update policy interrupted")
			return ctx.Err()
		default:
			eg.Go(func() error {
				select {
				case <-ctx.Done():
					glg.Info("Update policy interrupted")
					return ctx.Err()
				default:
					return fetchAndCachePolicy(ctx, rp, f)
				}
			})
		}
	}
	if err := eg.Wait(); err != nil {
		glg.Errorf("[%d] update policy fail", jobID)
		return err
	}
	rp.StartExpired(ctx, p.purgePeriod).
		EnableExpiredHook().
		SetExpiredHook(func(ctx context.Context, key string) {
			fetchAndCachePolicy(ctx, p.rolePolicies, p.fetchers[strings.Split(key, ":role.")[0]])
		})
	p.rolePolicies, rp = rp, p.rolePolicies
	glg.Debugf("tmp cache becomes effective")
	rp.Stop()
	rp.Clear()
	glg.Infof("[%d] update policy done", jobID)
	return nil
}
func (p *policyd) CheckPolicy(ctx context.Context, domain string, roles []string, action, resource string) error {
	_, err := p.CheckPolicyRoles(ctx, domain, roles, action, resource)
	return err
}