package accessctl import ( "errors" "fmt" "dehub.dev/src/dehub.git/typeobj" ) // ErrFilterNoMatch is returned from a FilterInterface's Match method when the // given request was not matched to the filter due to the request itself (as // opposed to some error in the filter's definition). type ErrFilterNoMatch struct { Err error } func (err ErrFilterNoMatch) Error() string { return fmt.Sprintf("matching with filter: %s", err.Err.Error()) } // Filter describes the methods that all Filters must implement. type Filter interface { // MatchCommit returns nil if the CommitRequest is matched by the filter, // otherwise it returns an error (ErrFilterNoMatch if the error is due to // the CommitRequest). MatchCommit(CommitRequest) error } // FilterUnion represents an access control filter being defined in the Config. // Only one of its fields may be filled at a time. type FilterUnion struct { Signature *FilterSignature `type:"signature"` Branch *FilterBranch `type:"branch"` FilesChanged *FilterFilesChanged `type:"files_changed"` PayloadType *FilterPayloadType `type:"payload_type"` CommitAttributes *FilterCommitAttributes `type:"commit_attributes"` Not *FilterNot `type:"not"` } // MarshalYAML implements the yaml.Marshaler interface. func (f FilterUnion) MarshalYAML() (interface{}, error) { return typeobj.MarshalYAML(f) } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (f *FilterUnion) UnmarshalYAML(unmarshal func(interface{}) error) error { return typeobj.UnmarshalYAML(f, unmarshal) } // Filter returns the Filter encapsulated by this FilterUnion. // // This method will panic if a Filter field is not populated. func (f FilterUnion) Filter() Filter { el, _, err := typeobj.Element(f) if err != nil { panic(err) } return el.(Filter) } // Type returns the Filter's type (as would be used in its YAML "type" field). // // This will panic if a Filter field is not populated. func (f FilterUnion) Type() string { _, typeStr, err := typeobj.Element(f) if err != nil { panic(err) } return typeStr } // FilterPayloadType filters by what type of payload is being requested. Exactly // one of its fields should be filled. type FilterPayloadType struct { Type string `yaml:"payload_type"` Types []string `yaml:"payload_types"` } var _ Filter = FilterPayloadType{} // MatchCommit implements the method for FilterInterface. func (f FilterPayloadType) MatchCommit(req CommitRequest) error { switch { case f.Type != "": if f.Type != req.Type { return ErrFilterNoMatch{ Err: fmt.Errorf("payload type %q does not match filter's type %q", req.Type, f.Type), } } return nil case len(f.Types) > 0: for _, typ := range f.Types { if typ == req.Type { return nil } } return ErrFilterNoMatch{ Err: fmt.Errorf("payload type %q does not match any of filter's types %+v", req.Type, f.Types), } default: return errors.New(`one of the following fields must be set: "payload_type", "payload_types"`) } } // FilterCommitAttributes filters by one more attributes a commit can have. If // more than one field is filled in then all relevant attributes must be present // on the commit for this filter to match. type FilterCommitAttributes struct { NonFastForward bool `yaml:"non_fast_forward"` } var _ Filter = FilterCommitAttributes{} // MatchCommit implements the method for FilterInterface. func (f FilterCommitAttributes) MatchCommit(req CommitRequest) error { if f.NonFastForward && !req.NonFastForward { return ErrFilterNoMatch{Err: errors.New("commit is a fast-forward")} } return nil }