package client import ( "encoding/json" "fmt" "io/ioutil" "net/http" "reflect" ) // CompanyPrivileges contains the privileges for a combination of email address and company id type CompanyPrivileges struct { Admin bool `json:"admin"` Company bool `json:"company"` Consumer bool `json:"consumer"` Time bool `json:"time"` Invoicing bool `json:"invoicing"` Accounting bool `json:"accounting"` Supplier bool `json:"supplier"` } // PrivilegeAdded is the event sent when a new privilege is added type PrivilegeAdded struct { Email string `json:"email"` CompanyID string `json:"companyId"` Name string `json:"name"` RegistrationNumber string `json:"registrationNumber"` Admin bool `json:"admin"` Company bool `json:"company"` Consumer bool `json:"consumer"` Time bool `json:"time"` Invoicing bool `json:"invoicing"` Accounting bool `json:"accounting"` Supplier bool `json:"supplier"` } // PrivilegeRemoved is the event sent when a privilege is removed type PrivilegeRemoved struct { Email string `json:"email"` CompanyID string `json:"companyId"` Name string `json:"name"` RegistrationNumber string `json:"registrationNumber"` } // PrivilegeHandler processes PrivilegeAdded-events and fetches the initial set of privileges from an authz-service type PrivilegeHandler struct { client *http.Client baseURL string privileges map[string]map[string]CompanyPrivileges } // OptsFunc is used to configure the PrivilegeHandler type OptsFunc func(handler *PrivilegeHandler) // WithBaseURL sets the base URL to the authz-service func WithBaseURL(url string) OptsFunc { return func(handler *PrivilegeHandler) { handler.baseURL = url } } // New creates a new PrivilegeHandler. Pass OptsFuncs to configure. func New(opts ...OptsFunc) *PrivilegeHandler { handler := &PrivilegeHandler{ client: &http.Client{}, baseURL: "http://authz-service", privileges: map[string]map[string]CompanyPrivileges{}, } for _, opt := range opts { opt(handler) } return handler } // Fetch the initial set of privileges from an authz-service func (h *PrivilegeHandler) Fetch() error { resp, err := h.client.Get(fmt.Sprintf("%s/authz", h.baseURL)) if err != nil { return err } buff, err := ioutil.ReadAll(resp.Body) if err != nil { return err } err = json.Unmarshal(buff, &h.privileges) if err != nil { return err } return nil } // Process privilege-related events and update the internal state func (h *PrivilegeHandler) Process(msg interface{}) bool { switch ev := msg.(type) { case *PrivilegeAdded: h.setPrivileges(ev) return true case *PrivilegeRemoved: h.removePrivileges(ev) return true default: fmt.Printf("Got unexpected message type (%s): '%+v'\n", reflect.TypeOf(msg).String(), msg) return false } } func (h *PrivilegeHandler) setPrivileges(ev *PrivilegeAdded) { if priv, exists := h.privileges[ev.Email]; exists { priv[ev.CompanyID] = CompanyPrivileges{ Admin: ev.Admin, Company: ev.Company, Consumer: ev.Consumer, Time: ev.Time, Invoicing: ev.Invoicing, Accounting: ev.Accounting, Supplier: ev.Supplier, } } else { h.privileges[ev.Email] = map[string]CompanyPrivileges{ ev.CompanyID: {}, } h.setPrivileges(ev) } } func (h *PrivilegeHandler) removePrivileges(ev *PrivilegeRemoved) { if priv, exists := h.privileges[ev.Email]; exists { delete(priv, ev.CompanyID) } } // CompaniesByUser return a slice of company ids matching the provided email and predicate func func (h *PrivilegeHandler) CompaniesByUser(email string, predicate func(privileges CompanyPrivileges) bool) []string { var result []string if p, exists := h.privileges[email]; exists { for k, v := range p { if predicate(v) { result = append(result, k) } } } return result } // IsAllowed return true if the provided predicate return true for the privileges matching the provided email and companyID, return false otherwise func (h *PrivilegeHandler) IsAllowed(email, companyID string, predicate func(privileges CompanyPrivileges) bool) bool { if p, exists := h.privileges[email]; exists { if v, exists := p[companyID]; exists { return predicate(v) } } return false }