mirror of
https://github.com/hoernschen/dendrite.git
synced 2025-07-31 13:22:46 +00:00
Initial support for multiple server names (#2829)
This PR is the first step towards virtual hosting by laying the groundwork for multiple server names being configured.
This commit is contained in:
parent
2a4c7f45b3
commit
f6dea712d2
46 changed files with 291 additions and 155 deletions
|
@ -74,7 +74,7 @@ func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login,
|
|||
JSON: jsonerror.BadJSON("A password must be supplied."),
|
||||
}
|
||||
}
|
||||
localpart, err := userutil.ParseUsernameParam(username, &t.Config.Matrix.ServerName)
|
||||
localpart, _, err := userutil.ParseUsernameParam(username, t.Config.Matrix)
|
||||
if err != nil {
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
|
|
|
@ -70,7 +70,7 @@ func AdminEvacuateUser(req *http.Request, cfg *config.ClientAPI, device *userapi
|
|||
if err != nil {
|
||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
if domain != cfg.Matrix.ServerName {
|
||||
if !cfg.Matrix.IsLocalServerName(domain) {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.MissingArgument("User ID must belong to this server."),
|
||||
|
@ -169,7 +169,7 @@ func AdminMarkAsStale(req *http.Request, cfg *config.ClientAPI, keyAPI api.Clien
|
|||
if err != nil {
|
||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
if domain == cfg.Matrix.ServerName {
|
||||
if cfg.Matrix.IsLocalServerName(domain) {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.InvalidParam("Can not mark local device list as stale"),
|
||||
|
|
|
@ -169,9 +169,21 @@ func createRoom(
|
|||
asAPI appserviceAPI.AppServiceInternalAPI,
|
||||
evTime time.Time,
|
||||
) util.JSONResponse {
|
||||
_, userDomain, err := gomatrixserverlib.SplitID('@', device.UserID)
|
||||
if err != nil {
|
||||
util.GetLogger(ctx).WithError(err).Error("gomatrixserverlib.SplitID failed")
|
||||
return jsonerror.InternalServerError()
|
||||
}
|
||||
if !cfg.Matrix.IsLocalServerName(userDomain) {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: jsonerror.Forbidden(fmt.Sprintf("User domain %q not configured locally", userDomain)),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO (#267): Check room ID doesn't clash with an existing one, and we
|
||||
// probably shouldn't be using pseudo-random strings, maybe GUIDs?
|
||||
roomID := fmt.Sprintf("!%s:%s", util.RandomString(16), cfg.Matrix.ServerName)
|
||||
roomID := fmt.Sprintf("!%s:%s", util.RandomString(16), userDomain)
|
||||
|
||||
logger := util.GetLogger(ctx)
|
||||
userID := device.UserID
|
||||
|
@ -314,7 +326,7 @@ func createRoom(
|
|||
|
||||
var roomAlias string
|
||||
if r.RoomAliasName != "" {
|
||||
roomAlias = fmt.Sprintf("#%s:%s", r.RoomAliasName, cfg.Matrix.ServerName)
|
||||
roomAlias = fmt.Sprintf("#%s:%s", r.RoomAliasName, userDomain)
|
||||
// check it's free TODO: This races but is better than nothing
|
||||
hasAliasReq := roomserverAPI.GetRoomIDForAliasRequest{
|
||||
Alias: roomAlias,
|
||||
|
@ -436,7 +448,7 @@ func createRoom(
|
|||
builder.PrevEvents = []gomatrixserverlib.EventReference{builtEvents[i-1].EventReference()}
|
||||
}
|
||||
var ev *gomatrixserverlib.Event
|
||||
ev, err = buildEvent(&builder, &authEvents, cfg, evTime, roomVersion)
|
||||
ev, err = buildEvent(&builder, userDomain, &authEvents, cfg, evTime, roomVersion)
|
||||
if err != nil {
|
||||
util.GetLogger(ctx).WithError(err).Error("buildEvent failed")
|
||||
return jsonerror.InternalServerError()
|
||||
|
@ -461,7 +473,7 @@ func createRoom(
|
|||
inputs = append(inputs, roomserverAPI.InputRoomEvent{
|
||||
Kind: roomserverAPI.KindNew,
|
||||
Event: event,
|
||||
Origin: cfg.Matrix.ServerName,
|
||||
Origin: userDomain,
|
||||
SendAsServer: roomserverAPI.DoNotSendToOtherServers,
|
||||
})
|
||||
}
|
||||
|
@ -548,7 +560,7 @@ func createRoom(
|
|||
Event: event,
|
||||
InviteRoomState: inviteStrippedState,
|
||||
RoomVersion: event.RoomVersion,
|
||||
SendAsServer: string(cfg.Matrix.ServerName),
|
||||
SendAsServer: string(userDomain),
|
||||
}, &inviteRes); err != nil {
|
||||
util.GetLogger(ctx).WithError(err).Error("PerformInvite failed")
|
||||
return util.JSONResponse{
|
||||
|
@ -591,6 +603,7 @@ func createRoom(
|
|||
// buildEvent fills out auth_events for the builder then builds the event
|
||||
func buildEvent(
|
||||
builder *gomatrixserverlib.EventBuilder,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
provider gomatrixserverlib.AuthEventProvider,
|
||||
cfg *config.ClientAPI,
|
||||
evTime time.Time,
|
||||
|
@ -606,7 +619,7 @@ func buildEvent(
|
|||
}
|
||||
builder.AuthEvents = refs
|
||||
event, err := builder.Build(
|
||||
evTime, cfg.Matrix.ServerName, cfg.Matrix.KeyID,
|
||||
evTime, serverName, cfg.Matrix.KeyID,
|
||||
cfg.Matrix.PrivateKey, roomVersion,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -75,7 +75,7 @@ func DirectoryRoom(
|
|||
if res.RoomID == "" {
|
||||
// If we don't know it locally, do a federation query.
|
||||
// But don't send the query to ourselves.
|
||||
if domain != cfg.Matrix.ServerName {
|
||||
if !cfg.Matrix.IsLocalServerName(domain) {
|
||||
fedRes, fedErr := federation.LookupRoomAlias(req.Context(), domain, roomAlias)
|
||||
if fedErr != nil {
|
||||
// TODO: Return 502 if the remote server errored.
|
||||
|
@ -127,7 +127,7 @@ func SetLocalAlias(
|
|||
}
|
||||
}
|
||||
|
||||
if domain != cfg.Matrix.ServerName {
|
||||
if !cfg.Matrix.IsLocalServerName(domain) {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: jsonerror.Forbidden("Alias must be on local homeserver"),
|
||||
|
|
|
@ -62,8 +62,7 @@ func GetPostPublicRooms(
|
|||
}
|
||||
|
||||
serverName := gomatrixserverlib.ServerName(request.Server)
|
||||
|
||||
if serverName != "" && serverName != cfg.Matrix.ServerName {
|
||||
if serverName != "" && !cfg.Matrix.IsLocalServerName(serverName) {
|
||||
res, err := federation.GetPublicRoomsFiltered(
|
||||
req.Context(), serverName,
|
||||
int(request.Limit), request.Since,
|
||||
|
|
|
@ -68,7 +68,7 @@ func Login(
|
|||
return *authErr
|
||||
}
|
||||
// make a device/access token
|
||||
authErr2 := completeAuth(req.Context(), cfg.Matrix.ServerName, userAPI, login, req.RemoteAddr, req.UserAgent())
|
||||
authErr2 := completeAuth(req.Context(), cfg.Matrix, userAPI, login, req.RemoteAddr, req.UserAgent())
|
||||
cleanup(req.Context(), &authErr2)
|
||||
return authErr2
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ func Login(
|
|||
}
|
||||
|
||||
func completeAuth(
|
||||
ctx context.Context, serverName gomatrixserverlib.ServerName, userAPI userapi.ClientUserAPI, login *auth.Login,
|
||||
ctx context.Context, cfg *config.Global, userAPI userapi.ClientUserAPI, login *auth.Login,
|
||||
ipAddr, userAgent string,
|
||||
) util.JSONResponse {
|
||||
token, err := auth.GenerateAccessToken()
|
||||
|
@ -88,7 +88,7 @@ func completeAuth(
|
|||
return jsonerror.InternalServerError()
|
||||
}
|
||||
|
||||
localpart, err := userutil.ParseUsernameParam(login.Username(), &serverName)
|
||||
localpart, serverName, err := userutil.ParseUsernameParam(login.Username(), cfg)
|
||||
if err != nil {
|
||||
util.GetLogger(ctx).WithError(err).Error("auth.ParseUsernameParam failed")
|
||||
return jsonerror.InternalServerError()
|
||||
|
|
|
@ -105,12 +105,13 @@ func sendMembership(ctx context.Context, profileAPI userapi.ClientUserAPI, devic
|
|||
return jsonerror.InternalServerError()
|
||||
}
|
||||
|
||||
serverName := device.UserDomain()
|
||||
if err = roomserverAPI.SendEvents(
|
||||
ctx, rsAPI,
|
||||
roomserverAPI.KindNew,
|
||||
[]*gomatrixserverlib.HeaderedEvent{event.Event.Headered(roomVer)},
|
||||
cfg.Matrix.ServerName,
|
||||
cfg.Matrix.ServerName,
|
||||
serverName,
|
||||
serverName,
|
||||
nil,
|
||||
false,
|
||||
); err != nil {
|
||||
|
@ -271,7 +272,7 @@ func sendInvite(
|
|||
Event: event,
|
||||
InviteRoomState: nil, // ask the roomserver to draw up invite room state for us
|
||||
RoomVersion: event.RoomVersion,
|
||||
SendAsServer: string(cfg.Matrix.ServerName),
|
||||
SendAsServer: string(device.UserDomain()),
|
||||
}, &inviteRes); err != nil {
|
||||
util.GetLogger(ctx).WithError(err).Error("PerformInvite failed")
|
||||
return util.JSONResponse{
|
||||
|
@ -341,7 +342,7 @@ func loadProfile(
|
|||
}
|
||||
|
||||
var profile *authtypes.Profile
|
||||
if serverName == cfg.Matrix.ServerName {
|
||||
if cfg.Matrix.IsLocalServerName(serverName) {
|
||||
profile, err = appserviceAPI.RetrieveUserProfile(ctx, userID, asAPI, profileAPI)
|
||||
} else {
|
||||
profile = &authtypes.Profile{}
|
||||
|
|
|
@ -63,7 +63,7 @@ func CreateOpenIDToken(
|
|||
JSON: openIDTokenResponse{
|
||||
AccessToken: response.Token.Token,
|
||||
TokenType: "Bearer",
|
||||
MatrixServerName: string(cfg.Matrix.ServerName),
|
||||
MatrixServerName: string(device.UserDomain()),
|
||||
ExpiresIn: response.Token.ExpiresAtMS / 1000, // convert ms to s
|
||||
},
|
||||
}
|
||||
|
|
|
@ -113,12 +113,19 @@ func SetAvatarURL(
|
|||
}
|
||||
}
|
||||
|
||||
localpart, _, err := gomatrixserverlib.SplitID('@', userID)
|
||||
localpart, domain, err := gomatrixserverlib.SplitID('@', userID)
|
||||
if err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed")
|
||||
return jsonerror.InternalServerError()
|
||||
}
|
||||
|
||||
if !cfg.Matrix.IsLocalServerName(domain) {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: jsonerror.Forbidden("userID does not belong to a locally configured domain"),
|
||||
}
|
||||
}
|
||||
|
||||
evTime, err := httputil.ParseTSParam(req)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
|
@ -129,8 +136,9 @@ func SetAvatarURL(
|
|||
|
||||
setRes := &userapi.PerformSetAvatarURLResponse{}
|
||||
if err = profileAPI.SetAvatarURL(req.Context(), &userapi.PerformSetAvatarURLRequest{
|
||||
Localpart: localpart,
|
||||
AvatarURL: r.AvatarURL,
|
||||
Localpart: localpart,
|
||||
ServerName: domain,
|
||||
AvatarURL: r.AvatarURL,
|
||||
}, setRes); err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("profileAPI.SetAvatarURL failed")
|
||||
return jsonerror.InternalServerError()
|
||||
|
@ -204,12 +212,19 @@ func SetDisplayName(
|
|||
}
|
||||
}
|
||||
|
||||
localpart, _, err := gomatrixserverlib.SplitID('@', userID)
|
||||
localpart, domain, err := gomatrixserverlib.SplitID('@', userID)
|
||||
if err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed")
|
||||
return jsonerror.InternalServerError()
|
||||
}
|
||||
|
||||
if !cfg.Matrix.IsLocalServerName(domain) {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: jsonerror.Forbidden("userID does not belong to a locally configured domain"),
|
||||
}
|
||||
}
|
||||
|
||||
evTime, err := httputil.ParseTSParam(req)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
|
@ -221,6 +236,7 @@ func SetDisplayName(
|
|||
profileRes := &userapi.PerformUpdateDisplayNameResponse{}
|
||||
err = profileAPI.SetDisplayName(req.Context(), &userapi.PerformUpdateDisplayNameRequest{
|
||||
Localpart: localpart,
|
||||
ServerName: domain,
|
||||
DisplayName: r.DisplayName,
|
||||
}, profileRes)
|
||||
if err != nil {
|
||||
|
@ -261,6 +277,12 @@ func updateProfile(
|
|||
return jsonerror.InternalServerError(), err
|
||||
}
|
||||
|
||||
_, domain, err := gomatrixserverlib.SplitID('@', userID)
|
||||
if err != nil {
|
||||
util.GetLogger(ctx).WithError(err).Error("gomatrixserverlib.SplitID failed")
|
||||
return jsonerror.InternalServerError(), err
|
||||
}
|
||||
|
||||
events, err := buildMembershipEvents(
|
||||
ctx, res.RoomIDs, *profile, userID, cfg, evTime, rsAPI,
|
||||
)
|
||||
|
@ -276,7 +298,7 @@ func updateProfile(
|
|||
return jsonerror.InternalServerError(), e
|
||||
}
|
||||
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, events, cfg.Matrix.ServerName, cfg.Matrix.ServerName, nil, true); err != nil {
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, events, domain, domain, nil, true); err != nil {
|
||||
util.GetLogger(ctx).WithError(err).Error("SendEvents failed")
|
||||
return jsonerror.InternalServerError(), err
|
||||
}
|
||||
|
@ -298,7 +320,7 @@ func getProfile(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if domain != cfg.Matrix.ServerName {
|
||||
if !cfg.Matrix.IsLocalServerName(domain) {
|
||||
profile, fedErr := federation.LookupProfile(ctx, domain, userID, "")
|
||||
if fedErr != nil {
|
||||
if x, ok := fedErr.(gomatrix.HTTPError); ok {
|
||||
|
|
|
@ -131,7 +131,8 @@ func SendRedaction(
|
|||
JSON: jsonerror.NotFound("Room does not exist"),
|
||||
}
|
||||
}
|
||||
if err = roomserverAPI.SendEvents(context.Background(), rsAPI, roomserverAPI.KindNew, []*gomatrixserverlib.HeaderedEvent{e}, cfg.Matrix.ServerName, cfg.Matrix.ServerName, nil, false); err != nil {
|
||||
domain := device.UserDomain()
|
||||
if err = roomserverAPI.SendEvents(context.Background(), rsAPI, roomserverAPI.KindNew, []*gomatrixserverlib.HeaderedEvent{e}, domain, domain, nil, false); err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Errorf("failed to SendEvents")
|
||||
return jsonerror.InternalServerError()
|
||||
}
|
||||
|
|
|
@ -412,7 +412,7 @@ func UserIDIsWithinApplicationServiceNamespace(
|
|||
return false
|
||||
}
|
||||
|
||||
if domain != cfg.Matrix.ServerName {
|
||||
if !cfg.Matrix.IsLocalServerName(domain) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ func SendEvent(
|
|||
// create a mutex for the specific user in the specific room
|
||||
// this avoids a situation where events that are received in quick succession are sent to the roomserver in a jumbled order
|
||||
userID := device.UserID
|
||||
domain := device.UserDomain()
|
||||
mutex, _ := userRoomSendMutexes.LoadOrStore(roomID+userID, &sync.Mutex{})
|
||||
mutex.(*sync.Mutex).Lock()
|
||||
defer mutex.(*sync.Mutex).Unlock()
|
||||
|
@ -185,8 +186,8 @@ func SendEvent(
|
|||
[]*gomatrixserverlib.HeaderedEvent{
|
||||
e.Headered(verRes.RoomVersion),
|
||||
},
|
||||
cfg.Matrix.ServerName,
|
||||
cfg.Matrix.ServerName,
|
||||
domain,
|
||||
domain,
|
||||
txnAndSessionID,
|
||||
false,
|
||||
); err != nil {
|
||||
|
|
|
@ -215,7 +215,7 @@ func queryIDServerStoreInvite(
|
|||
}
|
||||
|
||||
var profile *authtypes.Profile
|
||||
if serverName == cfg.Matrix.ServerName {
|
||||
if cfg.Matrix.IsLocalServerName(serverName) {
|
||||
res := &userapi.QueryProfileResponse{}
|
||||
err = userAPI.QueryProfile(ctx, &userapi.QueryProfileRequest{UserID: device.UserID}, res)
|
||||
if err != nil {
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
|
@ -24,23 +25,23 @@ import (
|
|||
// usernameParam can either be a user ID or just the localpart/username.
|
||||
// If serverName is passed, it is verified against the domain obtained from usernameParam (if present)
|
||||
// Returns error in case of invalid usernameParam.
|
||||
func ParseUsernameParam(usernameParam string, expectedServerName *gomatrixserverlib.ServerName) (string, error) {
|
||||
func ParseUsernameParam(usernameParam string, cfg *config.Global) (string, gomatrixserverlib.ServerName, error) {
|
||||
localpart := usernameParam
|
||||
|
||||
if strings.HasPrefix(usernameParam, "@") {
|
||||
lp, domain, err := gomatrixserverlib.SplitID('@', usernameParam)
|
||||
|
||||
if err != nil {
|
||||
return "", errors.New("invalid username")
|
||||
return "", "", errors.New("invalid username")
|
||||
}
|
||||
|
||||
if expectedServerName != nil && domain != *expectedServerName {
|
||||
return "", errors.New("user ID does not belong to this server")
|
||||
if !cfg.IsLocalServerName(domain) {
|
||||
return "", "", errors.New("user ID does not belong to this server")
|
||||
}
|
||||
|
||||
localpart = lp
|
||||
return lp, domain, nil
|
||||
}
|
||||
return localpart, nil
|
||||
return localpart, cfg.ServerName, nil
|
||||
}
|
||||
|
||||
// MakeUserID generates user ID from localpart & server name
|
||||
|
|
|
@ -15,6 +15,7 @@ package userutil
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
|
@ -28,7 +29,11 @@ var (
|
|||
|
||||
// TestGoodUserID checks that correct localpart is returned for a valid user ID.
|
||||
func TestGoodUserID(t *testing.T) {
|
||||
lp, err := ParseUsernameParam(goodUserID, &serverName)
|
||||
cfg := &config.Global{
|
||||
ServerName: serverName,
|
||||
}
|
||||
|
||||
lp, _, err := ParseUsernameParam(goodUserID, cfg)
|
||||
|
||||
if err != nil {
|
||||
t.Error("User ID Parsing failed for ", goodUserID, " with error: ", err.Error())
|
||||
|
@ -41,7 +46,11 @@ func TestGoodUserID(t *testing.T) {
|
|||
|
||||
// TestWithLocalpartOnly checks that localpart is returned when usernameParam contains only localpart.
|
||||
func TestWithLocalpartOnly(t *testing.T) {
|
||||
lp, err := ParseUsernameParam(localpart, &serverName)
|
||||
cfg := &config.Global{
|
||||
ServerName: serverName,
|
||||
}
|
||||
|
||||
lp, _, err := ParseUsernameParam(localpart, cfg)
|
||||
|
||||
if err != nil {
|
||||
t.Error("User ID Parsing failed for ", localpart, " with error: ", err.Error())
|
||||
|
@ -54,7 +63,11 @@ func TestWithLocalpartOnly(t *testing.T) {
|
|||
|
||||
// TestIncorrectDomain checks for error when there's server name mismatch.
|
||||
func TestIncorrectDomain(t *testing.T) {
|
||||
_, err := ParseUsernameParam(goodUserID, &invalidServerName)
|
||||
cfg := &config.Global{
|
||||
ServerName: invalidServerName,
|
||||
}
|
||||
|
||||
_, _, err := ParseUsernameParam(goodUserID, cfg)
|
||||
|
||||
if err == nil {
|
||||
t.Error("Invalid Domain should return an error")
|
||||
|
@ -63,7 +76,11 @@ func TestIncorrectDomain(t *testing.T) {
|
|||
|
||||
// TestBadUserID checks that ParseUsernameParam fails for invalid user ID
|
||||
func TestBadUserID(t *testing.T) {
|
||||
_, err := ParseUsernameParam(badUserID, &serverName)
|
||||
cfg := &config.Global{
|
||||
ServerName: serverName,
|
||||
}
|
||||
|
||||
_, _, err := ParseUsernameParam(badUserID, cfg)
|
||||
|
||||
if err == nil {
|
||||
t.Error("Illegal User ID should return an error")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue