github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/policy/checker.go (about)

     1  package policy
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"code.cloudfoundry.org/lager"
     8  	"github.com/jessevdk/go-flags"
     9  )
    10  
    11  const ActionUseImage = "UseImage"
    12  
    13  type PolicyCheckNotPass struct {
    14  	Reasons []string
    15  }
    16  
    17  func (e PolicyCheckNotPass) Error() string {
    18  	return fmt.Sprintf("policy check failed: %s", strings.Join(e.Reasons, ", "))
    19  }
    20  
    21  type Filter struct {
    22  	HttpMethods   []string `long:"policy-check-filter-http-method" description:"API http method to go through policy check"`
    23  	Actions       []string `long:"policy-check-filter-action" description:"Actions in the list will go through policy check"`
    24  	ActionsToSkip []string `long:"policy-check-filter-action-skip" description:"Actions the list will not go through policy check"`
    25  }
    26  
    27  type PolicyCheckInput struct {
    28  	Service        string      `json:"service"`
    29  	ClusterName    string      `json:"cluster_name"`
    30  	ClusterVersion string      `json:"cluster_version"`
    31  	HttpMethod     string      `json:"http_method,omitempty"`
    32  	Action         string      `json:"action"`
    33  	User           string      `json:"user,omitempty"`
    34  	Team           string      `json:"team,omitempty"`
    35  	Roles          []string    `json:"roles,omitempty"`
    36  	Pipeline       string      `json:"pipeline,omitempty"`
    37  	Data           interface{} `json:"data,omitempty"`
    38  }
    39  
    40  type PolicyCheckOutput struct {
    41  	Allowed bool
    42  	Reasons []string
    43  }
    44  
    45  // FailedPolicyCheck creates a generic failed check
    46  func FailedPolicyCheck() PolicyCheckOutput {
    47  	return PolicyCheckOutput{
    48  		Allowed: false,
    49  		Reasons: []string{},
    50  	}
    51  }
    52  
    53  // PassedPolicyCheck creates a generic passed check
    54  func PassedPolicyCheck() PolicyCheckOutput {
    55  	return PolicyCheckOutput{
    56  		Allowed: true,
    57  		Reasons: []string{},
    58  	}
    59  }
    60  
    61  //go:generate counterfeiter . Agent
    62  
    63  // Agent should be implemented by policy agents.
    64  type Agent interface {
    65  	// Check returns true if passes policy check. If not goes through policy
    66  	// check, just return true.
    67  	Check(PolicyCheckInput) (PolicyCheckOutput, error)
    68  }
    69  
    70  //go:generate counterfeiter . AgentFactory
    71  
    72  type AgentFactory interface {
    73  	Description() string
    74  	IsConfigured() bool
    75  	NewAgent(lager.Logger) (Agent, error)
    76  }
    77  
    78  var agentFactories []AgentFactory
    79  
    80  func RegisterAgent(factory AgentFactory) {
    81  	agentFactories = append(agentFactories, factory)
    82  }
    83  
    84  func WireCheckers(group *flags.Group) {
    85  	for _, factory := range agentFactories {
    86  		_, err := group.AddGroup(fmt.Sprintf("Policy Check Agent (%s)", factory.Description()), "", factory)
    87  		if err != nil {
    88  			panic(err)
    89  		}
    90  	}
    91  }
    92  
    93  var (
    94  	clusterName    string
    95  	clusterVersion string
    96  )
    97  
    98  //go:generate counterfeiter . Checker
    99  
   100  type Checker interface {
   101  	ShouldCheckHttpMethod(string) bool
   102  	ShouldCheckAction(string) bool
   103  	ShouldSkipAction(string) bool
   104  
   105  	Check(input PolicyCheckInput) (PolicyCheckOutput, error)
   106  }
   107  
   108  func Initialize(logger lager.Logger, cluster string, version string, filter Filter) (Checker, error) {
   109  	logger.Debug("policy-checker-initialize")
   110  
   111  	clusterName = cluster
   112  	clusterVersion = version
   113  
   114  	var checkerDescriptions []string
   115  	for _, factory := range agentFactories {
   116  		if factory.IsConfigured() {
   117  			checkerDescriptions = append(checkerDescriptions, factory.Description())
   118  		}
   119  	}
   120  	if len(checkerDescriptions) > 1 {
   121  		return nil, fmt.Errorf("Multiple policy checker configured: %s", strings.Join(checkerDescriptions, ", "))
   122  	}
   123  
   124  	for _, factory := range agentFactories {
   125  		if factory.IsConfigured() {
   126  			agent, err := factory.NewAgent(logger.Session("policy-checker"))
   127  			if err != nil {
   128  				return nil, err
   129  			}
   130  
   131  			logger.Info("warning-experiment-policy-check",
   132  				lager.Data{"rfc": "https://github.com/concourse/rfcs/pull/41"})
   133  
   134  			return &AgentChecker{
   135  				filter: filter,
   136  				agent:  agent,
   137  			}, nil
   138  		}
   139  	}
   140  
   141  	// No policy checker configured.
   142  	return NoopChecker{}, nil
   143  }
   144  
   145  type AgentChecker struct {
   146  	filter Filter
   147  	agent  Agent
   148  }
   149  
   150  func (c *AgentChecker) ShouldCheckHttpMethod(method string) bool {
   151  	return inArray(c.filter.HttpMethods, method)
   152  }
   153  
   154  func (c *AgentChecker) ShouldCheckAction(action string) bool {
   155  	return inArray(c.filter.Actions, action)
   156  }
   157  
   158  func (c *AgentChecker) ShouldSkipAction(action string) bool {
   159  	return inArray(c.filter.ActionsToSkip, action)
   160  }
   161  
   162  func inArray(array []string, target string) bool {
   163  	found := false
   164  	for _, ele := range array {
   165  		if ele == target {
   166  			found = true
   167  			break
   168  		}
   169  	}
   170  	return found
   171  }
   172  
   173  func (c *AgentChecker) Check(input PolicyCheckInput) (PolicyCheckOutput, error) {
   174  	input.Service = "concourse"
   175  	input.ClusterName = clusterName
   176  	input.ClusterVersion = clusterVersion
   177  	return c.agent.Check(input)
   178  }
   179  
   180  type NoopChecker struct{}
   181  
   182  func (noop NoopChecker) ShouldCheckHttpMethod(string) bool { return false }
   183  func (noop NoopChecker) ShouldCheckAction(string) bool     { return false }
   184  func (noop NoopChecker) ShouldSkipAction(string) bool      { return true }
   185  
   186  func (noop NoopChecker) Check(PolicyCheckInput) (PolicyCheckOutput, error) {
   187  	return PolicyCheckOutput{Allowed: true}, nil
   188  }