package appservice import ( "crypto/sha256" "fmt" ) // Service is one registered application service. type Service struct { *Registration // sender is the full user ID of SenderLocalpart. sender string } // SenderUserID is the service's own user. func (s *Service) SenderUserID() string { return s.sender } // OwnsUser reports whether the service may act as a user: its own sender, // or anybody in its user namespaces, exclusive or not. func (s *Service) OwnsUser(userID string) bool { return userID == s.sender || matchesAny(s.Namespaces.Users, userID) } // ClaimsUserExclusively reports whether a user ID is in one of the // service's exclusive user namespaces, which nobody else may register. func (s *Service) ClaimsUserExclusively(userID string) bool { return matchesExclusive(s.Namespaces.Users, userID) } // OwnsAlias reports whether an alias is in the service's alias // namespaces. func (s *Service) OwnsAlias(alias string) bool { return matchesAny(s.Namespaces.Aliases, alias) } // ClaimsAliasExclusively reports whether an alias is in an exclusive alias // namespace of the service. func (s *Service) ClaimsAliasExclusively(alias string) bool { return matchesExclusive(s.Namespaces.Aliases, alias) } // InterestedInUser reports whether events by or about a user concern the // service. func (s *Service) InterestedInUser(userID string) bool { return s.OwnsUser(userID) } // InterestedInRoom reports whether a room ID is in the service's room // namespaces. func (s *Service) InterestedInRoom(roomID string) bool { return matchesAny(s.Namespaces.Rooms, roomID) } func matchesAny(list []Namespace, id string) bool { for _, ns := range list { if ns.Matches(id) { return true } } return false } func matchesExclusive(list []Namespace, id string) bool { for _, ns := range list { if ns.Exclusive && ns.Matches(id) { return true } } return false } // Registry is every application service this server knows, looked up by // ID and by token. type Registry struct { services []*Service byID map[string]*Service // byToken is keyed by the SHA-256 of the as_token, not the token: a // map lookup's timing depends on the key, and the key an attacker // controls should not be the secret itself. byToken map[[sha256.Size]byte]*Service } // NewRegistry builds a registry, refusing two services with one ID or one // token, or a token shared with another service's hs_token. func NewRegistry(serverName string, regs []*Registration) (*Registry, error) { r := &Registry{ byID: map[string]*Service{}, byToken: map[[sha256.Size]byte]*Service{}, } hsTokens := map[string]string{} for _, reg := range regs { hsTokens[reg.HSToken] = reg.ID } for _, reg := range regs { if _, dup := r.byID[reg.ID]; dup { return nil, fmt.Errorf("application service %q is registered twice", reg.ID) } key := sha256.Sum256([]byte(reg.ASToken)) if other, dup := r.byToken[key]; dup { return nil, fmt.Errorf("application services %q and %q have the same as_token", other.ID, reg.ID) } if other, ok := hsTokens[reg.ASToken]; ok { return nil, fmt.Errorf("the as_token of application service %q is the hs_token of %q", reg.ID, other) } svc := &Service{Registration: reg, sender: "@" + reg.SenderLocalpart + ":" + serverName} r.services = append(r.services, svc) r.byID[reg.ID] = svc r.byToken[key] = svc } return r, nil } // Services lists every service, in the order they were registered. func (r *Registry) Services() []*Service { if r == nil { return nil } return r.services } // Empty reports whether no service is registered. func (r *Registry) Empty() bool { return r == nil || len(r.services) == 0 } // ByID returns a service by ID, or nil. func (r *Registry) ByID(id string) *Service { if r == nil { return nil } return r.byID[id] } // ByToken returns the service an as_token belongs to, or nil. func (r *Registry) ByToken(token string) *Service { if r == nil || token == "" { return nil } return r.byToken[sha256.Sum256([]byte(token))] } // ExclusiveUserOwner returns the service that claims a user ID // exclusively, or nil. The sender of a service counts as claimed by it. func (r *Registry) ExclusiveUserOwner(userID string) *Service { for _, svc := range r.Services() { if svc.sender == userID || svc.ClaimsUserExclusively(userID) { return svc } } return nil } // ExclusiveAliasOwner returns the service that claims an alias // exclusively, or nil. func (r *Registry) ExclusiveAliasOwner(alias string) *Service { for _, svc := range r.Services() { if svc.ClaimsAliasExclusively(alias) { return svc } } return nil } // UserOwners lists the services with a URL whose user namespaces match a // user: the ones to ask whether it exists. func (r *Registry) UserOwners(userID string) []*Service { var out []*Service for _, svc := range r.Services() { if svc.URL != "" && matchesAny(svc.Namespaces.Users, userID) { out = append(out, svc) } } return out } // AliasOwners lists the services with a URL whose alias namespaces match // an alias. func (r *Registry) AliasOwners(alias string) []*Service { var out []*Service for _, svc := range r.Services() { if svc.URL != "" && svc.OwnsAlias(alias) { out = append(out, svc) } } return out }