Add User consent configuration

Add consentAPIMux
This commit is contained in:
Till Faelligen 2022-02-14 12:59:13 +01:00
parent 5106cc807c
commit 5702b84dae
4 changed files with 107 additions and 4 deletions

View file

@ -68,6 +68,31 @@ global:
# to other servers and the federation API will not be exposed. # to other servers and the federation API will not be exposed.
disable_federation: false disable_federation: false
# Consent tracking configuration
# If either require_at_registration or send_server_notice_to_guest are true, consent
# messages will be sent to the users.
user_consent:
# Require consent when user registers for the first time
require_at_registration: false
# The name to be shown to the user
policy_name: "Privacy policy"
# The directory to search for templates
template_dir: "./templates/privacy"
# The version of the policy. When loading templates, ".gohtml" template is added as a suffix
# e.g: ${template_dir}/1.0.gohtml needs to exist, if this is set to "1.0"
version: "1.0"
# Send a consent message to guest users
send_server_notice_to_guest: false
# Default message to send to users
server_notice_content:
msg_type: "m.text"
body: >-
Please give your consent to the privacy policy at {{ .ConsentURL }}.
# The error message to display if the user hasn't given their consent yet
block_events_error: >-
You can't send any messages until you consent to the privacy policy at
{{ .ConsentURL }}.
# Configuration for NATS JetStream # Configuration for NATS JetStream
jetstream: jetstream:
# A list of NATS Server addresses to connect to. If none are specified, an # A list of NATS Server addresses to connect to. If none are specified, an

View file

