mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-18 04:13:10 -06:00
Merge branch 'master' into joined-hosts-api
This commit is contained in:
commit
efaf525ecf
210
clientapi/routing/auth_fallback.go
Normal file
210
clientapi/routing/auth_fallback.go
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
// Copyright 2019 Parminder Singh <parmsingh129@gmail.com>
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package routing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
|
"github.com/matrix-org/dendrite/common/config"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// recaptchaTemplate is an HTML webpage template for recaptcha auth
|
||||||
|
const recaptchaTemplate = `
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Authentication</title>
|
||||||
|
<meta name='viewport' content='width=device-width, initial-scale=1,
|
||||||
|
user-scalable=no, minimum-scale=1.0, maximum-scale=1.0'>
|
||||||
|
<script src="https://www.google.com/recaptcha/api.js"
|
||||||
|
async defer></script>
|
||||||
|
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
|
||||||
|
<script>
|
||||||
|
function captchaDone() {
|
||||||
|
$('#registrationForm').submit();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form id="registrationForm" method="post" action="{{.myUrl}}">
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Hello! We need to prevent computer programs and other automated
|
||||||
|
things from creating accounts on this server.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Please verify that you're not a robot.
|
||||||
|
</p>
|
||||||
|
<input type="hidden" name="session" value="{{.session}}" />
|
||||||
|
<div class="g-recaptcha"
|
||||||
|
data-sitekey="{{.siteKey}}"
|
||||||
|
data-callback="captchaDone">
|
||||||
|
</div>
|
||||||
|
<noscript>
|
||||||
|
<input type="submit" value="All Done" />
|
||||||
|
</noscript>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
|
||||||
|
// successTemplate is an HTML template presented to the user after successful
|
||||||
|
// recaptcha completion
|
||||||
|
const successTemplate = `
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Success!</title>
|
||||||
|
<meta name='viewport' content='width=device-width, initial-scale=1,
|
||||||
|
user-scalable=no, minimum-scale=1.0, maximum-scale=1.0'>
|
||||||
|
<script>
|
||||||
|
if (window.onAuthDone) {
|
||||||
|
window.onAuthDone();
|
||||||
|
} else if (window.opener && window.opener.postMessage) {
|
||||||
|
window.opener.postMessage("authDone", "*");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<p>Thank you!</p>
|
||||||
|
<p>You may now close this window and return to the application.</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
|
||||||
|
// serveTemplate fills template data and serves it using http.ResponseWriter
|
||||||
|
func serveTemplate(w http.ResponseWriter, templateHTML string, data map[string]string) {
|
||||||
|
t := template.Must(template.New("response").Parse(templateHTML))
|
||||||
|
if err := t.Execute(w, data); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthFallback implements GET and POST /auth/{authType}/fallback/web?session={sessionID}
|
||||||
|
func AuthFallback(
|
||||||
|
w http.ResponseWriter, req *http.Request, authType string,
|
||||||
|
cfg config.Dendrite,
|
||||||
|
) *util.JSONResponse {
|
||||||
|
sessionID := req.URL.Query().Get("session")
|
||||||
|
|
||||||
|
if sessionID == "" {
|
||||||
|
return writeHTTPMessage(w, req,
|
||||||
|
"Session ID not provided",
|
||||||
|
http.StatusBadRequest,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
serveRecaptcha := func() {
|
||||||
|
data := map[string]string{
|
||||||
|
"myUrl": req.URL.String(),
|
||||||
|
"session": sessionID,
|
||||||
|
"siteKey": cfg.Matrix.RecaptchaPublicKey,
|
||||||
|
}
|
||||||
|
serveTemplate(w, recaptchaTemplate, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
serveSuccess := func() {
|
||||||
|
data := map[string]string{}
|
||||||
|
serveTemplate(w, successTemplate, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Method == http.MethodGet {
|
||||||
|
// Handle Recaptcha
|
||||||
|
if authType == authtypes.LoginTypeRecaptcha {
|
||||||
|
if err := checkRecaptchaEnabled(&cfg, w, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
serveRecaptcha()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &util.JSONResponse{
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
JSON: jsonerror.NotFound("Unknown auth stage type"),
|
||||||
|
}
|
||||||
|
} else if req.Method == http.MethodPost {
|
||||||
|
// Handle Recaptcha
|
||||||
|
if authType == authtypes.LoginTypeRecaptcha {
|
||||||
|
if err := checkRecaptchaEnabled(&cfg, w, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
clientIP := req.RemoteAddr
|
||||||
|
err := req.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
res := httputil.LogThenError(req, err)
|
||||||
|
return &res
|
||||||
|
}
|
||||||
|
|
||||||
|
response := req.Form.Get("g-recaptcha-response")
|
||||||
|
if err := validateRecaptcha(&cfg, response, clientIP); err != nil {
|
||||||
|
util.GetLogger(req.Context()).Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success. Add recaptcha as a completed login flow
|
||||||
|
AddCompletedSessionStage(sessionID, authtypes.LoginTypeRecaptcha)
|
||||||
|
|
||||||
|
serveSuccess()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &util.JSONResponse{
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
JSON: jsonerror.NotFound("Unknown auth stage type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &util.JSONResponse{
|
||||||
|
Code: http.StatusMethodNotAllowed,
|
||||||
|
JSON: jsonerror.NotFound("Bad method"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkRecaptchaEnabled creates an error response if recaptcha is not usable on homeserver.
|
||||||
|
func checkRecaptchaEnabled(
|
||||||
|
cfg *config.Dendrite,
|
||||||
|
w http.ResponseWriter,
|
||||||
|
req *http.Request,
|
||||||
|
) *util.JSONResponse {
|
||||||
|
if !cfg.Matrix.RecaptchaEnabled {
|
||||||
|
return writeHTTPMessage(w, req,
|
||||||
|
"Recaptcha login is disabled on this Homeserver",
|
||||||
|
http.StatusBadRequest,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeHTTPMessage writes the given header and message to the HTTP response writer.
|
||||||
|
// Returns an error JSONResponse obtained through httputil.LogThenError if the writing failed, otherwise nil.
|
||||||
|
func writeHTTPMessage(
|
||||||
|
w http.ResponseWriter, req *http.Request,
|
||||||
|
message string, header int,
|
||||||
|
) *util.JSONResponse {
|
||||||
|
w.WriteHeader(header)
|
||||||
|
_, err := w.Write([]byte(message))
|
||||||
|
if err != nil {
|
||||||
|
res := httputil.LogThenError(req, err)
|
||||||
|
return &res
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -106,7 +106,7 @@ func (r createRoomRequest) Validate() *util.JSONResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var CreationContent common.CreateContent
|
var CreationContent gomatrixserverlib.CreateContent
|
||||||
err = json.Unmarshal(creationContentBytes, &CreationContent)
|
err = json.Unmarshal(creationContentBytes, &CreationContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
|
|
@ -196,7 +196,7 @@ func createRoom(
|
||||||
return httputil.LogThenError(req, err)
|
return httputil.LogThenError(req, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
membershipContent := common.MemberContent{
|
membershipContent := gomatrixserverlib.MemberContent{
|
||||||
Membership: gomatrixserverlib.Join,
|
Membership: gomatrixserverlib.Join,
|
||||||
DisplayName: profile.DisplayName,
|
DisplayName: profile.DisplayName,
|
||||||
AvatarURL: profile.AvatarURL,
|
AvatarURL: profile.AvatarURL,
|
||||||
|
|
@ -246,7 +246,7 @@ func createRoom(
|
||||||
{"m.room.member", userID, membershipContent},
|
{"m.room.member", userID, membershipContent},
|
||||||
{"m.room.power_levels", "", common.InitialPowerLevelsContent(userID)},
|
{"m.room.power_levels", "", common.InitialPowerLevelsContent(userID)},
|
||||||
// TODO: m.room.canonical_alias
|
// TODO: m.room.canonical_alias
|
||||||
{"m.room.join_rules", "", common.JoinRulesContent{JoinRule: joinRules}},
|
{"m.room.join_rules", "", gomatrixserverlib.JoinRuleContent{JoinRule: joinRules}},
|
||||||
{"m.room.history_visibility", "", common.HistoryVisibilityContent{HistoryVisibility: historyVisibility}},
|
{"m.room.history_visibility", "", common.HistoryVisibilityContent{HistoryVisibility: historyVisibility}},
|
||||||
}
|
}
|
||||||
if r.GuestCanJoin {
|
if r.GuestCanJoin {
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ func buildMembershipEvent(
|
||||||
membership = gomatrixserverlib.Leave
|
membership = gomatrixserverlib.Leave
|
||||||
}
|
}
|
||||||
|
|
||||||
content := common.MemberContent{
|
content := gomatrixserverlib.MemberContent{
|
||||||
Membership: membership,
|
Membership: membership,
|
||||||
DisplayName: profile.DisplayName,
|
DisplayName: profile.DisplayName,
|
||||||
AvatarURL: profile.AvatarURL,
|
AvatarURL: profile.AvatarURL,
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@ func buildMembershipEvents(
|
||||||
StateKey: &userID,
|
StateKey: &userID,
|
||||||
}
|
}
|
||||||
|
|
||||||
content := common.MemberContent{
|
content := gomatrixserverlib.MemberContent{
|
||||||
Membership: gomatrixserverlib.Join,
|
Membership: gomatrixserverlib.Join,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/common/config"
|
"github.com/matrix-org/dendrite/common/config"
|
||||||
|
|
@ -70,12 +71,17 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// sessionsDict keeps track of completed auth stages for each session.
|
// sessionsDict keeps track of completed auth stages for each session.
|
||||||
|
// It shouldn't be passed by value because it contains a mutex.
|
||||||
type sessionsDict struct {
|
type sessionsDict struct {
|
||||||
|
sync.Mutex
|
||||||
sessions map[string][]authtypes.LoginType
|
sessions map[string][]authtypes.LoginType
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCompletedStages returns the completed stages for a session.
|
// GetCompletedStages returns the completed stages for a session.
|
||||||
func (d sessionsDict) GetCompletedStages(sessionID string) []authtypes.LoginType {
|
func (d *sessionsDict) GetCompletedStages(sessionID string) []authtypes.LoginType {
|
||||||
|
d.Lock()
|
||||||
|
defer d.Unlock()
|
||||||
|
|
||||||
if completedStages, ok := d.sessions[sessionID]; ok {
|
if completedStages, ok := d.sessions[sessionID]; ok {
|
||||||
return completedStages
|
return completedStages
|
||||||
}
|
}
|
||||||
|
|
@ -83,23 +89,25 @@ func (d sessionsDict) GetCompletedStages(sessionID string) []authtypes.LoginType
|
||||||
return make([]authtypes.LoginType, 0)
|
return make([]authtypes.LoginType, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddCompletedStage records that a session has completed an auth stage.
|
|
||||||
func (d *sessionsDict) AddCompletedStage(sessionID string, stage authtypes.LoginType) {
|
|
||||||
// Return if the stage is already present
|
|
||||||
for _, completedStage := range d.GetCompletedStages(sessionID) {
|
|
||||||
if completedStage == stage {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d.sessions[sessionID] = append(d.GetCompletedStages(sessionID), stage)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSessionsDict() *sessionsDict {
|
func newSessionsDict() *sessionsDict {
|
||||||
return &sessionsDict{
|
return &sessionsDict{
|
||||||
sessions: make(map[string][]authtypes.LoginType),
|
sessions: make(map[string][]authtypes.LoginType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddCompletedSessionStage records that a session has completed an auth stage.
|
||||||
|
func AddCompletedSessionStage(sessionID string, stage authtypes.LoginType) {
|
||||||
|
sessions.Lock()
|
||||||
|
defer sessions.Unlock()
|
||||||
|
|
||||||
|
for _, completedStage := range sessions.sessions[sessionID] {
|
||||||
|
if completedStage == stage {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sessions.sessions[sessionID] = append(sessions.sessions[sessionID], stage)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// TODO: Remove old sessions. Need to do so on a session-specific timeout.
|
// TODO: Remove old sessions. Need to do so on a session-specific timeout.
|
||||||
// sessions stores the completed flow stages for all sessions. Referenced using their sessionID.
|
// sessions stores the completed flow stages for all sessions. Referenced using their sessionID.
|
||||||
|
|
@ -530,7 +538,7 @@ func handleRegistrationFlow(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Recaptcha to the list of completed registration stages
|
// Add Recaptcha to the list of completed registration stages
|
||||||
sessions.AddCompletedStage(sessionID, authtypes.LoginTypeRecaptcha)
|
AddCompletedSessionStage(sessionID, authtypes.LoginTypeRecaptcha)
|
||||||
|
|
||||||
case authtypes.LoginTypeSharedSecret:
|
case authtypes.LoginTypeSharedSecret:
|
||||||
// Check shared secret against config
|
// Check shared secret against config
|
||||||
|
|
@ -543,7 +551,7 @@ func handleRegistrationFlow(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add SharedSecret to the list of completed registration stages
|
// Add SharedSecret to the list of completed registration stages
|
||||||
sessions.AddCompletedStage(sessionID, authtypes.LoginTypeSharedSecret)
|
AddCompletedSessionStage(sessionID, authtypes.LoginTypeSharedSecret)
|
||||||
|
|
||||||
case "":
|
case "":
|
||||||
// Extract the access token from the request, if there's one to extract
|
// Extract the access token from the request, if there's one to extract
|
||||||
|
|
@ -573,7 +581,7 @@ func handleRegistrationFlow(
|
||||||
case authtypes.LoginTypeDummy:
|
case authtypes.LoginTypeDummy:
|
||||||
// there is nothing to do
|
// there is nothing to do
|
||||||
// Add Dummy to the list of completed registration stages
|
// Add Dummy to the list of completed registration stages
|
||||||
sessions.AddCompletedStage(sessionID, authtypes.LoginTypeDummy)
|
AddCompletedSessionStage(sessionID, authtypes.LoginTypeDummy)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,13 @@ func Setup(
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
|
r0mux.Handle("/auth/{authType}/fallback/web",
|
||||||
|
common.MakeHTMLAPI("auth_fallback", func(w http.ResponseWriter, req *http.Request) *util.JSONResponse {
|
||||||
|
vars := mux.Vars(req)
|
||||||
|
return AuthFallback(w, req, vars["authType"], cfg)
|
||||||
|
}),
|
||||||
|
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
r0mux.Handle("/pushrules/",
|
r0mux.Handle("/pushrules/",
|
||||||
common.MakeExternalAPI("push_rules", func(req *http.Request) util.JSONResponse {
|
common.MakeExternalAPI("push_rules", func(req *http.Request) util.JSONResponse {
|
||||||
// TODO: Implement push rules API
|
// TODO: Implement push rules API
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ type idServerStoreInviteResponse struct {
|
||||||
PublicKey string `json:"public_key"`
|
PublicKey string `json:"public_key"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
DisplayName string `json:"display_name"`
|
DisplayName string `json:"display_name"`
|
||||||
PublicKeys []common.PublicKey `json:"public_keys"`
|
PublicKeys []gomatrixserverlib.PublicKey `json:"public_keys"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -342,7 +342,7 @@ func emit3PIDInviteEvent(
|
||||||
}
|
}
|
||||||
|
|
||||||
validityURL := fmt.Sprintf("https://%s/_matrix/identity/api/v1/pubkey/isvalid", body.IDServer)
|
validityURL := fmt.Sprintf("https://%s/_matrix/identity/api/v1/pubkey/isvalid", body.IDServer)
|
||||||
content := common.ThirdPartyInviteContent{
|
content := gomatrixserverlib.ThirdPartyInviteContent{
|
||||||
DisplayName: res.DisplayName,
|
DisplayName: res.DisplayName,
|
||||||
KeyValidityURL: validityURL,
|
KeyValidityURL: validityURL,
|
||||||
PublicKey: res.PublicKey,
|
PublicKey: res.PublicKey,
|
||||||
|
|
|
||||||
|
|
@ -498,6 +498,11 @@ func (config *Dendrite) checkMatrix(configErrs *configErrors) {
|
||||||
checkNotEmpty(configErrs, "matrix.server_name", string(config.Matrix.ServerName))
|
checkNotEmpty(configErrs, "matrix.server_name", string(config.Matrix.ServerName))
|
||||||
checkNotEmpty(configErrs, "matrix.private_key", string(config.Matrix.PrivateKeyPath))
|
checkNotEmpty(configErrs, "matrix.private_key", string(config.Matrix.PrivateKeyPath))
|
||||||
checkNotZero(configErrs, "matrix.federation_certificates", int64(len(config.Matrix.FederationCertificatePaths)))
|
checkNotZero(configErrs, "matrix.federation_certificates", int64(len(config.Matrix.FederationCertificatePaths)))
|
||||||
|
if config.Matrix.RecaptchaEnabled {
|
||||||
|
checkNotEmpty(configErrs, "matrix.recaptcha_public_key", string(config.Matrix.RecaptchaPublicKey))
|
||||||
|
checkNotEmpty(configErrs, "matrix.recaptcha_private_key", string(config.Matrix.RecaptchaPrivateKey))
|
||||||
|
checkNotEmpty(configErrs, "matrix.recaptcha_siteverify_api", string(config.Matrix.RecaptchaSiteVerifyAPI))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkMedia verifies the parameters media.* are valid.
|
// checkMedia verifies the parameters media.* are valid.
|
||||||
|
|
|
||||||
|
|
@ -14,55 +14,7 @@
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
// CreateContent is the event content for http://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-create
|
import "github.com/matrix-org/gomatrixserverlib"
|
||||||
type CreateContent struct {
|
|
||||||
Creator string `json:"creator"`
|
|
||||||
Federate *bool `json:"m.federate,omitempty"`
|
|
||||||
RoomVersion string `json:"room_version,omitempty"`
|
|
||||||
Predecessor PreviousRoom `json:"predecessor,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PreviousRoom is the "Previous Room" structure defined at https://matrix.org/docs/spec/client_server/r0.5.0#m-room-create
|
|
||||||
type PreviousRoom struct {
|
|
||||||
RoomID string `json:"room_id"`
|
|
||||||
EventID string `json:"event_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MemberContent is the event content for http://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-member
|
|
||||||
type MemberContent struct {
|
|
||||||
Membership string `json:"membership"`
|
|
||||||
DisplayName string `json:"displayname,omitempty"`
|
|
||||||
AvatarURL string `json:"avatar_url,omitempty"`
|
|
||||||
Reason string `json:"reason,omitempty"`
|
|
||||||
ThirdPartyInvite *TPInvite `json:"third_party_invite,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TPInvite is the "Invite" structure defined at http://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-member
|
|
||||||
type TPInvite struct {
|
|
||||||
DisplayName string `json:"display_name"`
|
|
||||||
Signed TPInviteSigned `json:"signed"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TPInviteSigned is the "signed" structure defined at http://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-member
|
|
||||||
type TPInviteSigned struct {
|
|
||||||
MXID string `json:"mxid"`
|
|
||||||
Signatures map[string]map[string]string `json:"signatures"`
|
|
||||||
Token string `json:"token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ThirdPartyInviteContent is the content event for https://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-third-party-invite
|
|
||||||
type ThirdPartyInviteContent struct {
|
|
||||||
DisplayName string `json:"display_name"`
|
|
||||||
KeyValidityURL string `json:"key_validity_url"`
|
|
||||||
PublicKey string `json:"public_key"`
|
|
||||||
PublicKeys []PublicKey `json:"public_keys"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicKey is the PublicKeys structure in https://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-third-party-invite
|
|
||||||
type PublicKey struct {
|
|
||||||
KeyValidityURL string `json:"key_validity_url"`
|
|
||||||
PublicKey string `json:"public_key"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NameContent is the event content for https://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-name
|
// NameContent is the event content for https://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-name
|
||||||
type NameContent struct {
|
type NameContent struct {
|
||||||
|
|
@ -79,51 +31,26 @@ type GuestAccessContent struct {
|
||||||
GuestAccess string `json:"guest_access"`
|
GuestAccess string `json:"guest_access"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JoinRulesContent is the event content for http://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-join-rules
|
|
||||||
type JoinRulesContent struct {
|
|
||||||
JoinRule string `json:"join_rule"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// HistoryVisibilityContent is the event content for http://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-history-visibility
|
// HistoryVisibilityContent is the event content for http://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-history-visibility
|
||||||
type HistoryVisibilityContent struct {
|
type HistoryVisibilityContent struct {
|
||||||
HistoryVisibility string `json:"history_visibility"`
|
HistoryVisibility string `json:"history_visibility"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PowerLevelContent is the event content for http://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-power-levels
|
|
||||||
type PowerLevelContent struct {
|
|
||||||
EventsDefault int `json:"events_default"`
|
|
||||||
Invite int `json:"invite"`
|
|
||||||
StateDefault int `json:"state_default"`
|
|
||||||
Redact int `json:"redact"`
|
|
||||||
Ban int `json:"ban"`
|
|
||||||
UsersDefault int `json:"users_default"`
|
|
||||||
Events map[string]int `json:"events"`
|
|
||||||
Kick int `json:"kick"`
|
|
||||||
Users map[string]int `json:"users"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitialPowerLevelsContent returns the initial values for m.room.power_levels on room creation
|
// InitialPowerLevelsContent returns the initial values for m.room.power_levels on room creation
|
||||||
// if they have not been specified.
|
// if they have not been specified.
|
||||||
// http://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-power-levels
|
// http://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-power-levels
|
||||||
// https://github.com/matrix-org/synapse/blob/v0.19.2/synapse/handlers/room.py#L294
|
// https://github.com/matrix-org/synapse/blob/v0.19.2/synapse/handlers/room.py#L294
|
||||||
func InitialPowerLevelsContent(roomCreator string) PowerLevelContent {
|
func InitialPowerLevelsContent(roomCreator string) (c gomatrixserverlib.PowerLevelContent) {
|
||||||
return PowerLevelContent{
|
c.Defaults()
|
||||||
EventsDefault: 0,
|
c.Events = map[string]int64{
|
||||||
Invite: 0,
|
|
||||||
StateDefault: 50,
|
|
||||||
Redact: 50,
|
|
||||||
Ban: 50,
|
|
||||||
UsersDefault: 0,
|
|
||||||
Events: map[string]int{
|
|
||||||
"m.room.name": 50,
|
"m.room.name": 50,
|
||||||
"m.room.power_levels": 100,
|
"m.room.power_levels": 100,
|
||||||
"m.room.history_visibility": 100,
|
"m.room.history_visibility": 100,
|
||||||
"m.room.canonical_alias": 50,
|
"m.room.canonical_alias": 50,
|
||||||
"m.room.avatar": 50,
|
"m.room.avatar": 50,
|
||||||
},
|
|
||||||
Kick: 50,
|
|
||||||
Users: map[string]int{roomCreator: 100},
|
|
||||||
}
|
}
|
||||||
|
c.Users = map[string]int64{roomCreator: 100}
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// AliasesContent is the event content for http://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-aliases
|
// AliasesContent is the event content for http://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-aliases
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
opentracing "github.com/opentracing/opentracing-go"
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
"github.com/opentracing/opentracing-go/ext"
|
"github.com/opentracing/opentracing-go/ext"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -43,6 +44,24 @@ func MakeExternalAPI(metricsName string, f func(*http.Request) util.JSONResponse
|
||||||
return http.HandlerFunc(withSpan)
|
return http.HandlerFunc(withSpan)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeHTMLAPI adds Span metrics to the HTML Handler function
|
||||||
|
// This is used to serve HTML alongside JSON error messages
|
||||||
|
func MakeHTMLAPI(metricsName string, f func(http.ResponseWriter, *http.Request) *util.JSONResponse) http.Handler {
|
||||||
|
withSpan := func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
span := opentracing.StartSpan(metricsName)
|
||||||
|
defer span.Finish()
|
||||||
|
req = req.WithContext(opentracing.ContextWithSpan(req.Context(), span))
|
||||||
|
if err := f(w, req); err != nil {
|
||||||
|
h := util.MakeJSONAPI(util.NewJSONRequestHandler(func(req *http.Request) util.JSONResponse {
|
||||||
|
return *err
|
||||||
|
}))
|
||||||
|
h.ServeHTTP(w, req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return prometheus.InstrumentHandler(metricsName, http.HandlerFunc(withSpan))
|
||||||
|
}
|
||||||
|
|
||||||
// MakeInternalAPI turns a util.JSONRequestHandler function into an http.Handler.
|
// MakeInternalAPI turns a util.JSONRequestHandler function into an http.Handler.
|
||||||
// This is used for APIs that are internal to dendrite.
|
// This is used for APIs that are internal to dendrite.
|
||||||
// If we are passed a tracing context in the request headers then we use that
|
// If we are passed a tracing context in the request headers then we use that
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ func Setup(
|
||||||
// {keyID} argument and always return a response containing all of the keys.
|
// {keyID} argument and always return a response containing all of the keys.
|
||||||
v2keysmux.Handle("/server/{keyID}", localKeys).Methods(http.MethodGet)
|
v2keysmux.Handle("/server/{keyID}", localKeys).Methods(http.MethodGet)
|
||||||
v2keysmux.Handle("/server/", localKeys).Methods(http.MethodGet)
|
v2keysmux.Handle("/server/", localKeys).Methods(http.MethodGet)
|
||||||
|
v2keysmux.Handle("/server", localKeys).Methods(http.MethodGet)
|
||||||
|
|
||||||
v1fedmux.Handle("/send/{txnID}", common.MakeFedAPI(
|
v1fedmux.Handle("/send/{txnID}", common.MakeFedAPI(
|
||||||
"federation_send", cfg.Matrix.ServerName, keys,
|
"federation_send", cfg.Matrix.ServerName, keys,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/clientapi/producers"
|
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||||
"github.com/matrix-org/dendrite/common"
|
|
||||||
"github.com/matrix-org/dendrite/common/config"
|
"github.com/matrix-org/dendrite/common/config"
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
|
||||||
|
|
@ -42,7 +41,7 @@ type invite struct {
|
||||||
RoomID string `json:"room_id"`
|
RoomID string `json:"room_id"`
|
||||||
Sender string `json:"sender"`
|
Sender string `json:"sender"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
Signed common.TPInviteSigned `json:"signed"`
|
Signed gomatrixserverlib.MemberThirdPartyInviteSigned `json:"signed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type invites struct {
|
type invites struct {
|
||||||
|
|
@ -199,11 +198,11 @@ func createInviteFrom3PIDInvite(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
content := common.MemberContent{
|
content := gomatrixserverlib.MemberContent{
|
||||||
AvatarURL: profile.AvatarURL,
|
AvatarURL: profile.AvatarURL,
|
||||||
DisplayName: profile.DisplayName,
|
DisplayName: profile.DisplayName,
|
||||||
Membership: gomatrixserverlib.Invite,
|
Membership: gomatrixserverlib.Invite,
|
||||||
ThirdPartyInvite: &common.TPInvite{
|
ThirdPartyInvite: &gomatrixserverlib.MemberThirdPartyInvite{
|
||||||
Signed: inv.Signed,
|
Signed: inv.Signed,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -330,7 +329,7 @@ func sendToRemoteServer(
|
||||||
func fillDisplayName(
|
func fillDisplayName(
|
||||||
builder *gomatrixserverlib.EventBuilder, authEvents gomatrixserverlib.AuthEvents,
|
builder *gomatrixserverlib.EventBuilder, authEvents gomatrixserverlib.AuthEvents,
|
||||||
) error {
|
) error {
|
||||||
var content common.MemberContent
|
var content gomatrixserverlib.MemberContent
|
||||||
if err := json.Unmarshal(builder.Content, &content); err != nil {
|
if err := json.Unmarshal(builder.Content, &content); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -343,7 +342,7 @@ func fillDisplayName(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var thirdPartyInviteContent common.ThirdPartyInviteContent
|
var thirdPartyInviteContent gomatrixserverlib.ThirdPartyInviteContent
|
||||||
if err := json.Unmarshal(thirdPartyInviteEvent.Content(), &thirdPartyInviteContent); err != nil {
|
if err := json.Unmarshal(thirdPartyInviteEvent.Content(), &thirdPartyInviteContent); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -24,7 +24,7 @@ require (
|
||||||
github.com/lib/pq v0.0.0-20170918175043-23da1db4f16d
|
github.com/lib/pq v0.0.0-20170918175043-23da1db4f16d
|
||||||
github.com/matrix-org/dugong v0.0.0-20171220115018-ea0a4690a0d5
|
github.com/matrix-org/dugong v0.0.0-20171220115018-ea0a4690a0d5
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26
|
github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20190805173246-3a2199d5ecd6
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20190814163046-d6285a18401f
|
||||||
github.com/matrix-org/naffka v0.0.0-20171115094957-662bfd0841d0
|
github.com/matrix-org/naffka v0.0.0-20171115094957-662bfd0841d0
|
||||||
github.com/matrix-org/util v0.0.0-20171127121716-2e2df66af2f5
|
github.com/matrix-org/util v0.0.0-20171127121716-2e2df66af2f5
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1
|
github.com/matttproud/golang_protobuf_extensions v1.0.1
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -58,6 +58,8 @@ github.com/matrix-org/gomatrixserverlib v0.0.0-20190724145009-a6df10ef35d6 h1:B8
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20190724145009-a6df10ef35d6/go.mod h1:sf0RcKOdiwJeTti7A313xsaejNUGYDq02MQZ4JD4w/E=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20190724145009-a6df10ef35d6/go.mod h1:sf0RcKOdiwJeTti7A313xsaejNUGYDq02MQZ4JD4w/E=
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20190805173246-3a2199d5ecd6 h1:xr69Hk6QM3RIN6JSvx3RpDowBGpHpDDqhqXCeySwYow=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20190805173246-3a2199d5ecd6 h1:xr69Hk6QM3RIN6JSvx3RpDowBGpHpDDqhqXCeySwYow=
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20190805173246-3a2199d5ecd6/go.mod h1:sf0RcKOdiwJeTti7A313xsaejNUGYDq02MQZ4JD4w/E=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20190805173246-3a2199d5ecd6/go.mod h1:sf0RcKOdiwJeTti7A313xsaejNUGYDq02MQZ4JD4w/E=
|
||||||
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20190814163046-d6285a18401f h1:20CZL7ApB7xgR7sZF9yD/qpsP51Sfx0TTgUJ3vKgnZQ=
|
||||||
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20190814163046-d6285a18401f/go.mod h1:sf0RcKOdiwJeTti7A313xsaejNUGYDq02MQZ4JD4w/E=
|
||||||
github.com/matrix-org/naffka v0.0.0-20171115094957-662bfd0841d0 h1:p7WTwG+aXM86+yVrYAiCMW3ZHSmotVvuRbjtt3jC+4A=
|
github.com/matrix-org/naffka v0.0.0-20171115094957-662bfd0841d0 h1:p7WTwG+aXM86+yVrYAiCMW3ZHSmotVvuRbjtt3jC+4A=
|
||||||
github.com/matrix-org/naffka v0.0.0-20171115094957-662bfd0841d0/go.mod h1:cXoYQIENbdWIQHt1SyCo6Bl3C3raHwJ0wgVrXHSqf+A=
|
github.com/matrix-org/naffka v0.0.0-20171115094957-662bfd0841d0/go.mod h1:cXoYQIENbdWIQHt1SyCo6Bl3C3raHwJ0wgVrXHSqf+A=
|
||||||
github.com/matrix-org/util v0.0.0-20171013132526-8b1c8ab81986 h1:TiWl4hLvezAhRPM8tPcPDFTysZ7k4T/1J4GPp/iqlZo=
|
github.com/matrix-org/util v0.0.0-20171013132526-8b1c8ab81986 h1:TiWl4hLvezAhRPM8tPcPDFTysZ7k4T/1J4GPp/iqlZo=
|
||||||
|
|
|
||||||
3
testfile
3
testfile
|
|
@ -143,7 +143,7 @@ Trying to get push rules with unknown rule_id fails with 404
|
||||||
Events come down the correct room
|
Events come down the correct room
|
||||||
local user can join room with version 5
|
local user can join room with version 5
|
||||||
User can invite local user to room with version 5
|
User can invite local user to room with version 5
|
||||||
Inbound federation can receive room-join requests
|
Inbound federation can receive v1 room-join requests
|
||||||
Typing events appear in initial sync
|
Typing events appear in initial sync
|
||||||
Typing events appear in incremental sync
|
Typing events appear in incremental sync
|
||||||
Typing events appear in gapped sync
|
Typing events appear in gapped sync
|
||||||
|
|
@ -170,3 +170,4 @@ Deleted tags appear in an incremental v2 /sync
|
||||||
Outbound federation can query profile data
|
Outbound federation can query profile data
|
||||||
/event/ on joined room works
|
/event/ on joined room works
|
||||||
/event/ does not allow access to events before the user joined
|
/event/ does not allow access to events before the user joined
|
||||||
|
Federation key API allows unsigned requests for keys
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue