1package policy
2
3import (
4	"fmt"
5	"strings"
6
7	"code.cloudfoundry.org/lager"
8	"github.com/jessevdk/go-flags"
9)
10
11const ActionUseImage = "UseImage"
12
13type PolicyCheckNotPass struct {
14	Reasons []string
15}
16
17func (e PolicyCheckNotPass) Error() string {
18	return fmt.Sprintf("policy check failed: %s", strings.Join(e.Reasons, ", "))
19}
20
21type 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
27type 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
40type PolicyCheckOutput struct {
41	Allowed bool
42	Reasons []string
43}
44
45// FailedPolicyCheck creates a generic failed check
46func FailedPolicyCheck() PolicyCheckOutput {
47	return PolicyCheckOutput{
48		Allowed: false,
49		Reasons: []string{},
50	}
51}
52
53// PassedPolicyCheck creates a generic passed check
54func 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.
64type 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
72type AgentFactory interface {
73	Description() string
74	IsConfigured() bool
75	NewAgent(lager.Logger) (Agent, error)
76}
77
78var agentFactories []AgentFactory
79
80func RegisterAgent(factory AgentFactory) {
81	agentFactories = append(agentFactories, factory)
82}
83
84func 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
93var (
94	clusterName    string
95	clusterVersion string
96)
97
98func Initialize(logger lager.Logger, cluster string, version string, filter Filter) (*Checker, error) {
99	logger.Debug("policy-checker-initialize")
100
101	clusterName = cluster
102	clusterVersion = version
103
104	var checkerDescriptions []string
105	for _, factory := range agentFactories {
106		if factory.IsConfigured() {
107			checkerDescriptions = append(checkerDescriptions, factory.Description())
108		}
109	}
110	if len(checkerDescriptions) > 1 {
111		return nil, fmt.Errorf("Multiple policy checker configured: %s", strings.Join(checkerDescriptions, ", "))
112	}
113
114	for _, factory := range agentFactories {
115		if factory.IsConfigured() {
116			agent, err := factory.NewAgent(logger.Session("policy-checker"))
117			if err != nil {
118				return nil, err
119			}
120
121			logger.Info("warning-experiment-policy-check",
122				lager.Data{"rfc": "https://github.com/concourse/rfcs/pull/41"})
123
124			return &Checker{
125				filter: filter,
126				agent:  agent,
127			}, nil
128		}
129	}
130
131	// No policy checker configured.
132	return nil, nil
133}
134
135type Checker struct {
136	filter Filter
137	agent  Agent
138}
139
140func (c *Checker) ShouldCheckHttpMethod(method string) bool {
141	return inArray(c.filter.HttpMethods, method)
142}
143
144func (c *Checker) ShouldCheckAction(action string) bool {
145	return inArray(c.filter.Actions, action)
146}
147
148func (c *Checker) ShouldSkipAction(action string) bool {
149	return inArray(c.filter.ActionsToSkip, action)
150}
151
152func inArray(array []string, target string) bool {
153	found := false
154	for _, ele := range array {
155		if ele == target {
156			found = true
157			break
158		}
159	}
160	return found
161}
162
163func (c *Checker) Check(input PolicyCheckInput) (PolicyCheckOutput, error) {
164	input.Service = "concourse"
165	input.ClusterName = clusterName
166	input.ClusterVersion = clusterVersion
167	return c.agent.Check(input)
168}
169