@ -21,6 +21,7 @@ import (
"io" "io"
"net" "net"
"net/http" "net/http"
_ "net/http/pprof"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
@ -56,8 +57,6 @@ import (
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
userapiinthttp "github.com/matrix-org/dendrite/userapi/inthttp" userapiinthttp "github.com/matrix-org/dendrite/userapi/inthttp"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
_ "net/http/pprof"
) )
// BaseDendrite is a base for creating new instances of dendrite. It parses // BaseDendrite is a base for creating new instances of dendrite. It parses
@ -74,6 +73,7 @@ type BaseDendrite struct {
PublicKeyAPIMux *mux.Router PublicKeyAPIMux *mux.Router
PublicMediaAPIMux *mux.Router PublicMediaAPIMux *mux.Router
PublicWellKnownAPIMux *mux.Router PublicWellKnownAPIMux *mux.Router
PublicConsentAPIMux *mux.Router
InternalAPIMux *mux.Router InternalAPIMux *mux.Router
SynapseAdminMux *mux.Router SynapseAdminMux *mux.Router
UseHTTPAPIs bool UseHTTPAPIs bool
@ -205,6 +205,7 @@ func NewBaseDendrite(cfg *config.Dendrite, componentName string, options ...Base
PublicKeyAPIMux: mux.NewRouter().SkipClean(true).PathPrefix(httputil.PublicKeyPathPrefix).Subrouter().UseEncodedPath(), PublicKeyAPIMux: mux.NewRouter().SkipClean(true).PathPrefix(httputil.PublicKeyPathPrefix).Subrouter().UseEncodedPath(),
PublicMediaAPIMux: mux.NewRouter().SkipClean(true).PathPrefix(httputil.PublicMediaPathPrefix).Subrouter().UseEncodedPath(), PublicMediaAPIMux: mux.NewRouter().SkipClean(true).PathPrefix(httputil.PublicMediaPathPrefix).Subrouter().UseEncodedPath(),
PublicWellKnownAPIMux: mux.NewRouter().SkipClean(true).PathPrefix(httputil.PublicWellKnownPrefix).Subrouter().UseEncodedPath(), PublicWellKnownAPIMux: mux.NewRouter().SkipClean(true).PathPrefix(httputil.PublicWellKnownPrefix).Subrouter().UseEncodedPath(),
PublicConsentAPIMux: mux.NewRouter().SkipClean(true).PathPrefix("/_matrix").Subrouter().UseEncodedPath(),
InternalAPIMux: mux.NewRouter().SkipClean(true).PathPrefix(httputil.InternalPathPrefix).Subrouter().UseEncodedPath(), InternalAPIMux: mux.NewRouter().SkipClean(true).PathPrefix(httputil.InternalPathPrefix).Subrouter().UseEncodedPath(),
SynapseAdminMux: mux.NewRouter().SkipClean(true).PathPrefix("/_synapse/").Subrouter().UseEncodedPath(), SynapseAdminMux: mux.NewRouter().SkipClean(true).PathPrefix("/_synapse/").Subrouter().UseEncodedPath(),
apiHttpClient: &apiClient, apiHttpClient: &apiClient,
@ -388,6 +389,7 @@ func (b *BaseDendrite) SetupAndServeHTTP(
externalRouter.PathPrefix("/_synapse/").Handler(b.SynapseAdminMux) externalRouter.PathPrefix("/_synapse/").Handler(b.SynapseAdminMux)
externalRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(b.PublicMediaAPIMux) externalRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(b.PublicMediaAPIMux)
externalRouter.PathPrefix(httputil.PublicWellKnownPrefix).Handler(b.PublicWellKnownAPIMux) externalRouter.PathPrefix(httputil.PublicWellKnownPrefix).Handler(b.PublicWellKnownAPIMux)
externalRouter.PathPrefix("/_matrix").Handler(b.PublicConsentAPIMux)
if internalAddr != NoListener && internalAddr != externalAddr { if internalAddr != NoListener && internalAddr != externalAddr {
go func() { go func() {

View file

@ -1,7 +1,10 @@
package config package config
import ( import (
"fmt"
"html/template"
"math/rand" "math/rand"
"path/filepath"
"time" "time"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
@ -57,6 +60,9 @@ type Global struct {
// DNS caching options for all outbound HTTP requests // DNS caching options for all outbound HTTP requests
DNSCache DNSCacheOptions `yaml:"dns_cache"` DNSCache DNSCacheOptions `yaml:"dns_cache"`
// Consent tracking options
UserConsentOptions UserConsentOptions `yaml:"user_consent"`
} }
func (c *Global) Defaults(generate bool) { func (c *Global) Defaults(generate bool) {
@ -72,6 +78,7 @@ func (c *Global) Defaults(generate bool) {
c.Metrics.Defaults(generate) c.Metrics.Defaults(generate)
c.DNSCache.Defaults() c.DNSCache.Defaults()
c.Sentry.Defaults() c.Sentry.Defaults()
c.UserConsentOptions.Defaults()
} }
func (c *Global) Verify(configErrs *ConfigErrors, isMonolith bool) { func (c *Global) Verify(configErrs *ConfigErrors, isMonolith bool) {
@ -82,6 +89,7 @@ func (c *Global) Verify(configErrs *ConfigErrors, isMonolith bool) {
c.Metrics.Verify(configErrs, isMonolith) c.Metrics.Verify(configErrs, isMonolith)
c.Sentry.Verify(configErrs, isMonolith) c.Sentry.Verify(configErrs, isMonolith)
c.DNSCache.Verify(configErrs, isMonolith) c.DNSCache.Verify(configErrs, isMonolith)
c.UserConsentOptions.Verify(configErrs, isMonolith)
} }
type OldVerifyKeys struct { type OldVerifyKeys struct {
@ -195,3 +203,71 @@ func (c *DNSCacheOptions) Verify(configErrs *ConfigErrors, isMonolith bool) {
checkPositive(configErrs, "cache_size", int64(c.CacheSize)) checkPositive(configErrs, "cache_size", int64(c.CacheSize))
checkPositive(configErrs, "cache_lifetime", int64(c.CacheLifetime)) checkPositive(configErrs, "cache_lifetime", int64(c.CacheLifetime))
} }
// Consent tracking configuration
// If either require_at_registration or send_server_notice_to_guest are true, consent
// messages will be sent to the users.
type UserConsentOptions struct {
// Require consent when user registers for the first time
RequireAtRegistration bool `yaml:"require_at_registration"`
// The name to be shown to the user
PolicyName string `yaml:"policy_name"`
// The directory to search for *.gohtml templates
TemplateDir string `yaml:"template_dir"`
// The version of the policy. When loading templates, ".gohtml" template is added as a suffix
// e.g: ${template_dir}/1.0.gohtml needs to exist, if this is set to 1.0
Version string `yaml:"version"`
// Send a consent message to guest users
SendServerNoticeToGuest bool `yaml:"send_server_notice_to_guest"`
// Default message to send to users
ServerNoticeContent struct {
MsgType string `yaml:"msg_type"`
Body string `yaml:"body"`
} `yaml:"server_notice_content"`
// The error message to display if the user hasn't given their consent yet
BlockEventsError string `yaml:"block_events_error"`
// All loaded templates
Templates *template.Template `yaml:"-"`
}
func (c *UserConsentOptions) Defaults() {
c.RequireAtRegistration = false
c.SendServerNoticeToGuest = false
c.PolicyName = "Privacy Policy"
c.Version = "1.0"
c.TemplateDir = "./templates/privacy"
}
func (c *UserConsentOptions) Verify(configErrors *ConfigErrors, isMonolith bool) {
if c.Enabled() {
checkNotEmpty(configErrors, "template_dir", c.TemplateDir)
checkNotEmpty(configErrors, "version", c.Version)
checkNotEmpty(configErrors, "policy_name", c.PolicyName)
if len(*configErrors) > 0 {
return
}
p, err := filepath.Abs(c.TemplateDir)
if err != nil {
configErrors.Add("unable to get template directory")
return
}
// Read all defined *.gohtml templates
t, err := template.ParseGlob(filepath.Join(p, "*.gohtml"))
if err != nil || t == nil {
configErrors.Add(fmt.Sprintf("unable to read consent templates: %+v", err))
return
}
c.Templates = t
// Verify we've got a template for the defined version
versionTemplate := c.Templates.Lookup(c.Version + ".gohtml")
if versionTemplate == nil {
configErrors.Add(fmt.Sprintf("unable to load defined '%s' policy template", c.Version))
}
}
}
func (c *UserConsentOptions) Enabled() bool {
return c.RequireAtRegistration || c.SendServerNoticeToGuest
}

View file

@ -55,9 +55,9 @@ type Monolith struct {
} }
// AddAllPublicRoutes attaches all public paths to the given router // AddAllPublicRoutes attaches all public paths to the given router
func (m *Monolith) AddAllPublicRoutes(process *process.ProcessContext, csMux, ssMux, keyMux, wkMux, mediaMux, synapseMux *mux.Router) { func (m *Monolith) AddAllPublicRoutes(process *process.ProcessContext, csMux, ssMux, keyMux, wkMux, mediaMux, synapseMux, consentMux *mux.Router) {
clientapi.AddPublicRoutes( clientapi.AddPublicRoutes(
csMux, synapseMux, &m.Config.ClientAPI, m.AccountDB, csMux, synapseMux, consentMux, &m.Config.ClientAPI, m.AccountDB,
m.FedClient, m.RoomserverAPI, m.FedClient, m.RoomserverAPI,
m.EDUInternalAPI, m.AppserviceAPI, transactions.New(), m.EDUInternalAPI, m.AppserviceAPI, transactions.New(),
m.FederationAPI, m.UserAPI, m.KeyAPI, m.ExtPublicRoomsProvider, m.FederationAPI, m.UserAPI, m.KeyAPI, m.ExtPublicRoomsProvider,