Add group_id, rate_limit and protocol AS config options (#478)

* Add group_id, rate_limit and protocol AS config options

* We currently just record and error check these options. There are not
currently implemented.

Signed-off-by: Andrew Morgan <andrewm@matrix.org>

* Clean things up and fix yaml declaration

* Warn loudly when app service requests unimplemented options

* Fix comments

* Remove high cyclomatic complexity of appservice checkErrors

* Set default rate limited to true
This commit is contained in:
Andrew Morgan 2018-06-18 02:43:15 -07:00 committed by GitHub
parent 78440083df
commit 93b7b18646
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 13 deletions

View file

@ -182,6 +182,12 @@ func (s *OutputRoomEventConsumer) filterRoomserverEvents(ctx context.Context, ev
// appserviceIsInterestedInEvent returns a boolean depending on whether a given // appserviceIsInterestedInEvent returns a boolean depending on whether a given
// event falls within one of a given application service's namespaces. // event falls within one of a given application service's namespaces.
func (s *OutputRoomEventConsumer) appserviceIsInterestedInEvent(ctx context.Context, event gomatrixserverlib.Event, appservice config.ApplicationService) bool { func (s *OutputRoomEventConsumer) appserviceIsInterestedInEvent(ctx context.Context, event gomatrixserverlib.Event, appservice config.ApplicationService) bool {
// No reason to queue events if they'll never be sent to the application
// service
if appservice.URL == "" {
return false
}
// Check sender of the event // Check sender of the event
for _, userNamespace := range appservice.NamespaceMap["users"] { for _, userNamespace := range appservice.NamespaceMap["users"] {
if userNamespace.RegexpObject.MatchString(event.Sender()) { if userNamespace.RegexpObject.MatchString(event.Sender()) {

View file

@ -21,6 +21,7 @@ import (
"regexp" "regexp"
"strings" "strings"
log "github.com/sirupsen/logrus"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
) )
@ -31,6 +32,14 @@ type ApplicationServiceNamespace struct {
Exclusive bool `yaml:"exclusive"` Exclusive bool `yaml:"exclusive"`
// A regex pattern that represents the namespace // A regex pattern that represents the namespace
Regex string `yaml:"regex"` Regex string `yaml:"regex"`
// The ID of an existing group that all users of this application service will
// be added to. This field is only relevant to the `users` namespace.
// Note that users who are joined to this group through an application service
// are not to be listed when querying for the group's members, however the
// group should be listed when querying an application service user's groups.
// This is to prevent making spamming all users of an application service
// trivial.
GroupID string `yaml:"group_id"`
// Regex object representing our pattern. Saves having to recompile every time // Regex object representing our pattern. Saves having to recompile every time
RegexpObject *regexp.Regexp RegexpObject *regexp.Regexp
} }
@ -51,14 +60,20 @@ type ApplicationService struct {
// Information about an application service's namespaces. Key is either // Information about an application service's namespaces. Key is either
// "users", "aliases" or "rooms" // "users", "aliases" or "rooms"
NamespaceMap map[string][]ApplicationServiceNamespace `yaml:"namespaces"` NamespaceMap map[string][]ApplicationServiceNamespace `yaml:"namespaces"`
// Whether rate limiting is applied to each application service user
RateLimited bool `yaml:"rate_limited"`
// Any custom protocols that this application service provides (e.g. IRC)
Protocols []string `yaml:"protocols"`
} }
// loadAppservices iterates through all application service config files // loadAppservices iterates through all application service config files
// and loads their data into the config object for later access. // and loads their data into the config object for later access.
func loadAppservices(config *Dendrite) error { func loadAppservices(config *Dendrite) error {
for _, configPath := range config.ApplicationServices.ConfigFiles { for _, configPath := range config.ApplicationServices.ConfigFiles {
// Create a new application service // Create a new application service with default options
var appservice ApplicationService appservice := ApplicationService{
RateLimited: true,
}
// Create an absolute path from a potentially relative path // Create an absolute path from a potentially relative path
absPath, err := filepath.Abs(configPath) absPath, err := filepath.Abs(configPath)
@ -161,8 +176,20 @@ func checkErrors(config *Dendrite) (err error) {
var idMap = make(map[string]bool) var idMap = make(map[string]bool)
var tokenMap = make(map[string]bool) var tokenMap = make(map[string]bool)
// Compile regexp object for checking groupIDs
groupIDRegexp := regexp.MustCompile(`\+.*:.*`)
// Check each application service for any config errors // Check each application service for any config errors
for _, appservice := range config.Derived.ApplicationServices { for _, appservice := range config.Derived.ApplicationServices {
// Namespace-related checks
for key, namespaceSlice := range appservice.NamespaceMap {
for _, namespace := range namespaceSlice {
if err := validateNamespace(&appservice, key, &namespace, groupIDRegexp); err != nil {
return err
}
}
}
// Check if we've already seen this ID. No two application services // Check if we've already seen this ID. No two application services
// can have the same ID or token. // can have the same ID or token.
if idMap[appservice.ID] { if idMap[appservice.ID] {
@ -193,24 +220,53 @@ func checkErrors(config *Dendrite) (err error) {
)}) )})
} }
} }
}
// Check that namespace(s) are valid regex // TODO: Remove once rate_limited is implemented
for _, appservice := range config.Derived.ApplicationServices { if appservice.RateLimited {
for _, namespaceSlice := range appservice.NamespaceMap { log.Warn("WARNING: Application service option rate_limited is currently unimplemented")
for _, namespace := range namespaceSlice { }
if !IsValidRegex(namespace.Regex) { // TODO: Remove once protocols is implemented
return configErrors([]string{fmt.Sprintf( if len(appservice.Protocols) > 0 {
"Invalid regex string for Application Service %s", appservice.ID, log.Warn("WARNING: Application service option protocols is currently unimplemented")
)})
}
}
} }
} }
return setupRegexps(config) return setupRegexps(config)
} }
// validateNamespace returns nil or an error based on whether a given
// application service namespace is valid. A namespace is valid if it has the
// required fields, and its regex is correct.
func validateNamespace(
appservice *ApplicationService,
key string,
namespace *ApplicationServiceNamespace,
groupIDRegexp *regexp.Regexp,
) error {
// Check that namespace(s) are valid regex
if !IsValidRegex(namespace.Regex) {
return configErrors([]string{fmt.Sprintf(
"Invalid regex string for Application Service %s", appservice.ID,
)})
}
// Check if GroupID for the users namespace is in the correct format
if key == "users" && namespace.GroupID != "" {
// TODO: Remove once group_id is implemented
log.Warn("WARNING: Application service option group_id is currently unimplemented")
correctFormat := groupIDRegexp.MatchString(namespace.GroupID)
if !correctFormat {
return configErrors([]string{fmt.Sprintf(
"Invalid user group_id field for application service %s.",
appservice.ID,
)})
}
}
return nil
}
// IsValidRegex returns true or false based on whether the // IsValidRegex returns true or false based on whether the
// given string is valid regex or not // given string is valid regex or not
func IsValidRegex(regexString string) bool { func IsValidRegex(regexString string) bool {