mirror of
https://github.com/matrix-org/dendrite.git
synced 2026-01-16 18:43:10 -06:00
Force sync upstream commit 98d3f88bfb
This commit is contained in:
commit
1d633c0d5e
14
.github/workflows/dendrite.yml
vendored
14
.github/workflows/dendrite.yml
vendored
|
|
@ -25,7 +25,7 @@ jobs:
|
|||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.19
|
||||
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
|
|
@ -70,7 +70,7 @@ jobs:
|
|||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.19
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
go: ["1.18", "1.19"]
|
||||
go: ["1.19"]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup go
|
||||
|
|
@ -137,7 +137,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
go: ["1.18", "1.19"]
|
||||
go: [ "1.19"]
|
||||
goos: ["linux"]
|
||||
goarch: ["amd64", "386"]
|
||||
steps:
|
||||
|
|
@ -171,7 +171,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go: ["1.18", "1.19"]
|
||||
go: ["1.19"]
|
||||
goos: ["windows"]
|
||||
goarch: ["amd64"]
|
||||
steps:
|
||||
|
|
@ -220,7 +220,7 @@ jobs:
|
|||
- name: Setup go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: "1.18"
|
||||
go-version: "1.19"
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
|
|
@ -245,7 +245,7 @@ jobs:
|
|||
- name: Setup go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: "1.18"
|
||||
go-version: "1.19"
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
|
|
|
|||
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
|
|
@ -92,6 +92,8 @@ jobs:
|
|||
target: monolith
|
||||
platforms: ${{ env.PLATFORMS }}
|
||||
push: true
|
||||
build-args: |
|
||||
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
|
||||
tags: |
|
||||
${{ env.DOCKER_NAMESPACE }}/dendrite-monolith:latest
|
||||
${{ env.DOCKER_NAMESPACE }}/dendrite-monolith:${{ env.RELEASE_VERSION }}
|
||||
|
|
|
|||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -53,6 +53,7 @@ _testmain.go
|
|||
|
||||
# Default configuration file
|
||||
dendrite.yaml
|
||||
zion-appservice.yaml
|
||||
|
||||
# Database files
|
||||
*.db
|
||||
|
|
@ -73,3 +74,8 @@ complement/
|
|||
docs/_site
|
||||
|
||||
media_store/
|
||||
|
||||
# Debug
|
||||
**/__debug_bin
|
||||
|
||||
.env
|
||||
25
authorization/authorization.go
Normal file
25
authorization/authorization.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// 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 authorization
|
||||
|
||||
type AuthorizationArgs struct {
|
||||
RoomId string
|
||||
UserId string
|
||||
Permission Permission
|
||||
}
|
||||
|
||||
type Authorization interface {
|
||||
IsAllowed(args AuthorizationArgs) (bool, error)
|
||||
}
|
||||
23
authorization/default_authorization.go
Normal file
23
authorization/default_authorization.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// 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 authorization
|
||||
|
||||
type DefaultAuthorization struct {
|
||||
}
|
||||
|
||||
func (azm *DefaultAuthorization) IsAllowed(args AuthorizationArgs) (bool, error) {
|
||||
// Default. No authorization logic.
|
||||
return true, nil
|
||||
}
|
||||
59
authorization/permissions.go
Normal file
59
authorization/permissions.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package authorization
|
||||
|
||||
type Permission int64
|
||||
|
||||
const (
|
||||
// since iota starts with 0, the first value
|
||||
// defined here will be the default
|
||||
PermissionUndefined Permission = iota
|
||||
PermissionRead
|
||||
PermissionWrite
|
||||
PermissionPing
|
||||
PermissionInvite
|
||||
PermissionRedact
|
||||
PermissionBan
|
||||
PermissionModifyChannelProfile
|
||||
PermissionModifyChannelPermissions
|
||||
PermissionPinMessages
|
||||
PermissionAddRemoveChannels
|
||||
PermissionModifySpacePermissions
|
||||
PermissionModifyChannelDefaults
|
||||
PermissionModifySpaceProfile
|
||||
PermissionOwner
|
||||
)
|
||||
|
||||
func (p Permission) String() string {
|
||||
switch p {
|
||||
case PermissionUndefined:
|
||||
return "Undefined"
|
||||
case PermissionRead:
|
||||
return "Read"
|
||||
case PermissionWrite:
|
||||
return "Write"
|
||||
case PermissionPing:
|
||||
return "Ping"
|
||||
case PermissionInvite:
|
||||
return "Invite"
|
||||
case PermissionRedact:
|
||||
return "Redact"
|
||||
case PermissionBan:
|
||||
return "Ban"
|
||||
case PermissionModifyChannelProfile:
|
||||
return "ModifyChannelProfile"
|
||||
case PermissionModifyChannelPermissions:
|
||||
return "ModifyChannelPermissions"
|
||||
case PermissionPinMessages:
|
||||
return "PinMessages"
|
||||
case PermissionAddRemoveChannels:
|
||||
return "AddRemoveChannels"
|
||||
case PermissionModifySpacePermissions:
|
||||
return "ModifySpacePermissions"
|
||||
case PermissionModifyChannelDefaults:
|
||||
return "ModifyChannelDefaults"
|
||||
case PermissionModifySpaceProfile:
|
||||
return "ModifySpaceProfile"
|
||||
case PermissionOwner:
|
||||
return "Owner"
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
27
build/docker/Dockerfile.monolith
Normal file
27
build/docker/Dockerfile.monolith
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
FROM docker.io/golang:1.19-alpine AS base
|
||||
|
||||
RUN apk --update --no-cache add bash build-base
|
||||
|
||||
ARG RELEASE_VERSION="Unreleased"
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY . /build
|
||||
|
||||
RUN mkdir -p bin
|
||||
RUN go build -trimpath -o bin/ -ldflags="-X 'github.com/matrix-org/dendrite/clientapi/routing.ReleaseVersion=$RELEASE_VERSION'" ./cmd/dendrite-monolith-server
|
||||
RUN go build -trimpath -o bin/ ./cmd/create-account
|
||||
RUN go build -trimpath -o bin/ ./cmd/generate-keys
|
||||
|
||||
FROM alpine:latest
|
||||
LABEL org.opencontainers.image.title="Dendrite (Monolith)"
|
||||
LABEL org.opencontainers.image.description="Next-generation Matrix homeserver written in Go"
|
||||
LABEL org.opencontainers.image.source="https://github.com/matrix-org/dendrite"
|
||||
LABEL org.opencontainers.image.licenses="Apache-2.0"
|
||||
|
||||
COPY --from=base /build/bin/* /usr/bin/
|
||||
|
||||
VOLUME /etc/dendrite
|
||||
WORKDIR /etc/dendrite
|
||||
|
||||
ENTRYPOINT ["/usr/bin/dendrite-monolith-server"]
|
||||
|
|
@ -1,19 +1,19 @@
|
|||
#syntax=docker/dockerfile:1.2
|
||||
|
||||
FROM golang:1.18-stretch as build
|
||||
FROM golang:1.19-buster as build
|
||||
RUN apt-get update && apt-get install -y postgresql
|
||||
WORKDIR /build
|
||||
|
||||
# No password when connecting over localhost
|
||||
RUN sed -i "s%127.0.0.1/32 md5%127.0.0.1/32 trust%g" /etc/postgresql/9.6/main/pg_hba.conf && \
|
||||
RUN sed -i "s%127.0.0.1/32 md5%127.0.0.1/32 trust%g" /etc/postgresql/11/main/pg_hba.conf && \
|
||||
# Bump up max conns for moar concurrency
|
||||
sed -i 's/max_connections = 100/max_connections = 2000/g' /etc/postgresql/9.6/main/postgresql.conf
|
||||
sed -i 's/max_connections = 100/max_connections = 2000/g' /etc/postgresql/11/main/postgresql.conf
|
||||
|
||||
# This entry script starts postgres, waits for it to be up then starts dendrite
|
||||
RUN echo '\
|
||||
#!/bin/bash -eu \n\
|
||||
pg_lsclusters \n\
|
||||
pg_ctlcluster 9.6 main start \n\
|
||||
pg_ctlcluster 11 main start \n\
|
||||
\n\
|
||||
until pg_isready \n\
|
||||
do \n\
|
||||
|
|
@ -31,9 +31,9 @@ RUN mkdir /dendrite
|
|||
RUN --mount=target=. \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
go build -o /dendrite ./cmd/generate-config && \
|
||||
go build -o /dendrite ./cmd/generate-keys && \
|
||||
go build -o /dendrite ./cmd/dendrite-monolith-server
|
||||
go build --race -o /dendrite ./cmd/generate-config && \
|
||||
go build --race -o /dendrite ./cmd/generate-keys && \
|
||||
go build --race -o /dendrite ./cmd/dendrite-monolith-server
|
||||
|
||||
WORKDIR /dendrite
|
||||
RUN ./generate-keys --private-key matrix_key.pem
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ echo "Installing golangci-lint..."
|
|||
|
||||
# Make a backup of go.{mod,sum} first
|
||||
cp go.mod go.mod.bak && cp go.sum go.sum.bak
|
||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.45.2
|
||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.49.0
|
||||
|
||||
# Run linting
|
||||
echo "Looking for lint..."
|
||||
|
|
|
|||
|
|
@ -11,4 +11,9 @@ const (
|
|||
LoginTypeRecaptcha = "m.login.recaptcha"
|
||||
LoginTypeApplicationService = "m.login.application_service"
|
||||
LoginTypeToken = "m.login.token"
|
||||
LoginTypePublicKey = "m.login.publickey"
|
||||
)
|
||||
|
||||
const (
|
||||
LoginTypePublicKeyEthereum = "m.login.publickey.ethereum"
|
||||
)
|
||||
|
|
|
|||
19
clientapi/auth/authtypes/stages.go
Normal file
19
clientapi/auth/authtypes/stages.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// 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 authtypes
|
||||
|
||||
const (
|
||||
LoginStagePublicKeyNewRegistration = "m.login.publickey.newregistration"
|
||||
)
|
||||
|
|
@ -32,7 +32,15 @@ import (
|
|||
// called after authorization has completed, with the result of the authorization.
|
||||
// If the final return value is non-nil, an error occurred and the cleanup function
|
||||
// is nil.
|
||||
func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.UserLoginAPI, userAPI UserInternalAPIForLogin, cfg *config.ClientAPI) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
||||
func LoginFromJSONReader(
|
||||
ctx context.Context,
|
||||
r io.Reader,
|
||||
useraccountAPI uapi.UserLoginAPI,
|
||||
userAPI UserInternalAPIForLogin,
|
||||
clientUserAPI uapi.ClientUserAPI,
|
||||
userInteractiveAuth *UserInteractive,
|
||||
cfg *config.ClientAPI,
|
||||
) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
||||
reqBytes, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
err := &util.JSONResponse{
|
||||
|
|
@ -54,17 +62,23 @@ func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.U
|
|||
}
|
||||
|
||||
var typ Type
|
||||
switch header.Type {
|
||||
case authtypes.LoginTypePassword:
|
||||
switch {
|
||||
case header.Type == authtypes.LoginTypePassword && !cfg.PasswordAuthenticationDisabled:
|
||||
typ = &LoginTypePassword{
|
||||
GetAccountByPassword: useraccountAPI.QueryAccountByPassword,
|
||||
Config: cfg,
|
||||
}
|
||||
case authtypes.LoginTypeToken:
|
||||
case header.Type == authtypes.LoginTypeToken:
|
||||
typ = &LoginTypeToken{
|
||||
UserAPI: userAPI,
|
||||
Config: cfg,
|
||||
}
|
||||
case header.Type == authtypes.LoginTypePublicKey && cfg.PublicKeyAuthentication.Enabled():
|
||||
typ = &LoginTypePublicKey{
|
||||
UserAPI: clientUserAPI,
|
||||
UserInteractive: userInteractiveAuth,
|
||||
Config: cfg,
|
||||
}
|
||||
default:
|
||||
err := util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
|
|
|
|||
150
clientapi/auth/login_publickey.go
Normal file
150
clientapi/auth/login_publickey.go
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// 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 auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"net/http"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/internal/mapsutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
type LoginPublicKeyHandler interface {
|
||||
AccountExists(ctx context.Context) (string, *jsonerror.MatrixError)
|
||||
CreateLogin() *Login
|
||||
GetSession() string
|
||||
GetType() string
|
||||
IsValidUserId(userId string) bool
|
||||
ValidateLoginResponse() (bool, *jsonerror.MatrixError)
|
||||
}
|
||||
|
||||
// LoginTypePublicKey implements https://matrix.org/docs/spec/client_server/..... (to be spec'ed)
|
||||
type LoginTypePublicKey struct {
|
||||
UserAPI userapi.ClientUserAPI
|
||||
UserInteractive *UserInteractive
|
||||
Config *config.ClientAPI
|
||||
}
|
||||
|
||||
func (t *LoginTypePublicKey) Name() string {
|
||||
return authtypes.LoginTypePublicKey
|
||||
}
|
||||
|
||||
func (t *LoginTypePublicKey) AddFlows(userInteractive *UserInteractive) {
|
||||
if t.Config.PublicKeyAuthentication.Ethereum.Enabled {
|
||||
userInteractive.Flows = append(userInteractive.Flows, userInteractiveFlow{
|
||||
Stages: []string{
|
||||
authtypes.LoginTypePublicKeyEthereum,
|
||||
},
|
||||
})
|
||||
params := t.Config.PublicKeyAuthentication.GetPublicKeyRegistrationParams()
|
||||
userInteractive.Params = mapsutil.MapsUnion(userInteractive.Params, params)
|
||||
}
|
||||
|
||||
if t.Config.PublicKeyAuthentication.Enabled() {
|
||||
userInteractive.Types[t.Name()] = t
|
||||
}
|
||||
}
|
||||
|
||||
// LoginFromJSON implements Type.
|
||||
func (t *LoginTypePublicKey) LoginFromJSON(ctx context.Context, reqBytes []byte) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
||||
// "A client should first make a request with no auth parameter. The homeserver returns an HTTP 401 response, with a JSON body"
|
||||
// https://matrix.org/docs/spec/client_server/r0.6.1#user-interactive-api-in-the-rest-api
|
||||
authBytes := gjson.GetBytes(reqBytes, "auth")
|
||||
if !authBytes.Exists() {
|
||||
return nil, nil, t.UserInteractive.NewSession()
|
||||
}
|
||||
|
||||
var authHandler LoginPublicKeyHandler
|
||||
authType := gjson.GetBytes(reqBytes, "auth.type").String()
|
||||
|
||||
switch authType {
|
||||
case authtypes.LoginTypePublicKeyEthereum:
|
||||
pkEthHandler, err := CreatePublicKeyEthereumHandler(
|
||||
[]byte(authBytes.Raw),
|
||||
t.UserAPI,
|
||||
t.Config,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: err,
|
||||
}
|
||||
}
|
||||
authHandler = *pkEthHandler
|
||||
default:
|
||||
return nil, nil, &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.InvalidParam("auth.type"),
|
||||
}
|
||||
}
|
||||
|
||||
return t.continueLoginFlow(ctx, authHandler)
|
||||
}
|
||||
|
||||
func (t *LoginTypePublicKey) continueLoginFlow(ctx context.Context, authHandler LoginPublicKeyHandler) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
||||
loginOK := false
|
||||
sessionID := authHandler.GetSession()
|
||||
|
||||
defer func() {
|
||||
if loginOK {
|
||||
t.UserInteractive.AddCompletedStage(sessionID, authHandler.GetType())
|
||||
} else {
|
||||
t.UserInteractive.DeleteSession(sessionID)
|
||||
}
|
||||
}()
|
||||
|
||||
if _, ok := t.UserInteractive.Sessions[sessionID]; !ok {
|
||||
return nil, nil, &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.Unknown("the session ID is missing or unknown."),
|
||||
}
|
||||
}
|
||||
|
||||
localPart, err := authHandler.AccountExists(ctx)
|
||||
// user account does not exist or there is an error.
|
||||
if localPart == "" || err != nil {
|
||||
return nil, nil, &util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: err,
|
||||
}
|
||||
}
|
||||
|
||||
// user account exists
|
||||
isValidated, err := authHandler.ValidateLoginResponse()
|
||||
if err != nil {
|
||||
return nil, nil, &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: err,
|
||||
}
|
||||
}
|
||||
|
||||
if isValidated {
|
||||
loginOK = true
|
||||
login := authHandler.CreateLogin()
|
||||
return login, func(context.Context, *util.JSONResponse) {}, nil
|
||||
}
|
||||
|
||||
return nil, nil, &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.Unknown("authentication failed, or the account does not exist."),
|
||||
}
|
||||
}
|
||||
158
clientapi/auth/login_publickey_ethereum.go
Normal file
158
clientapi/auth/login_publickey_ethereum.go
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// 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 auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/clientapi/userutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/spruceid/siwe-go"
|
||||
)
|
||||
|
||||
type LoginPublicKeyEthereum struct {
|
||||
// https://github.com/tak-hntlabs/matrix-spec-proposals/blob/main/proposals/3782-matrix-publickey-login-spec.md#client-sends-login-request-with-authentication-data
|
||||
Type string `json:"type"`
|
||||
UserId string `json:"user_id"`
|
||||
Session string `json:"session"`
|
||||
Message string `json:"message"`
|
||||
Signature string `json:"signature"`
|
||||
|
||||
userAPI userapi.ClientUserAPI
|
||||
config *config.ClientAPI
|
||||
}
|
||||
|
||||
func CreatePublicKeyEthereumHandler(
|
||||
reqBytes []byte,
|
||||
userAPI userapi.ClientUserAPI,
|
||||
config *config.ClientAPI,
|
||||
) (*LoginPublicKeyEthereum, *jsonerror.MatrixError) {
|
||||
var pk LoginPublicKeyEthereum
|
||||
if err := json.Unmarshal(reqBytes, &pk); err != nil {
|
||||
return nil, jsonerror.BadJSON("auth")
|
||||
}
|
||||
|
||||
pk.config = config
|
||||
pk.userAPI = userAPI
|
||||
// Case-insensitive
|
||||
pk.UserId = strings.ToLower(pk.UserId)
|
||||
|
||||
return &pk, nil
|
||||
}
|
||||
|
||||
func (pk LoginPublicKeyEthereum) GetSession() string {
|
||||
return pk.Session
|
||||
}
|
||||
|
||||
func (pk LoginPublicKeyEthereum) GetType() string {
|
||||
return pk.Type
|
||||
}
|
||||
|
||||
func (pk LoginPublicKeyEthereum) AccountExists(ctx context.Context) (string, *jsonerror.MatrixError) {
|
||||
localPart, _, err := userutil.ParseUsernameParam(pk.UserId, pk.config.Matrix)
|
||||
if err != nil {
|
||||
// userId does not exist
|
||||
return "", jsonerror.Forbidden("the address is incorrect, or the account does not exist.")
|
||||
}
|
||||
|
||||
if !pk.IsValidUserId(localPart) {
|
||||
return "", jsonerror.InvalidUsername("the username is not valid.")
|
||||
}
|
||||
|
||||
res := userapi.QueryAccountAvailabilityResponse{}
|
||||
if err := pk.userAPI.QueryAccountAvailability(ctx, &userapi.QueryAccountAvailabilityRequest{
|
||||
Localpart: localPart,
|
||||
}, &res); err != nil {
|
||||
return "", jsonerror.Unknown("failed to check availability: " + err.Error())
|
||||
}
|
||||
|
||||
if localPart == "" || res.Available {
|
||||
return "", jsonerror.Forbidden("the address is incorrect, account does not exist")
|
||||
}
|
||||
|
||||
return localPart, nil
|
||||
}
|
||||
|
||||
var validChainAgnosticIdRegex = regexp.MustCompile("^eip155=3a[0-9]+=3a0x[0-9a-fA-F]+$")
|
||||
|
||||
func (pk LoginPublicKeyEthereum) IsValidUserId(userId string) bool {
|
||||
// Verify that the user ID is a valid one according to spec.
|
||||
// https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-10.md
|
||||
|
||||
// Matrix ID has additional grammar requirements for user ID.
|
||||
// https://spec.matrix.org/v1.1/appendices/#user-identifiers
|
||||
// Make sure disallowed characters are escaped.
|
||||
// E.g. ":" is replaced with "=3a".
|
||||
|
||||
isValid := validChainAgnosticIdRegex.MatchString(userId)
|
||||
|
||||
// In addition, double check that the user ID
|
||||
// matches the authentication data in the request.
|
||||
return isValid && strings.ToLower(userId) == pk.UserId
|
||||
}
|
||||
|
||||
func (pk LoginPublicKeyEthereum) ValidateLoginResponse() (bool, *jsonerror.MatrixError) {
|
||||
// Parse the message to extract all the fields.
|
||||
message, err := siwe.ParseMessage(pk.Message)
|
||||
if err != nil {
|
||||
return false, jsonerror.InvalidParam("auth.message")
|
||||
}
|
||||
|
||||
// Check signature to verify message was not tempered
|
||||
_, err = message.Verify(pk.Signature, (*string)(&pk.config.Matrix.ServerName), nil, nil)
|
||||
if err != nil {
|
||||
return false, jsonerror.InvalidSignature(err.Error())
|
||||
}
|
||||
|
||||
// Error if the user ID does not match the signed message.
|
||||
isVerifiedUserId := pk.verifyMessageUserId(message)
|
||||
if !isVerifiedUserId {
|
||||
return false, jsonerror.InvalidUsername(pk.UserId)
|
||||
}
|
||||
|
||||
// Error if the chainId is not supported by the server.
|
||||
if pk.config.PublicKeyAuthentication.Ethereum.GetChainID() != message.GetChainID() {
|
||||
return false, jsonerror.Forbidden("chainId")
|
||||
}
|
||||
|
||||
// No errors.
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (pk LoginPublicKeyEthereum) CreateLogin() *Login {
|
||||
identifier := LoginIdentifier{
|
||||
Type: "m.id.decentralizedid",
|
||||
User: pk.UserId,
|
||||
}
|
||||
login := Login{
|
||||
Identifier: identifier,
|
||||
}
|
||||
return &login
|
||||
}
|
||||
|
||||
func (pk LoginPublicKeyEthereum) verifyMessageUserId(message *siwe.Message) bool {
|
||||
// Use info in the signed message to derive the expected user ID.
|
||||
expectedUserId := fmt.Sprintf("eip155=3a%d=3a%s", message.GetChainID(), message.GetAddress())
|
||||
|
||||
// Case-insensitive comparison to make sure the user ID matches the expected
|
||||
// one derived from the signed message.
|
||||
return pk.UserId == strings.ToLower(expectedUserId)
|
||||
}
|
||||
471
clientapi/auth/login_publickey_ethereum_test.go
Normal file
471
clientapi/auth/login_publickey_ethereum_test.go
Normal file
|
|
@ -0,0 +1,471 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// 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 auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/internal/mapsutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
testutil "github.com/matrix-org/dendrite/test"
|
||||
uapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type loginContext struct {
|
||||
config *config.ClientAPI
|
||||
userInteractive *UserInteractive
|
||||
}
|
||||
|
||||
func createLoginContext(_ *testing.T) *loginContext {
|
||||
cfg := &config.ClientAPI{
|
||||
Matrix: &config.Global{
|
||||
ServerName: testutil.TestServerName,
|
||||
},
|
||||
Derived: &config.Derived{},
|
||||
PasswordAuthenticationDisabled: true,
|
||||
PublicKeyAuthentication: config.PublicKeyAuthentication{
|
||||
Ethereum: config.EthereumAuthConfig{
|
||||
Enabled: true,
|
||||
Version: 1,
|
||||
ConfigChainID: strconv.Itoa(testutil.EthereumTestNetworkId),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pkFlows := cfg.PublicKeyAuthentication.GetPublicKeyRegistrationFlows()
|
||||
cfg.Derived.Registration.Flows = append(cfg.Derived.Registration.Flows, pkFlows...)
|
||||
pkParams := cfg.PublicKeyAuthentication.GetPublicKeyRegistrationParams()
|
||||
cfg.Derived.Registration.Params = mapsutil.MapsUnion(cfg.Derived.Registration.Params, pkParams)
|
||||
|
||||
var userAPI fakePublicKeyUserApi
|
||||
var loginApi uapi.UserLoginAPI
|
||||
|
||||
userInteractive := NewUserInteractive(
|
||||
loginApi,
|
||||
&userAPI,
|
||||
cfg)
|
||||
|
||||
return &loginContext{
|
||||
config: cfg,
|
||||
userInteractive: userInteractive,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type fakePublicKeyUserApi struct {
|
||||
UserInternalAPIForLogin
|
||||
uapi.UserLoginAPI
|
||||
uapi.ClientUserAPI
|
||||
DeletedTokens []string
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) QueryAccountAvailability(ctx context.Context, req *uapi.QueryAccountAvailabilityRequest, res *uapi.QueryAccountAvailabilityResponse) error {
|
||||
if req.Localpart == "does_not_exist" {
|
||||
res.Available = true
|
||||
return nil
|
||||
}
|
||||
|
||||
res.Available = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) QueryAccountByPassword(ctx context.Context, req *uapi.QueryAccountByPasswordRequest, res *uapi.QueryAccountByPasswordResponse) error {
|
||||
if req.PlaintextPassword == "invalidpassword" {
|
||||
res.Account = nil
|
||||
return nil
|
||||
}
|
||||
res.Exists = true
|
||||
res.Account = &uapi.Account{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) PerformLoginTokenDeletion(ctx context.Context, req *uapi.PerformLoginTokenDeletionRequest, res *uapi.PerformLoginTokenDeletionResponse) error {
|
||||
ua.DeletedTokens = append(ua.DeletedTokens, req.Token)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) PerformLoginTokenCreation(ctx context.Context, req *uapi.PerformLoginTokenCreationRequest, res *uapi.PerformLoginTokenCreationResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*fakePublicKeyUserApi) QueryLoginToken(ctx context.Context, req *uapi.QueryLoginTokenRequest, res *uapi.QueryLoginTokenResponse) error {
|
||||
if req.Token == "invalidtoken" {
|
||||
return nil
|
||||
}
|
||||
|
||||
res.Data = &uapi.LoginTokenData{UserID: "@auser:example.com"}
|
||||
return nil
|
||||
}
|
||||
|
||||
func publicKeyTestSession(
|
||||
ctx *context.Context,
|
||||
cfg *config.ClientAPI,
|
||||
userInteractive *UserInteractive,
|
||||
userAPI *fakePublicKeyUserApi,
|
||||
|
||||
) string {
|
||||
emptyAuth := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: `{
|
||||
"type": "m.login.publickey"
|
||||
}`,
|
||||
}
|
||||
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
*ctx,
|
||||
strings.NewReader(emptyAuth.Body),
|
||||
userAPI,
|
||||
userAPI,
|
||||
userAPI,
|
||||
userInteractive,
|
||||
cfg)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(*ctx, nil)
|
||||
}
|
||||
|
||||
json := err.JSON.(Challenge)
|
||||
return json.Session
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyEthereum(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
wallet, _ := testutil.CreateTestAccount()
|
||||
message, _ := testutil.CreateEip4361TestMessage(wallet.PublicAddress)
|
||||
signature, _ := testutil.SignMessage(message.String(), wallet.PrivateKey)
|
||||
sessionId := publicKeyTestSession(
|
||||
&ctx,
|
||||
loginContext.config,
|
||||
loginContext.userInteractive,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
// Escape \t and \n. Work around for marshalling and unmarshalling message.
|
||||
msgStr := testutil.FromEip4361MessageToString(message)
|
||||
body := fmt.Sprintf(`{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v",
|
||||
"user_id": "%v",
|
||||
"message": "%v",
|
||||
"signature": "%v"
|
||||
}
|
||||
}`,
|
||||
sessionId,
|
||||
wallet.Eip155UserId,
|
||||
msgStr,
|
||||
signature,
|
||||
)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// Test
|
||||
login, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Nilf(err, "err actual: %v, expected: nil", err)
|
||||
assert.NotNil(login, "login: actual: nil, expected: not nil")
|
||||
assert.Truef(
|
||||
login.Identifier.Type == "m.id.decentralizedid",
|
||||
"login.Identifier.Type actual: %v, expected: %v", login.Identifier.Type, "m.id.decentralizedid")
|
||||
walletAddress := strings.ToLower(wallet.Eip155UserId)
|
||||
assert.Truef(
|
||||
login.Identifier.User == walletAddress,
|
||||
"login.Identifier.User actual: %v, expected: %v", login.Identifier.User, walletAddress)
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyEthereumMissingSignature(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
wallet, _ := testutil.CreateTestAccount()
|
||||
message, _ := testutil.CreateEip4361TestMessage(wallet.PublicAddress)
|
||||
sessionId := publicKeyTestSession(
|
||||
&ctx,
|
||||
loginContext.config,
|
||||
loginContext.userInteractive,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
// Escape \t and \n. Work around for marshalling and unmarshalling message.
|
||||
msgStr := testutil.FromEip4361MessageToString(message)
|
||||
body := fmt.Sprintf(`{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v",
|
||||
"user_id": "%v",
|
||||
"message": "%v"
|
||||
}
|
||||
}`,
|
||||
sessionId,
|
||||
wallet.Eip155UserId,
|
||||
msgStr,
|
||||
)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusUnauthorized,
|
||||
"err.Code actual: %v, expected: %v", err.Code, http.StatusUnauthorized)
|
||||
json := err.JSON.(*jsonerror.MatrixError)
|
||||
expectedErr := jsonerror.InvalidSignature("")
|
||||
assert.Truef(
|
||||
json.ErrCode == expectedErr.ErrCode,
|
||||
"err.JSON.ErrCode actual: %v, expected: %v", json.ErrCode, expectedErr.ErrCode)
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyEthereumEmptyMessage(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
wallet, _ := testutil.CreateTestAccount()
|
||||
sessionId := publicKeyTestSession(
|
||||
&ctx,
|
||||
loginContext.config,
|
||||
loginContext.userInteractive,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
body := fmt.Sprintf(`{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v",
|
||||
"user_id": "%v"
|
||||
}
|
||||
}`, sessionId, wallet.Eip155UserId)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusUnauthorized,
|
||||
"err.Code actual: %v, expected: %v", err.Code, http.StatusUnauthorized)
|
||||
json := err.JSON.(*jsonerror.MatrixError)
|
||||
expectedErr := jsonerror.InvalidParam("")
|
||||
assert.Truef(
|
||||
json.ErrCode == expectedErr.ErrCode,
|
||||
"err.JSON.ErrCode actual: %v, expected: %v", json.ErrCode, expectedErr.ErrCode)
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyEthereumWrongUserId(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
wallet, _ := testutil.CreateTestAccount()
|
||||
sessionId := publicKeyTestSession(
|
||||
&ctx,
|
||||
loginContext.config,
|
||||
loginContext.userInteractive,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
body := fmt.Sprintf(`{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v",
|
||||
"user_id": "%v"
|
||||
}
|
||||
}`,
|
||||
sessionId,
|
||||
wallet.PublicAddress)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusForbidden,
|
||||
"err.Code actual: %v, expected: %v", err.Code, http.StatusForbidden)
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyEthereumMissingUserId(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
sessionId := publicKeyTestSession(
|
||||
&ctx,
|
||||
loginContext.config,
|
||||
loginContext.userInteractive,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
body := fmt.Sprintf(`{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v"
|
||||
}
|
||||
}`, sessionId)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusForbidden,
|
||||
"err.Code actual: %v, expected: %v", err.Code, http.StatusForbidden)
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyEthereumAccountNotAvailable(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
sessionId := publicKeyTestSession(
|
||||
&ctx,
|
||||
loginContext.config,
|
||||
loginContext.userInteractive,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
body := fmt.Sprintf(`{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v",
|
||||
"user_id": "does_not_exist"
|
||||
}
|
||||
}`, sessionId)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusForbidden,
|
||||
"err.Code actual: %v, expected: %v", err.Code, http.StatusForbidden)
|
||||
}
|
||||
163
clientapi/auth/login_publickey_test.go
Normal file
163
clientapi/auth/login_publickey_test.go
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// 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 auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
testutil "github.com/matrix-org/dendrite/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLoginPublicKeyNewSession(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: `{ "type": "m.login.publickey" }`,
|
||||
}
|
||||
|
||||
// Test
|
||||
login, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.NotNilf(
|
||||
err,
|
||||
"err actual: not nil returned %+v, expected: nil", login)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusUnauthorized,
|
||||
"err.Code actual: %v, expected: %v", err.Code, http.StatusUnauthorized)
|
||||
challenge := err.JSON.(Challenge)
|
||||
assert.NotEmptyf(challenge.Session, "challenge.Session")
|
||||
assert.Truef(
|
||||
authtypes.LoginTypePublicKeyEthereum == challenge.Flows[0].Stages[0],
|
||||
"challenge.Flows[0].Stages[0] actual: %v, expected: %v", challenge.Flows[0].Stages[0], authtypes.LoginTypePublicKeyEthereum)
|
||||
params := challenge.Params[authtypes.LoginTypePublicKeyEthereum]
|
||||
assert.NotEmptyf(
|
||||
params,
|
||||
"challenge.Params[\"%v\"] actual %v, expected %v",
|
||||
authtypes.LoginTypePublicKeyEthereum,
|
||||
params,
|
||||
"[object]")
|
||||
ethParams := params.(config.EthereumAuthParams)
|
||||
assert.Equalf(
|
||||
testutil.EthereumTestNetworkId,
|
||||
ethParams.ChainID,
|
||||
"ChainID actual: %d, expected %d", ethParams.ChainID, testutil.EthereumTestNetworkId)
|
||||
assert.NotEmptyf(ethParams.Version, "Version actual: \"\", expected: not empty")
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyInvalidSessionId(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: `{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "invalid_session_id"
|
||||
}
|
||||
}`,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusUnauthorized,
|
||||
"err.Code actual %v, expected %v", err.Code, http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyInvalidAuthType(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: `{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.someAlgo"
|
||||
}
|
||||
}`,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.NotNil(err, "Expected an err response.actual: nil")
|
||||
assert.Truef(
|
||||
err.Code == http.StatusUnauthorized,
|
||||
"err.Code actual %v, expected %v", err.Code, http.StatusUnauthorized)
|
||||
_, ok := err.JSON.(Challenge)
|
||||
assert.False(
|
||||
ok,
|
||||
"should not return a Challenge response")
|
||||
}
|
||||
|
|
@ -61,6 +61,13 @@ func TestLoginFromJSONReader(t *testing.T) {
|
|||
WantDeletedTokens: []string{"atoken"},
|
||||
},
|
||||
}
|
||||
userInteractive := UserInteractive{
|
||||
Flows: []userInteractiveFlow{},
|
||||
Types: make(map[string]Type),
|
||||
Sessions: make(map[string][]string),
|
||||
Params: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
for _, tst := range tsts {
|
||||
t.Run(tst.Name, func(t *testing.T) {
|
||||
var userAPI fakeUserInternalAPI
|
||||
|
|
@ -69,7 +76,7 @@ func TestLoginFromJSONReader(t *testing.T) {
|
|||
ServerName: serverName,
|
||||
},
|
||||
}
|
||||
login, cleanup, err := LoginFromJSONReader(ctx, strings.NewReader(tst.Body), &userAPI, &userAPI, cfg)
|
||||
login, cleanup, err := LoginFromJSONReader(ctx, strings.NewReader(tst.Body), &userAPI, &userAPI, &userAPI, &userInteractive, cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("LoginFromJSONReader failed: %+v", err)
|
||||
}
|
||||
|
|
@ -139,6 +146,13 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
|||
WantErrCode: "M_INVALID_ARGUMENT_VALUE",
|
||||
},
|
||||
}
|
||||
userInteractive := UserInteractive{
|
||||
Flows: []userInteractiveFlow{},
|
||||
Types: make(map[string]Type),
|
||||
Sessions: make(map[string][]string),
|
||||
Params: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
for _, tst := range tsts {
|
||||
t.Run(tst.Name, func(t *testing.T) {
|
||||
var userAPI fakeUserInternalAPI
|
||||
|
|
@ -147,7 +161,7 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
|||
ServerName: serverName,
|
||||
},
|
||||
}
|
||||
_, cleanup, errRes := LoginFromJSONReader(ctx, strings.NewReader(tst.Body), &userAPI, &userAPI, cfg)
|
||||
_, cleanup, errRes := LoginFromJSONReader(ctx, strings.NewReader(tst.Body), &userAPI, &userAPI, &userAPI, &userInteractive, cfg)
|
||||
if errRes == nil {
|
||||
cleanup(ctx, nil)
|
||||
t.Fatalf("LoginFromJSONReader err: got %+v, want code %q", errRes, tst.WantErrCode)
|
||||
|
|
@ -160,6 +174,8 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
|||
|
||||
type fakeUserInternalAPI struct {
|
||||
UserInternalAPIForLogin
|
||||
uapi.UserLoginAPI
|
||||
uapi.ClientUserAPI
|
||||
DeletedTokens []string
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -113,3 +113,12 @@ func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login,
|
|||
}
|
||||
return &r.Login, nil
|
||||
}
|
||||
|
||||
func (t *LoginTypePassword) AddFLows(userInteractive *UserInteractive) {
|
||||
flow := userInteractiveFlow{
|
||||
Stages: []string{t.Name()},
|
||||
}
|
||||
|
||||
userInteractive.Flows = append(userInteractive.Flows, flow)
|
||||
userInteractive.Types[t.Name()] = t
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/internal/mapsutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/util"
|
||||
|
|
@ -75,7 +76,7 @@ type Login struct {
|
|||
|
||||
// Username returns the user localpart/user_id in this request, if it exists.
|
||||
func (r *Login) Username() string {
|
||||
if r.Identifier.Type == "m.id.user" {
|
||||
if r.Identifier.Type == "m.id.user" || r.Identifier.Type == "m.id.decentralizedid" {
|
||||
return r.Identifier.User
|
||||
}
|
||||
// deprecated but without it Element iOS won't log in
|
||||
|
|
@ -109,24 +110,39 @@ type UserInteractive struct {
|
|||
Types map[string]Type
|
||||
// Map of session ID to completed login types, will need to be extended in future
|
||||
Sessions map[string][]string
|
||||
Params map[string]interface{}
|
||||
}
|
||||
|
||||
func NewUserInteractive(userAccountAPI api.UserLoginAPI, cfg *config.ClientAPI) *UserInteractive {
|
||||
func NewUserInteractive(
|
||||
userAccountAPI api.UserLoginAPI,
|
||||
clientUserAPI api.ClientUserAPI,
|
||||
cfg *config.ClientAPI,
|
||||
) *UserInteractive {
|
||||
userInteractive := UserInteractive{
|
||||
Flows: []userInteractiveFlow{},
|
||||
Types: make(map[string]Type),
|
||||
Sessions: make(map[string][]string),
|
||||
Params: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
if !cfg.PasswordAuthenticationDisabled {
|
||||
typePassword := &LoginTypePassword{
|
||||
GetAccountByPassword: userAccountAPI.QueryAccountByPassword,
|
||||
Config: cfg,
|
||||
}
|
||||
return &UserInteractive{
|
||||
Flows: []userInteractiveFlow{
|
||||
{
|
||||
Stages: []string{typePassword.Name()},
|
||||
},
|
||||
},
|
||||
Types: map[string]Type{
|
||||
typePassword.Name(): typePassword,
|
||||
},
|
||||
Sessions: make(map[string][]string),
|
||||
typePassword.AddFLows(&userInteractive)
|
||||
}
|
||||
|
||||
if cfg.PublicKeyAuthentication.Enabled() {
|
||||
typePublicKey := &LoginTypePublicKey{
|
||||
clientUserAPI,
|
||||
&userInteractive,
|
||||
cfg,
|
||||
}
|
||||
typePublicKey.AddFlows(&userInteractive)
|
||||
}
|
||||
|
||||
return &userInteractive
|
||||
}
|
||||
|
||||
func (u *UserInteractive) IsSingleStageFlow(authType string) bool {
|
||||
|
|
@ -147,6 +163,12 @@ func (u *UserInteractive) AddCompletedStage(sessionID, authType string) {
|
|||
u.Unlock()
|
||||
}
|
||||
|
||||
func (u *UserInteractive) DeleteSession(sessionID string) {
|
||||
u.Lock()
|
||||
delete(u.Sessions, sessionID)
|
||||
u.Unlock()
|
||||
}
|
||||
|
||||
type Challenge struct {
|
||||
Completed []string `json:"completed"`
|
||||
Flows []userInteractiveFlow `json:"flows"`
|
||||
|
|
@ -158,17 +180,27 @@ type Challenge struct {
|
|||
// Challenge returns an HTTP 401 with the supported flows for authenticating
|
||||
func (u *UserInteractive) challenge(sessionID string) *util.JSONResponse {
|
||||
u.RLock()
|
||||
paramsCopy := mapsutil.MapCopy(u.Params)
|
||||
completed := u.Sessions[sessionID]
|
||||
flows := u.Flows
|
||||
u.RUnlock()
|
||||
|
||||
for key, element := range paramsCopy {
|
||||
p := GetAuthParams(element)
|
||||
if p != nil {
|
||||
// If an auth flow has params,
|
||||
// send it as part of the challenge.
|
||||
paramsCopy[key] = p
|
||||
}
|
||||
}
|
||||
|
||||
return &util.JSONResponse{
|
||||
Code: 401,
|
||||
JSON: Challenge{
|
||||
Completed: completed,
|
||||
Flows: flows,
|
||||
Session: sessionID,
|
||||
Params: make(map[string]interface{}),
|
||||
Params: paramsCopy,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -265,3 +297,12 @@ func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *
|
|||
// TODO: Check if there's more stages to go and return an error
|
||||
return login, nil
|
||||
}
|
||||
|
||||
func GetAuthParams(params interface{}) interface{} {
|
||||
v, ok := params.(config.AuthParams)
|
||||
if ok {
|
||||
p := v.GetParams()
|
||||
return p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
type fakeAccountDatabase struct{}
|
||||
type fakeAccountDatabase struct {
|
||||
api.UserLoginAPI
|
||||
api.ClientUserAPI
|
||||
}
|
||||
|
||||
func (d *fakeAccountDatabase) PerformPasswordUpdate(ctx context.Context, req *api.PerformPasswordUpdateRequest, res *api.PerformPasswordUpdateResponse) error {
|
||||
return nil
|
||||
|
|
@ -50,7 +53,8 @@ func setup() *UserInteractive {
|
|||
ServerName: serverName,
|
||||
},
|
||||
}
|
||||
return NewUserInteractive(&fakeAccountDatabase{}, cfg)
|
||||
accountApi := fakeAccountDatabase{}
|
||||
return NewUserInteractive(&accountApi, &accountApi, cfg)
|
||||
}
|
||||
|
||||
func TestUserInteractiveChallenge(t *testing.T) {
|
||||
|
|
|
|||
24
clientapi/authorization/authorization.go
Normal file
24
clientapi/authorization/authorization.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package authorization
|
||||
|
||||
import (
|
||||
"github.com/matrix-org/dendrite/authorization"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
_ "github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/zion"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func NewRoomserverAuthorization(cfg *config.ClientAPI, rsAPI zion.RoomserverStoreAPI) authorization.Authorization {
|
||||
// Load authorization manager for Zion
|
||||
if cfg.PublicKeyAuthentication.Ethereum.GetEnableAuthZ() {
|
||||
auth, err := zion.NewZionAuthorization(cfg, rsAPI)
|
||||
|
||||
if err != nil {
|
||||
log.Errorln("Failed to initialise Zion authorization manager. Using default.", err)
|
||||
} else {
|
||||
return auth
|
||||
}
|
||||
}
|
||||
|
||||
return &authorization.DefaultAuthorization{}
|
||||
}
|
||||
|
|
@ -42,28 +42,42 @@ type flow struct {
|
|||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
func passwordLogin() flows {
|
||||
f := flows{}
|
||||
func passwordLogin(f *flows) {
|
||||
s := flow{
|
||||
Type: "m.login.password",
|
||||
}
|
||||
f.Flows = append(f.Flows, s)
|
||||
return f
|
||||
}
|
||||
|
||||
func publicKeyLogin(f *flows) {
|
||||
loginFlow := flow{
|
||||
Type: "m.login.publickey",
|
||||
}
|
||||
f.Flows = append(f.Flows, loginFlow)
|
||||
}
|
||||
|
||||
// Login implements GET and POST /login
|
||||
func Login(
|
||||
req *http.Request, userAPI userapi.ClientUserAPI,
|
||||
req *http.Request,
|
||||
userAPI userapi.ClientUserAPI,
|
||||
userInteractiveAuth *auth.UserInteractive,
|
||||
cfg *config.ClientAPI,
|
||||
) util.JSONResponse {
|
||||
if req.Method == http.MethodGet {
|
||||
// TODO: support other forms of login other than password, depending on config options
|
||||
f := flows{}
|
||||
if !cfg.PasswordAuthenticationDisabled {
|
||||
passwordLogin(&f)
|
||||
}
|
||||
if cfg.PublicKeyAuthentication.Enabled() {
|
||||
publicKeyLogin(&f)
|
||||
}
|
||||
// TODO: support other forms of login depending on config options
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: passwordLogin(),
|
||||
JSON: f,
|
||||
}
|
||||
} else if req.Method == http.MethodPost {
|
||||
login, cleanup, authErr := auth.LoginFromJSONReader(req.Context(), req.Body, userAPI, userAPI, cfg)
|
||||
login, cleanup, authErr := auth.LoginFromJSONReader(req.Context(), req.Body, userAPI, userAPI, userAPI, userInteractiveAuth, cfg)
|
||||
if authErr != nil {
|
||||
return *authErr
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/eventutil"
|
||||
"github.com/matrix-org/dendrite/internal/mapsutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
|
|
@ -157,6 +158,13 @@ func (d *sessionsDict) startTimer(duration time.Duration, sessionID string) {
|
|||
})
|
||||
}
|
||||
|
||||
func (d *sessionsDict) hasSession(sessionID string) bool {
|
||||
d.RLock()
|
||||
defer d.RUnlock()
|
||||
_, ok := d.sessions[sessionID]
|
||||
return ok
|
||||
}
|
||||
|
||||
// addCompletedSessionStage records that a session has completed an auth stage
|
||||
// also starts a timer to delete the session once done.
|
||||
func (d *sessionsDict) addCompletedSessionStage(sessionID string, stage authtypes.LoginType) {
|
||||
|
|
@ -241,7 +249,7 @@ type authDict struct {
|
|||
}
|
||||
|
||||
// http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#user-interactive-authentication-api
|
||||
type userInteractiveResponse struct {
|
||||
type UserInteractiveResponse struct {
|
||||
Flows []authtypes.Flow `json:"flows"`
|
||||
Completed []authtypes.LoginType `json:"completed"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
|
|
@ -254,9 +262,18 @@ func newUserInteractiveResponse(
|
|||
sessionID string,
|
||||
fs []authtypes.Flow,
|
||||
params map[string]interface{},
|
||||
) userInteractiveResponse {
|
||||
return userInteractiveResponse{
|
||||
fs, sessions.getCompletedStages(sessionID), params, sessionID,
|
||||
) UserInteractiveResponse {
|
||||
paramsCopy := mapsutil.MapCopy(params)
|
||||
for key, element := range paramsCopy {
|
||||
p := auth.GetAuthParams(element)
|
||||
if p != nil {
|
||||
// If an auth flow has params, make a new copy
|
||||
// and send it as part of the response.
|
||||
paramsCopy[key] = p
|
||||
}
|
||||
}
|
||||
return UserInteractiveResponse{
|
||||
fs, sessions.getCompletedStages(sessionID), paramsCopy, sessionID,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -616,6 +633,10 @@ func Register(
|
|||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.MissingArgument("A known registration type (e.g. m.login.application_service) must be specified if an access_token is provided"),
|
||||
}
|
||||
|
||||
case r.Auth.Type == authtypes.LoginTypePublicKey && cfg.PublicKeyAuthentication.Enabled():
|
||||
// Skip checks here. Will be validated later.
|
||||
|
||||
default:
|
||||
// Spec-compliant case (neither the access_token nor the login type are
|
||||
// specified, so it's a normal user registration)
|
||||
|
|
@ -634,7 +655,7 @@ func Register(
|
|||
"session_id": r.Auth.Session,
|
||||
}).Info("Processing registration request")
|
||||
|
||||
return handleRegistrationFlow(req, r, sessionID, cfg, userAPI, accessToken, accessTokenErr)
|
||||
return handleRegistrationFlow(req, reqBody, r, sessionID, cfg, userAPI, accessToken, accessTokenErr)
|
||||
}
|
||||
|
||||
func handleGuestRegistration(
|
||||
|
|
@ -703,6 +724,7 @@ func handleGuestRegistration(
|
|||
// nolint: gocyclo
|
||||
func handleRegistrationFlow(
|
||||
req *http.Request,
|
||||
reqBody []byte,
|
||||
r registerRequest,
|
||||
sessionID string,
|
||||
cfg *config.ClientAPI,
|
||||
|
|
@ -761,7 +783,17 @@ func handleRegistrationFlow(
|
|||
case authtypes.LoginTypeDummy:
|
||||
// there is nothing to do
|
||||
// Add Dummy to the list of completed registration stages
|
||||
if !cfg.PasswordAuthenticationDisabled {
|
||||
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypeDummy)
|
||||
}
|
||||
|
||||
case authtypes.LoginTypePublicKey:
|
||||
_, authType, err := handlePublicKeyRegistration(cfg, reqBody, &r, userAPI)
|
||||
if err != nil {
|
||||
return *err
|
||||
}
|
||||
|
||||
sessions.addCompletedSessionStage(sessionID, authType)
|
||||
|
||||
case "":
|
||||
// An empty auth type means that we want to fetch the available
|
||||
|
|
|
|||
103
clientapi/routing/register_publickey.go
Normal file
103
clientapi/routing/register_publickey.go
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// 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 (
|
||||
"net/http"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth"
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func handlePublicKeyRegistration(
|
||||
cfg *config.ClientAPI,
|
||||
reqBytes []byte,
|
||||
r *registerRequest,
|
||||
userAPI userapi.ClientUserAPI,
|
||||
) (bool, authtypes.LoginType, *util.JSONResponse) {
|
||||
if !cfg.PublicKeyAuthentication.Enabled() {
|
||||
return false, "", &util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: jsonerror.Forbidden("public key account registration is disabled"),
|
||||
}
|
||||
}
|
||||
|
||||
var authHandler auth.LoginPublicKeyHandler
|
||||
authType := gjson.GetBytes(reqBytes, "auth.public_key_response.type").String()
|
||||
|
||||
switch authType {
|
||||
case authtypes.LoginTypePublicKeyEthereum:
|
||||
authBytes := gjson.GetBytes(reqBytes, "auth.public_key_response")
|
||||
pkEthHandler, err := auth.CreatePublicKeyEthereumHandler(
|
||||
[]byte(authBytes.Raw),
|
||||
userAPI,
|
||||
cfg,
|
||||
)
|
||||
if err != nil {
|
||||
return false, "", &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: err,
|
||||
}
|
||||
}
|
||||
authHandler = pkEthHandler
|
||||
default:
|
||||
// No response. Client is asking for a new registration session
|
||||
return false, authtypes.LoginStagePublicKeyNewRegistration, nil
|
||||
}
|
||||
|
||||
if !sessions.hasSession(authHandler.GetSession()) {
|
||||
return false, "", &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.Unknown("the session ID is missing or unknown."),
|
||||
}
|
||||
}
|
||||
|
||||
isValidUserId := authHandler.IsValidUserId(r.Username)
|
||||
if !isValidUserId {
|
||||
return false, "", &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.InvalidUsername(r.Username),
|
||||
}
|
||||
}
|
||||
|
||||
isValidated, jerr := authHandler.ValidateLoginResponse()
|
||||
if jerr != nil {
|
||||
return false, "", &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jerr,
|
||||
}
|
||||
}
|
||||
|
||||
// Registration flow requires a password to
|
||||
// create a user account. Create a random one
|
||||
// to satisfy the requirement. This is not used
|
||||
// for public key cryptography.
|
||||
createPassword(r)
|
||||
|
||||
return isValidated, authtypes.LoginType(authHandler.GetType()), nil
|
||||
}
|
||||
|
||||
func createPassword(request *registerRequest) {
|
||||
// Public key auth does not use password.
|
||||
// Create a random one that is never used.
|
||||
// Login validation will be done using public / private
|
||||
// key cryptography.
|
||||
request.Password = util.RandomString(sessionIDLength)
|
||||
}
|
||||
382
clientapi/routing/register_publickey_test.go
Normal file
382
clientapi/routing/register_publickey_test.go
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// 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 (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth"
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/internal/mapsutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
testutil "github.com/matrix-org/dendrite/test"
|
||||
uapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const testCaip10UserId = "eip155=3a1=3a0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"
|
||||
|
||||
type registerContext struct {
|
||||
config *config.ClientAPI
|
||||
userInteractive *auth.UserInteractive
|
||||
}
|
||||
|
||||
func createRegisterContext(_ *testing.T) *registerContext {
|
||||
cfg := &config.ClientAPI{
|
||||
Matrix: &config.Global{
|
||||
ServerName: testutil.TestServerName,
|
||||
},
|
||||
Derived: &config.Derived{},
|
||||
PasswordAuthenticationDisabled: true,
|
||||
PublicKeyAuthentication: config.PublicKeyAuthentication{
|
||||
Ethereum: config.EthereumAuthConfig{
|
||||
Enabled: true,
|
||||
Version: 1,
|
||||
ConfigChainID: strconv.Itoa(testutil.EthereumTestNetworkId),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pkFlows := cfg.PublicKeyAuthentication.GetPublicKeyRegistrationFlows()
|
||||
cfg.Derived.Registration.Flows = append(cfg.Derived.Registration.Flows, pkFlows...)
|
||||
pkParams := cfg.PublicKeyAuthentication.GetPublicKeyRegistrationParams()
|
||||
cfg.Derived.Registration.Params = mapsutil.MapsUnion(cfg.Derived.Registration.Params, pkParams)
|
||||
|
||||
var userAPI fakePublicKeyUserApi
|
||||
var loginApi uapi.UserLoginAPI
|
||||
|
||||
userInteractive := auth.NewUserInteractive(
|
||||
loginApi,
|
||||
&userAPI,
|
||||
cfg)
|
||||
|
||||
return ®isterContext{
|
||||
config: cfg,
|
||||
userInteractive: userInteractive,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type fakeHttpRequest struct {
|
||||
request *http.Request
|
||||
body []byte
|
||||
registerRequest registerRequest
|
||||
}
|
||||
|
||||
func createFakeHttpRequest(body string) *fakeHttpRequest {
|
||||
var r registerRequest
|
||||
req, _ := http.NewRequest(http.MethodPost, "", strings.NewReader(body))
|
||||
reqBody := []byte(body)
|
||||
json.Unmarshal([]byte(body), &r)
|
||||
|
||||
return &fakeHttpRequest{
|
||||
request: req,
|
||||
body: reqBody,
|
||||
registerRequest: r,
|
||||
}
|
||||
}
|
||||
|
||||
type fakePublicKeyUserApi struct {
|
||||
auth.UserInternalAPIForLogin
|
||||
uapi.UserLoginAPI
|
||||
uapi.ClientUserAPI
|
||||
DeletedTokens []string
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) QueryAccountAvailability(ctx context.Context, req *uapi.QueryAccountAvailabilityRequest, res *uapi.QueryAccountAvailabilityResponse) error {
|
||||
if req.Localpart == "does_not_exist" {
|
||||
res.Available = true
|
||||
return nil
|
||||
}
|
||||
|
||||
res.Available = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) QueryAccountByPassword(ctx context.Context, req *uapi.QueryAccountByPasswordRequest, res *uapi.QueryAccountByPasswordResponse) error {
|
||||
if req.PlaintextPassword == "invalidpassword" {
|
||||
res.Account = nil
|
||||
return nil
|
||||
}
|
||||
res.Exists = true
|
||||
res.Account = &uapi.Account{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) PerformDeviceCreation(
|
||||
ctx context.Context,
|
||||
req *uapi.PerformDeviceCreationRequest,
|
||||
res *uapi.PerformDeviceCreationResponse) error {
|
||||
res.DeviceCreated = true
|
||||
res.Device = &uapi.Device{
|
||||
ID: "device_id",
|
||||
UserID: req.Localpart,
|
||||
AccessToken: req.AccessToken,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) PerformAccountCreation(
|
||||
ctx context.Context,
|
||||
req *uapi.PerformAccountCreationRequest,
|
||||
res *uapi.PerformAccountCreationResponse) error {
|
||||
res.AccountCreated = true
|
||||
res.Account = &uapi.Account{
|
||||
AppServiceID: req.AppServiceID,
|
||||
Localpart: req.Localpart,
|
||||
ServerName: testutil.TestServerName,
|
||||
UserID: fmt.Sprintf("@%s:%s", req.Localpart, testutil.TestServerName),
|
||||
AccountType: req.AccountType,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) PerformLoginTokenDeletion(ctx context.Context, req *uapi.PerformLoginTokenDeletionRequest, res *uapi.PerformLoginTokenDeletionResponse) error {
|
||||
ua.DeletedTokens = append(ua.DeletedTokens, req.Token)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) PerformLoginTokenCreation(ctx context.Context, req *uapi.PerformLoginTokenCreationRequest, res *uapi.PerformLoginTokenCreationResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*fakePublicKeyUserApi) QueryLoginToken(ctx context.Context, req *uapi.QueryLoginTokenRequest, res *uapi.QueryLoginTokenResponse) error {
|
||||
if req.Token == "invalidtoken" {
|
||||
return nil
|
||||
}
|
||||
|
||||
res.Data = &uapi.LoginTokenData{UserID: "@auser:example.com"}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newRegistrationSession(
|
||||
t *testing.T,
|
||||
userId string,
|
||||
userAPI *fakePublicKeyUserApi,
|
||||
) string {
|
||||
body := fmt.Sprintf(`{
|
||||
"auth": {
|
||||
"type": "m.login.publickey",
|
||||
"username": "%v"
|
||||
}
|
||||
}`,
|
||||
userId)
|
||||
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
fakeReq := createFakeHttpRequest(test.Body)
|
||||
sessionID := util.RandomString(sessionIDLength)
|
||||
registerContext := createRegisterContext(t)
|
||||
|
||||
// Test
|
||||
response := handleRegistrationFlow(
|
||||
fakeReq.request,
|
||||
fakeReq.body,
|
||||
fakeReq.registerRequest,
|
||||
sessionID,
|
||||
registerContext.config,
|
||||
userAPI,
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
|
||||
json := response.JSON.(UserInteractiveResponse)
|
||||
return json.Session
|
||||
}
|
||||
|
||||
func TestRegisterEthereum(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
wallet, _ := testutil.CreateTestAccount()
|
||||
message, _ := testutil.CreateEip4361TestMessage(wallet.PublicAddress)
|
||||
signature, _ := testutil.SignMessage(message.String(), wallet.PrivateKey)
|
||||
registerContext := createRegisterContext(t)
|
||||
sessionId := newRegistrationSession(
|
||||
t,
|
||||
wallet.Eip155UserId,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
// Escape \t and \n. Work around for marshalling and unmarshalling message.
|
||||
msgStr := testutil.FromEip4361MessageToString(message)
|
||||
body := fmt.Sprintf(`{
|
||||
"username": "%v",
|
||||
"auth": {
|
||||
"type": "m.login.publickey",
|
||||
"session": "%v",
|
||||
"public_key_response": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v",
|
||||
"user_id": "%v",
|
||||
"message": "%v",
|
||||
"signature": "%v"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
wallet.Eip155UserId,
|
||||
sessionId,
|
||||
sessionId,
|
||||
wallet.Eip155UserId,
|
||||
msgStr,
|
||||
signature,
|
||||
)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
fakeReq := createFakeHttpRequest(test.Body)
|
||||
|
||||
// Test
|
||||
response := handleRegistrationFlow(
|
||||
fakeReq.request,
|
||||
fakeReq.body,
|
||||
fakeReq.registerRequest,
|
||||
sessionId,
|
||||
registerContext.config,
|
||||
&userAPI,
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.NotNil(response, "response actual: nil, expected: not nil")
|
||||
registerRes := response.JSON.(registerResponse)
|
||||
assert.Truef(
|
||||
registerRes.UserID == wallet.Eip155UserId,
|
||||
"registerRes.UserID actual: %v, expected: %v", registerRes.UserID, wallet.Eip155UserId)
|
||||
assert.NotEmptyf(
|
||||
registerRes.AccessToken,
|
||||
"registerRes.AccessToken actual: empty, expected: not empty")
|
||||
}
|
||||
|
||||
func TestNewRegistrationSession(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
|
||||
body := fmt.Sprintf(`{
|
||||
"auth": {
|
||||
"type": "m.login.publickey",
|
||||
"username": "%v"
|
||||
}
|
||||
}`,
|
||||
testCaip10UserId)
|
||||
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
fakeReq := createFakeHttpRequest(test.Body)
|
||||
sessionID := util.RandomString(sessionIDLength)
|
||||
registerContext := createRegisterContext(t)
|
||||
|
||||
// Test
|
||||
response := handleRegistrationFlow(
|
||||
fakeReq.request,
|
||||
fakeReq.body,
|
||||
fakeReq.registerRequest,
|
||||
sessionID,
|
||||
registerContext.config,
|
||||
&userAPI,
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.NotNilf(response, "response not nil")
|
||||
assert.Truef(
|
||||
response.Code == http.StatusUnauthorized,
|
||||
"response.Code actual %v, expected %v", response.Code, http.StatusUnauthorized)
|
||||
json := response.JSON.(UserInteractiveResponse)
|
||||
assert.NotEmptyf(json.Session, "response.Session")
|
||||
assert.NotEmptyf(json.Completed, "response.Completed")
|
||||
assert.Truef(
|
||||
json.Completed[0] == authtypes.LoginStagePublicKeyNewRegistration,
|
||||
"response.Completed[0] actual %v, expected %v", json.Completed[0], authtypes.LoginStagePublicKeyNewRegistration)
|
||||
assert.Truef(
|
||||
authtypes.LoginTypePublicKeyEthereum == json.Flows[0].Stages[0],
|
||||
"response.Flows[0].Stages[0] actual: %v, expected: %v", json.Flows[0].Stages[0], authtypes.LoginTypePublicKeyEthereum)
|
||||
|
||||
params := json.Params[authtypes.LoginTypePublicKeyEthereum]
|
||||
assert.NotEmptyf(
|
||||
params,
|
||||
"response.Params[\"%v\"] actual %v, expected %v",
|
||||
authtypes.LoginTypePublicKeyEthereum,
|
||||
params,
|
||||
"[object]")
|
||||
ethParams := params.(config.EthereumAuthParams)
|
||||
assert.Equalf(
|
||||
testutil.EthereumTestNetworkId,
|
||||
ethParams.ChainID,
|
||||
"ChainID actual: %d, expected %d", ethParams.ChainID, testutil.EthereumTestNetworkId)
|
||||
assert.NotEmptyf(ethParams.Version, "Version actual: \"\", expected: not empty")
|
||||
}
|
||||
|
||||
func TestRegistrationUnimplementedAlgo(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
body := fmt.Sprintf(`{
|
||||
"auth": {
|
||||
"type": "m.login.publickey.someAlgo",
|
||||
"username": "%v"
|
||||
}
|
||||
}`,
|
||||
testCaip10UserId)
|
||||
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
fakeReq := createFakeHttpRequest(test.Body)
|
||||
sessionID := util.RandomString(sessionIDLength)
|
||||
registerContext := createRegisterContext(t)
|
||||
|
||||
// Test
|
||||
response := handleRegistrationFlow(
|
||||
fakeReq.request,
|
||||
fakeReq.body,
|
||||
fakeReq.registerRequest,
|
||||
sessionID,
|
||||
registerContext.config,
|
||||
&userAPI,
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.NotNilf(response, "response not nil")
|
||||
assert.Truef(
|
||||
response.Code == http.StatusNotImplemented,
|
||||
"response.Code actual %v, expected %v", response.Code, http.StatusNotImplemented)
|
||||
}
|
||||
|
|
@ -27,8 +27,10 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
|
||||
appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
|
||||
authz "github.com/matrix-org/dendrite/authorization"
|
||||
"github.com/matrix-org/dendrite/clientapi/api"
|
||||
"github.com/matrix-org/dendrite/clientapi/auth"
|
||||
clientApiAuthz "github.com/matrix-org/dendrite/clientapi/authorization"
|
||||
clientutil "github.com/matrix-org/dendrite/clientapi/httputil"
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||
|
|
@ -40,8 +42,11 @@ import (
|
|||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
zion "github.com/matrix-org/dendrite/zion"
|
||||
)
|
||||
|
||||
var ReleaseVersion string
|
||||
|
||||
// Setup registers HTTP handlers with the given ServeMux. It also supplies the given http.Client
|
||||
// to clients which need to make outbound HTTP requests.
|
||||
//
|
||||
|
|
@ -63,10 +68,17 @@ func Setup(
|
|||
extRoomsProvider api.ExtraPublicRoomsProvider,
|
||||
mscCfg *config.MSCs, natsClient *nats.Conn,
|
||||
) {
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"ReleaseVersion": ReleaseVersion,
|
||||
}).Info("Started clientAPI router with ReleaseVersion")
|
||||
|
||||
prometheus.MustRegister(amtRegUsers, sendEventDuration)
|
||||
|
||||
rateLimits := httputil.NewRateLimits(&cfg.RateLimiting)
|
||||
userInteractiveAuth := auth.NewUserInteractive(userAPI, cfg)
|
||||
userInteractiveAuth := auth.NewUserInteractive(userAPI, userAPI, cfg)
|
||||
clientAuthz := zion.ClientRoomserverStruct{ClientRoomserverAPI: rsAPI}
|
||||
authorization := clientApiAuthz.NewRoomserverAuthorization(cfg, clientAuthz)
|
||||
|
||||
unstableFeatures := map[string]bool{
|
||||
"org.matrix.e2e_cross_signing": true,
|
||||
|
|
@ -103,6 +115,7 @@ func Setup(
|
|||
JSON: struct {
|
||||
Versions []string `json:"versions"`
|
||||
UnstableFeatures map[string]bool `json:"unstable_features"`
|
||||
ReleaseVersion string `json:"release_version"`
|
||||
}{Versions: []string{
|
||||
"r0.0.1",
|
||||
"r0.1.0",
|
||||
|
|
@ -114,7 +127,7 @@ func Setup(
|
|||
"v1.0",
|
||||
"v1.1",
|
||||
"v1.2",
|
||||
}, UnstableFeatures: unstableFeatures},
|
||||
}, UnstableFeatures: unstableFeatures, ReleaseVersion: ReleaseVersion},
|
||||
}
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
|
@ -249,6 +262,20 @@ func Setup(
|
|||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
|
||||
isAllowed, _ := authorization.IsAllowed(authz.AuthorizationArgs{
|
||||
RoomId: vars["roomIDOrAlias"],
|
||||
UserId: device.UserID,
|
||||
Permission: authz.PermissionRead,
|
||||
})
|
||||
|
||||
if !isAllowed {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.Forbidden("Unauthorised"),
|
||||
}
|
||||
}
|
||||
|
||||
return JoinRoomByIDOrAlias(
|
||||
req, device, rsAPI, userAPI, vars["roomIDOrAlias"],
|
||||
)
|
||||
|
|
@ -333,6 +360,20 @@ func Setup(
|
|||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
|
||||
isAllowedInviter, _ := authorization.IsAllowed(authz.AuthorizationArgs{
|
||||
RoomId: vars["roomID"],
|
||||
UserId: device.UserID,
|
||||
Permission: authz.PermissionInvite,
|
||||
})
|
||||
|
||||
if !isAllowedInviter {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.Forbidden("Inviter not allowed"),
|
||||
}
|
||||
}
|
||||
|
||||
return SendInvite(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
|
@ -357,6 +398,20 @@ func Setup(
|
|||
v3mux.Handle("/rooms/{roomID}/send/{eventType}",
|
||||
httputil.MakeAuthAPI("send_message", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
||||
isAllowed, _ := authorization.IsAllowed(authz.AuthorizationArgs{
|
||||
RoomId: vars["roomID"],
|
||||
UserId: device.UserID,
|
||||
Permission: authz.PermissionWrite,
|
||||
})
|
||||
|
||||
if !isAllowed {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.Forbidden("Unauthorised"),
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
|
|
@ -366,6 +421,20 @@ func Setup(
|
|||
v3mux.Handle("/rooms/{roomID}/send/{eventType}/{txnID}",
|
||||
httputil.MakeAuthAPI("send_message", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
||||
isAllowed, _ := authorization.IsAllowed(authz.AuthorizationArgs{
|
||||
RoomId: vars["roomID"],
|
||||
UserId: device.UserID,
|
||||
Permission: authz.PermissionWrite,
|
||||
})
|
||||
|
||||
if !isAllowed {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.Forbidden("Unauthorised to send event"),
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
|
|
@ -486,7 +555,6 @@ func Setup(
|
|||
return GetVisibility(req, rsAPI, vars["roomID"])
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/directory/list/room/{roomID}",
|
||||
httputil.MakeAuthAPI("directory_list", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -626,7 +694,7 @@ func Setup(
|
|||
if r := rateLimits.Limit(req, nil); r != nil {
|
||||
return *r
|
||||
}
|
||||
return Login(req, userAPI, cfg)
|
||||
return Login(req, userAPI, userInteractiveAuth, cfg)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ const HEAD = "HEAD"
|
|||
// due to the error:
|
||||
// When using COPY with more than one source file, the destination must be a directory and end with a /
|
||||
// We need to run a postgres anyway, so use the dockerfile associated with Complement instead.
|
||||
const Dockerfile = `FROM golang:1.18-stretch as build
|
||||
const Dockerfile = `FROM golang:1.19-buster as build
|
||||
RUN apt-get update && apt-get install -y postgresql
|
||||
WORKDIR /build
|
||||
|
||||
|
|
@ -57,25 +57,25 @@ WORKDIR /build
|
|||
# Complement Dockerfile which wgets a branch.
|
||||
COPY . .
|
||||
|
||||
RUN go build ./cmd/dendrite-monolith-server
|
||||
RUN go build ./cmd/generate-keys
|
||||
RUN go build ./cmd/generate-config
|
||||
RUN go build --race ./cmd/dendrite-monolith-server
|
||||
RUN go build --race ./cmd/generate-keys
|
||||
RUN go build --race ./cmd/generate-config
|
||||
RUN ./generate-config --ci > dendrite.yaml
|
||||
RUN ./generate-keys --private-key matrix_key.pem --tls-cert server.crt --tls-key server.key
|
||||
|
||||
# Replace the connection string with a single postgres DB, using user/db = 'postgres' and no password
|
||||
RUN sed -i "s%connection_string:.*$%connection_string: postgresql://postgres@localhost/postgres?sslmode=disable%g" dendrite.yaml
|
||||
# No password when connecting over localhost
|
||||
RUN sed -i "s%127.0.0.1/32 md5%127.0.0.1/32 trust%g" /etc/postgresql/9.6/main/pg_hba.conf
|
||||
RUN sed -i "s%127.0.0.1/32 md5%127.0.0.1/32 trust%g" /etc/postgresql/11/main/pg_hba.conf
|
||||
# Bump up max conns for moar concurrency
|
||||
RUN sed -i 's/max_connections = 100/max_connections = 2000/g' /etc/postgresql/9.6/main/postgresql.conf
|
||||
RUN sed -i 's/max_connections = 100/max_connections = 2000/g' /etc/postgresql/11/main/postgresql.conf
|
||||
RUN sed -i 's/max_open_conns:.*$/max_open_conns: 100/g' dendrite.yaml
|
||||
|
||||
# This entry script starts postgres, waits for it to be up then starts dendrite
|
||||
RUN echo '\
|
||||
#!/bin/bash -eu \n\
|
||||
pg_lsclusters \n\
|
||||
pg_ctlcluster 9.6 main start \n\
|
||||
pg_ctlcluster 11 main start \n\
|
||||
\n\
|
||||
until pg_isready \n\
|
||||
do \n\
|
||||
|
|
@ -341,7 +341,7 @@ func runImage(dockerClient *client.Client, volumeName, version, imageID string)
|
|||
{
|
||||
Type: mount.TypeVolume,
|
||||
Source: volumeName,
|
||||
Target: "/var/lib/postgresql/9.6/main",
|
||||
Target: "/var/lib/postgresql/11/main",
|
||||
},
|
||||
},
|
||||
}, nil, nil, "dendrite_upgrade_test_"+version)
|
||||
|
|
@ -416,6 +416,9 @@ func loadAndRunTests(dockerClient *client.Client, volumeName, v string, branchTo
|
|||
if err = runTests(csAPIURL, v); err != nil {
|
||||
return fmt.Errorf("failed to run tests on version %s: %s", v, err)
|
||||
}
|
||||
// Sleep to let the database sync before returning and destroying the dendrite container
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
|
@ -219,14 +220,19 @@ func verifyTestsRan(baseURL string, branchNames []string) error {
|
|||
}
|
||||
// we expect 4 messages per version
|
||||
msgCount := 0
|
||||
// To aid debugging when some messages are missing
|
||||
msgArray := make([]gomatrix.Event, 0)
|
||||
|
||||
for _, ev := range history.Chunk {
|
||||
if ev.Type == "m.room.message" {
|
||||
msgCount += 1
|
||||
msgArray = append(msgArray, ev)
|
||||
}
|
||||
}
|
||||
wantMsgCount := len(branchNames) * 4
|
||||
if msgCount != wantMsgCount {
|
||||
return fmt.Errorf("got %d messages in global room, want %d", msgCount, wantMsgCount)
|
||||
msgArrayJSON, _ := json.Marshal(msgArray)
|
||||
return fmt.Errorf("got %d messages in global room, want %d msgArray %v", msgCount, wantMsgCount, string(msgArrayJSON))
|
||||
}
|
||||
log.Println(" messages exist: OK")
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -170,6 +170,17 @@ client_api:
|
|||
# of whether registration is otherwise disabled.
|
||||
registration_shared_secret: ""
|
||||
|
||||
# Disable password authentication.
|
||||
password_authentication_disabled: false
|
||||
|
||||
# public key authentication
|
||||
public_key_authentication:
|
||||
ethereum:
|
||||
enabled: false
|
||||
version: 1
|
||||
chain_id: 31337
|
||||
network_url: "http://127.0.0.1:8545"
|
||||
|
||||
# Whether to require reCAPTCHA for registration. If you have enabled registration
|
||||
# then this is HIGHLY RECOMMENDED to reduce the risk of your homeserver being used
|
||||
# for coordinated spam attacks.
|
||||
|
|
|
|||
|
|
@ -166,6 +166,17 @@ client_api:
|
|||
# of whether registration is otherwise disabled.
|
||||
registration_shared_secret: ""
|
||||
|
||||
# Disable password authentication.
|
||||
password_authentication_disabled: false
|
||||
|
||||
# public key authentication
|
||||
public_key_authentication:
|
||||
ethereum:
|
||||
enabled: false
|
||||
version: 1
|
||||
chain_id: 31337
|
||||
network_url: "http://127.0.0.1:8545"
|
||||
|
||||
# Whether to require reCAPTCHA for registration. If you have enabled registration
|
||||
# then this is HIGHLY RECOMMENDED to reduce the risk of your homeserver being used
|
||||
# for coordinated spam attacks.
|
||||
|
|
|
|||
387
dendrite-zion.yaml
Normal file
387
dendrite-zion.yaml
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
# This is the Dendrite configuration file.
|
||||
#
|
||||
# The configuration is split up into sections - each Dendrite component has a
|
||||
# configuration section, in addition to the "global" section which applies to
|
||||
# all components.
|
||||
#
|
||||
# At a minimum, to get started, you will need to update the settings in the
|
||||
# "global" section for your deployment, and you will need to check that the
|
||||
# database "connection_string" line in each component section is correct.
|
||||
#
|
||||
# Each component with a "database" section can accept the following formats
|
||||
# for "connection_string":
|
||||
# SQLite: file:filename.db
|
||||
# file:///path/to/filename.db
|
||||
# PostgreSQL: postgresql://user:pass@hostname/database?params=...
|
||||
#
|
||||
# SQLite is embedded into Dendrite and therefore no further prerequisites are
|
||||
# needed for the database when using SQLite mode. However, performance with
|
||||
# PostgreSQL is significantly better and recommended for multi-user deployments.
|
||||
# SQLite is typically around 20-30% slower than PostgreSQL when tested with a
|
||||
# small number of users and likely will perform worse still with a higher volume
|
||||
# of users.
|
||||
#
|
||||
# The "max_open_conns" and "max_idle_conns" settings configure the maximum
|
||||
# number of open/idle database connections. The value 0 will use the database
|
||||
# engine default, and a negative value will use unlimited connections. The
|
||||
# "conn_max_lifetime" option controls the maximum length of time a database
|
||||
# connection can be idle in seconds - a negative value is unlimited.
|
||||
|
||||
# The version of the configuration file.
|
||||
version: 2
|
||||
|
||||
# Global Matrix configuration. This configuration applies to all components.
|
||||
global:
|
||||
# The domain name of this homeserver.
|
||||
server_name: ${SERVER_NAME}
|
||||
|
||||
# The path to the signing private key file, used to sign requests and events.
|
||||
# Note that this is NOT the same private key as used for TLS! To generate a
|
||||
# signing key, use "./bin/generate-keys --private-key matrix_key.pem".
|
||||
private_key: matrix_key.pem
|
||||
|
||||
# Global database connection pool, for PostgreSQL monolith deployments only. If
|
||||
# this section is populated then you can omit the "database" blocks in all other
|
||||
# sections. For polylith deployments, or monolith deployments using SQLite databases,
|
||||
# you must configure the "database" block for each component instead.
|
||||
database:
|
||||
connection_string: ${DATABASE_CONNECTION_STRING}
|
||||
max_open_conns: 10
|
||||
max_idle_conns: 5
|
||||
conn_max_lifetime: -1
|
||||
|
||||
# The paths and expiry timestamps (as a UNIX timestamp in millisecond precision)
|
||||
# to old signing private keys that were formerly in use on this domain. These
|
||||
# keys will not be used for federation request or event signing, but will be
|
||||
# provided to any other homeserver that asks when trying to verify old events.
|
||||
# old_private_keys:
|
||||
# - private_key: old_matrix_key.pem
|
||||
# expired_at: 1601024554498
|
||||
|
||||
# How long a remote server can cache our server signing key before requesting it
|
||||
# again. Increasing this number will reduce the number of requests made by other
|
||||
# servers for our key but increases the period that a compromised key will be
|
||||
# considered valid by other homeservers.
|
||||
key_validity_period: 168h0m0s
|
||||
|
||||
# The server name to delegate server-server communications to, with optional port
|
||||
# e.g. localhost:443
|
||||
well_known_server_name: ""
|
||||
|
||||
# Lists of domains that the server will trust as identity servers to verify third
|
||||
# party identifiers such as phone numbers and email addresses.
|
||||
trusted_third_party_id_servers:
|
||||
- matrix.org
|
||||
- vector.im
|
||||
|
||||
# Disables federation. Dendrite will not be able to make any outbound HTTP requests
|
||||
# to other servers and the federation API will not be exposed.
|
||||
disable_federation: false
|
||||
|
||||
# Configures the handling of presence events.
|
||||
presence:
|
||||
# Whether inbound presence events are allowed, e.g. receiving presence events from other servers
|
||||
enable_inbound: false
|
||||
# Whether outbound presence events are allowed, e.g. sending presence events to other servers
|
||||
enable_outbound: false
|
||||
|
||||
# Server notices allows server admins to send messages to all users.
|
||||
server_notices:
|
||||
enabled: false
|
||||
# The server localpart to be used when sending notices, ensure this is not yet taken
|
||||
local_part: "_server"
|
||||
# The displayname to be used when sending notices
|
||||
display_name: "Server alerts"
|
||||
# The mxid of the avatar to use
|
||||
avatar_url: ""
|
||||
# The roomname to be used when creating messages
|
||||
room_name: "Server Alerts"
|
||||
|
||||
# Configuration for NATS JetStream
|
||||
jetstream:
|
||||
# A list of NATS Server addresses to connect to. If none are specified, an
|
||||
# internal NATS server will be started automatically when running Dendrite
|
||||
# in monolith mode. It is required to specify the address of at least one
|
||||
# NATS Server node if running in polylith mode.
|
||||
addresses:
|
||||
# - localhost:4222
|
||||
|
||||
# Keep all NATS streams in memory, rather than persisting it to the storage
|
||||
# path below. This option is present primarily for integration testing and
|
||||
# should not be used on a real world Dendrite deployment.
|
||||
in_memory: false
|
||||
|
||||
# Persistent directory to store JetStream streams in. This directory
|
||||
# should be preserved across Dendrite restarts.
|
||||
storage_path: ./
|
||||
|
||||
# The prefix to use for stream names for this homeserver - really only
|
||||
# useful if running more than one Dendrite on the same NATS deployment.
|
||||
topic_prefix: Dendrite
|
||||
|
||||
# Configuration for Prometheus metric collection.
|
||||
metrics:
|
||||
# Whether or not Prometheus metrics are enabled.
|
||||
enabled: false
|
||||
|
||||
# HTTP basic authentication to protect access to monitoring.
|
||||
basic_auth:
|
||||
username: metrics
|
||||
password: metrics
|
||||
|
||||
# DNS cache options. The DNS cache may reduce the load on DNS servers
|
||||
# if there is no local caching resolver available for use.
|
||||
dns_cache:
|
||||
# Whether or not the DNS cache is enabled.
|
||||
enabled: false
|
||||
|
||||
# Maximum number of entries to hold in the DNS cache, and
|
||||
# for how long those items should be considered valid in seconds.
|
||||
cache_size: 256
|
||||
cache_lifetime: "5m" # 5minutes; see https://pkg.go.dev/time@master#ParseDuration for more
|
||||
|
||||
# Configuration for the Appservice API.
|
||||
app_service_api:
|
||||
internal_api:
|
||||
listen: http://localhost:7777 # Only used in polylith deployments
|
||||
connect: http://localhost:7777 # Only used in polylith deployments
|
||||
database:
|
||||
max_open_conns: 10
|
||||
max_idle_conns: 2
|
||||
conn_max_lifetime: -1
|
||||
|
||||
# Disable the validation of TLS certificates of appservices. This is
|
||||
# not recommended in production since it may allow appservice traffic
|
||||
# to be sent to an unverified endpoint.
|
||||
disable_tls_validation: false
|
||||
|
||||
# Appservice configuration files to load into this homeserver.
|
||||
config_files: []
|
||||
|
||||
# Configuration for the Client API.
|
||||
client_api:
|
||||
internal_api:
|
||||
listen: http://localhost:7771 # Only used in polylith deployments
|
||||
connect: http://localhost:7771 # Only used in polylith deployments
|
||||
external_api:
|
||||
listen: http://[::]:8071
|
||||
|
||||
# Prevents new users from being able to register on this homeserver, except when
|
||||
# using the registration shared secret below.
|
||||
registration_disabled: false
|
||||
|
||||
# Prevents new guest accounts from being created. Guest registration is also
|
||||
# disabled implicitly by setting 'registration_disabled' above.
|
||||
guests_disabled: true
|
||||
|
||||
# If set, allows registration by anyone who knows the shared secret, regardless of
|
||||
# whether registration is otherwise disabled.
|
||||
registration_shared_secret: ""
|
||||
|
||||
# Disable password authentication.
|
||||
password_authentication_disabled: true # TODO: turn this into an environment variable - or create a separate dendrite.yaml for dev vs prod
|
||||
|
||||
# public key authentication
|
||||
public_key_authentication:
|
||||
ethereum:
|
||||
enabled: true
|
||||
version: 1
|
||||
chain_id: ${CHAIN_ID}
|
||||
network_url: ${BLOCKCHAIN_PROVIDER_URL}
|
||||
enable_authz: ${ENABLE_AUTHZ}
|
||||
|
||||
# Whether to require reCAPTCHA for registration.
|
||||
enable_registration_captcha: false
|
||||
|
||||
# Settings for ReCAPTCHA.
|
||||
recaptcha_public_key: ""
|
||||
recaptcha_private_key: ""
|
||||
recaptcha_bypass_secret: ""
|
||||
recaptcha_siteverify_api: ""
|
||||
|
||||
# TURN server information that this homeserver should send to clients.
|
||||
turn:
|
||||
turn_user_lifetime: ""
|
||||
turn_uris: []
|
||||
turn_shared_secret: ""
|
||||
turn_username: ""
|
||||
turn_password: ""
|
||||
|
||||
# Settings for rate-limited endpoints. Rate limiting will kick in after the
|
||||
# threshold number of "slots" have been taken by requests from a specific
|
||||
# host. Each "slot" will be released after the cooloff time in milliseconds.
|
||||
rate_limiting:
|
||||
enabled: true
|
||||
threshold: 5
|
||||
cooloff_ms: 500
|
||||
|
||||
# Configuration for the Federation API.
|
||||
federation_api:
|
||||
internal_api:
|
||||
listen: http://localhost:7772 # Only used in polylith deployments
|
||||
connect: http://localhost:7772 # Only used in polylith deployments
|
||||
external_api:
|
||||
listen: http://[::]:8072
|
||||
database:
|
||||
max_open_conns: 10
|
||||
max_idle_conns: 2
|
||||
conn_max_lifetime: -1
|
||||
|
||||
# How many times we will try to resend a failed transaction to a specific server. The
|
||||
# backoff is 2**x seconds, so 1 = 2 seconds, 2 = 4 seconds, 3 = 8 seconds etc.
|
||||
send_max_retries: 16
|
||||
|
||||
# Disable the validation of TLS certificates of remote federated homeservers. Do not
|
||||
# enable this option in production as it presents a security risk!
|
||||
disable_tls_validation: false
|
||||
|
||||
# Perspective keyservers to use as a backup when direct key fetches fail. This may
|
||||
# be required to satisfy key requests for servers that are no longer online when
|
||||
# joining some rooms.
|
||||
key_perspectives:
|
||||
- server_name: matrix.org
|
||||
keys:
|
||||
- key_id: ed25519:auto
|
||||
public_key: Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw
|
||||
- key_id: ed25519:a_RXGa
|
||||
public_key: l8Hft5qXKn1vfHrg3p4+W8gELQVo8N13JkluMfmn2sQ
|
||||
|
||||
# This option will control whether Dendrite will prefer to look up keys directly
|
||||
# or whether it should try perspective servers first, using direct fetches as a
|
||||
# last resort.
|
||||
prefer_direct_fetch: false
|
||||
|
||||
# Configuration for the Key Server (for end-to-end encryption).
|
||||
key_server:
|
||||
internal_api:
|
||||
listen: http://localhost:7779 # Only used in polylith deployments
|
||||
connect: http://localhost:7779 # Only used in polylith deployments
|
||||
database:
|
||||
max_open_conns: 10
|
||||
max_idle_conns: 2
|
||||
conn_max_lifetime: -1
|
||||
|
||||
# Configuration for the Media API.
|
||||
media_api:
|
||||
internal_api:
|
||||
listen: http://localhost:7774 # Only used in polylith deployments
|
||||
connect: http://localhost:7774 # Only used in polylith deployments
|
||||
external_api:
|
||||
listen: http://[::]:8074
|
||||
database:
|
||||
max_open_conns: 5
|
||||
max_idle_conns: 2
|
||||
conn_max_lifetime: -1
|
||||
|
||||
# Storage path for uploaded media. May be relative or absolute.
|
||||
base_path: ./media_store
|
||||
|
||||
# The maximum allowed file size (in bytes) for media uploads to this homeserver
|
||||
# (0 = unlimited). If using a reverse proxy, ensure it allows requests at
|
||||
# least this large (e.g. client_max_body_size in nginx.)
|
||||
max_file_size_bytes: 10485760
|
||||
|
||||
# Whether to dynamically generate thumbnails if needed.
|
||||
dynamic_thumbnails: false
|
||||
|
||||
# The maximum number of simultaneous thumbnail generators to run.
|
||||
max_thumbnail_generators: 10
|
||||
|
||||
# A list of thumbnail sizes to be generated for media content.
|
||||
thumbnail_sizes:
|
||||
- width: 32
|
||||
height: 32
|
||||
method: crop
|
||||
- width: 96
|
||||
height: 96
|
||||
method: crop
|
||||
- width: 640
|
||||
height: 480
|
||||
method: scale
|
||||
|
||||
# Configuration for experimental MSC's
|
||||
mscs:
|
||||
# A list of enabled MSC's
|
||||
# Currently valid values are:
|
||||
# - msc2836 (Threading, see https://github.com/matrix-org/matrix-doc/pull/2836)
|
||||
# - msc2946 # (Spaces Summary, see https://github.com/matrix-org/matrix-doc/pull/2946)
|
||||
mscs: [msc2946]
|
||||
database:
|
||||
max_open_conns: 5
|
||||
max_idle_conns: 2
|
||||
conn_max_lifetime: -1
|
||||
|
||||
# Configuration for the Room Server.
|
||||
room_server:
|
||||
internal_api:
|
||||
listen: http://localhost:7770 # Only used in polylith deployments
|
||||
connect: http://localhost:7770 # Only used in polylith deployments
|
||||
database:
|
||||
max_open_conns: 10
|
||||
max_idle_conns: 2
|
||||
conn_max_lifetime: -1
|
||||
|
||||
# Configuration for the Sync API.
|
||||
sync_api:
|
||||
internal_api:
|
||||
listen: http://localhost:7773 # Only used in polylith deployments
|
||||
connect: http://localhost:7773 # Only used in polylith deployments
|
||||
external_api:
|
||||
listen: http://[::]:8073
|
||||
database:
|
||||
max_open_conns: 10
|
||||
max_idle_conns: 2
|
||||
conn_max_lifetime: -1
|
||||
|
||||
# This option controls which HTTP header to inspect to find the real remote IP
|
||||
# address of the client. This is likely required if Dendrite is running behind
|
||||
# a reverse proxy server.
|
||||
# real_ip_header: X-Real-IP
|
||||
|
||||
# Configuration for the User API.
|
||||
user_api:
|
||||
# The cost when hashing passwords on registration/login. Default: 10. Min: 4, Max: 31
|
||||
# See https://pkg.go.dev/golang.org/x/crypto/bcrypt for more information.
|
||||
# Setting this lower makes registration/login consume less CPU resources at the cost of security
|
||||
# should the database be compromised. Setting this higher makes registration/login consume more
|
||||
# CPU resources but makes it harder to brute force password hashes.
|
||||
# This value can be low if performing tests or on embedded Dendrite instances (e.g WASM builds)
|
||||
# bcrypt_cost: 10
|
||||
internal_api:
|
||||
listen: http://localhost:7781 # Only used in polylith deployments
|
||||
connect: http://localhost:7781 # Only used in polylith deployments
|
||||
account_database:
|
||||
max_open_conns: 10
|
||||
max_idle_conns: 2
|
||||
conn_max_lifetime: -1
|
||||
# The length of time that a token issued for a relying party from
|
||||
# /_matrix/client/r0/user/{userId}/openid/request_token endpoint
|
||||
# is considered to be valid in milliseconds.
|
||||
# The default lifetime is 3600000ms (60 minutes).
|
||||
# openid_token_lifetime_ms: 3600000
|
||||
|
||||
# Configuration for Opentracing.
|
||||
# See https://github.com/matrix-org/dendrite/tree/master/docs/tracing for information on
|
||||
# how this works and how to set it up.
|
||||
tracing:
|
||||
enabled: false
|
||||
jaeger:
|
||||
serviceName: ""
|
||||
disabled: false
|
||||
rpc_metrics: false
|
||||
tags: []
|
||||
sampler: null
|
||||
reporter: null
|
||||
headers: null
|
||||
baggage_restrictions: null
|
||||
throttler: null
|
||||
|
||||
# Logging configuration
|
||||
logging:
|
||||
- type: std
|
||||
level: info
|
||||
- type: file
|
||||
# The logging level, must be one of debug, info, warn, error, fatal, panic.
|
||||
level: info
|
||||
params:
|
||||
path: ./logs
|
||||
21
go.mod
21
go.mod
|
|
@ -7,16 +7,19 @@ require (
|
|||
github.com/MFAshby/stdemuxerhook v1.0.0
|
||||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
github.com/blevesearch/bleve/v2 v2.3.4
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1
|
||||
github.com/codeclysm/extract v2.2.0+incompatible
|
||||
github.com/dgraph-io/ristretto v0.1.1-0.20220403145359-8e850b710d6d
|
||||
github.com/docker/docker v20.10.19+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/ethereum/go-ethereum v1.10.25
|
||||
github.com/getsentry/sentry-go v0.14.0
|
||||
github.com/gologme/log v1.3.0
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/joho/godotenv v1.4.0
|
||||
github.com/kardianos/minwinsvc v1.0.2
|
||||
github.com/lib/pq v1.10.7
|
||||
github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e
|
||||
|
|
@ -36,6 +39,7 @@ require (
|
|||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.13.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spruceid/siwe-go v0.2.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/tidwall/gjson v1.14.3
|
||||
github.com/tidwall/sjson v1.2.5
|
||||
|
|
@ -59,6 +63,7 @@ require (
|
|||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/RoaringBitmap/roaring v1.2.1 // indirect
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.3.3 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.0.3 // indirect
|
||||
|
|
@ -76,11 +81,17 @@ require (
|
|||
github.com/blevesearch/zapx/v13 v13.3.5 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.3.5 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.5 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 // indirect
|
||||
github.com/deckarep/golang-set v1.8.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.1 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
||||
|
|
@ -117,9 +128,14 @@ require (
|
|||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/relvacode/iso8601 v1.1.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa // indirect
|
||||
github.com/rjeczalik/notify v0.9.1 // indirect
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.5 // indirect
|
||||
github.com/tklauser/numcpus v0.2.2 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
|
|
@ -129,6 +145,7 @@ require (
|
|||
golang.org/x/tools v0.2.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/macaroon.v2 v2.1.0 // indirect
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
|
|
@ -141,4 +158,4 @@ require (
|
|||
modernc.org/token v1.0.1 // indirect
|
||||
)
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
|
|
|||
70
go.sum
70
go.sum
|
|
@ -53,6 +53,9 @@ github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJ
|
|||
github.com/RoaringBitmap/roaring v0.9.4/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=
|
||||
github.com/RoaringBitmap/roaring v1.2.1 h1:58/LJlg/81wfEHd5L9qsHduznOIhyv4qb1yWcSvVq9A=
|
||||
github.com/RoaringBitmap/roaring v1.2.1/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
|
|
@ -119,7 +122,12 @@ github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2w
|
|||
github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
|
||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
|
||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
|
|
@ -135,12 +143,22 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz
|
|||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
|
||||
github.com/couchbase/moss v0.2.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
|
||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 h1:RAV05c0xOkJ3dZGS0JFybxFKZ2WMLabgx3uXnd7rpGs=
|
||||
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
|
||||
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
|
||||
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
|
||||
github.com/dgraph-io/ristretto v0.1.1-0.20220403145359-8e850b710d6d h1:Wrc3UKTS+cffkOx0xRGFC+ZesNuTfn0ThvEC72N0krk=
|
||||
github.com/dgraph-io/ristretto v0.1.1-0.20220403145359-8e850b710d6d/go.mod h1:RAy2GVV4sTWVlNMavv3xhLsk18rxhfhDnombTe6EF5c=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
|
|
@ -157,14 +175,19 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3
|
|||
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ethereum/go-ethereum v1.10.25 h1:5dFrKJDnYf8L6/5o42abCE6a9yJm9cs4EJVRyYMr55s=
|
||||
github.com/ethereum/go-ethereum v1.10.25/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg=
|
||||
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
|
||||
github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70=
|
||||
github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
|
|
@ -185,6 +208,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
|||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
|
||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
|
|
@ -192,6 +217,7 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
|
|||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
|
|
@ -205,6 +231,8 @@ github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
|||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
|
|
@ -287,15 +315,23 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
|
|||
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
||||
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
|
||||
github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.0.0 h1:pO2K/gKgKaat5LdpAhxhluX2GPQMaI3W5FUz/I/UnWk=
|
||||
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
|
||||
github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v0.0.0-20171115153421-f7279a603ede/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
|
|
@ -354,9 +390,11 @@ github.com/matrix-org/pinecone v0.0.0-20221103125849-37f2e9b9ba37 h1:CQWFrgH9TJO
|
|||
github.com/matrix-org/pinecone v0.0.0-20221103125849-37f2e9b9ba37/go.mod h1:F3GHppRuHCTDeoOmmgjZMeJdbql91+RSGGsATWfC7oc=
|
||||
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4 h1:eCEHXWDv9Rm335MSuB49mFUK44bwZPFSDde3ORE3syk=
|
||||
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4/go.mod h1:vVQlW/emklohkZnOPwD3LrZUBqdfsbiyO3p1lNV8F6U=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
|
|
@ -367,6 +405,8 @@ github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA
|
|||
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||
github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
|
||||
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI=
|
||||
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
|
@ -400,6 +440,7 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S
|
|||
github.com/ngrok/sqlmw v0.0.0-20220520173518-97c9c04efc79 h1:Dmx8g2747UTVPzSkmohk84S3g/uWqd6+f4SSLPhLcfA=
|
||||
github.com/ngrok/sqlmw v0.0.0-20220520173518-97c9c04efc79/go.mod h1:E26fwEtRNigBfFfHDWsklmo0T7Ixbg0XXgck+Hq4O9k=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo/v2 v2.3.0 h1:kUMoxMoQG3ogk/QWyKh3zibV7BKZ+xBpWil1cTylVqc=
|
||||
|
|
@ -452,14 +493,25 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
|||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
|
||||
github.com/relvacode/iso8601 v1.1.0 h1:2nV8sp0eOjpoKQ2vD3xSDygsjAx37NHG2UlZiCkDH4I=
|
||||
github.com/relvacode/iso8601 v1.1.0/go.mod h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa h1:tEkEyxYeZ43TR55QU/hsIt9aRGBxbgGuz9CGykjvogY=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE=
|
||||
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
|
|
@ -474,6 +526,9 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL
|
|||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spruceid/siwe-go v0.2.0 h1:MkBZ/TpPlh1mBhul3h/XLSNZJAbbaHF587Q/VQbhPI0=
|
||||
github.com/spruceid/siwe-go v0.2.0/go.mod h1:rvV+8/z/ryBKqdw9RcexFgtcsrDlESOGR38sPdVWbSI=
|
||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
|
|
@ -489,16 +544,23 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
||||
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4=
|
||||
github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
|
||||
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
|
||||
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
|
||||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4=
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
|
||||
|
|
@ -508,8 +570,10 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM
|
|||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y=
|
||||
github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/yggdrasil-network/yggdrasil-go v0.4.6 h1:GALUDV9QPz/5FVkbazpkTc9EABHufA556JwUJZr41j4=
|
||||
github.com/yggdrasil-network/yggdrasil-go v0.4.6/go.mod h1:PBMoAOvQjA9geNEeGyMXA9QgCS6Bu+9V+1VkWM84wpw=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
|
@ -643,6 +707,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220907140024-f12130a52804 h1:0SH2R3f1b1VmIMG7BXbEZCBUu2dKmHschSmjqGUrW8A=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -685,6 +750,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
@ -865,6 +931,8 @@ gopkg.in/h2non/bimg.v1 v1.1.9/go.mod h1:PgsZL7dLwUbsGm1NYps320GxGgvQNTnecMCZqxV1
|
|||
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
|
||||
gopkg.in/macaroon.v2 v2.1.0 h1:HZcsjBCzq9t0eBPMKqTN/uSN6JOm78ZJ2INbqcBQOUI=
|
||||
gopkg.in/macaroon.v2 v2.1.0/go.mod h1:OUb+TQP/OP0WOerC2Jp/3CwhIKyIa9kQjuc7H24e6/o=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
|||
44
internal/mapsutil/maps.go
Normal file
44
internal/mapsutil/maps.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// 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 mapsutil
|
||||
|
||||
// Union two maps together with "b" overriding the values of "a"
|
||||
// if the keys collide.
|
||||
func MapsUnion(a map[string]interface{}, b map[string]interface{}) map[string]interface{} {
|
||||
c := make(map[string]interface{})
|
||||
for k, v := range a {
|
||||
c[k] = v
|
||||
}
|
||||
for k, v := range b {
|
||||
c[k] = v
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Make a copy of the map
|
||||
func MapCopy(m map[string]interface{}) map[string]interface{} {
|
||||
cp := make(map[string]interface{})
|
||||
for k, v := range m {
|
||||
vm, ok := v.(map[string]interface{})
|
||||
if ok {
|
||||
cp[k] = MapCopy(vm)
|
||||
} else {
|
||||
cp[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return cp
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
|
@ -322,6 +323,7 @@ FederationHit:
|
|||
b.eventIDToBeforeStateIDs[targetEvent.EventID()] = res
|
||||
return res, nil
|
||||
}
|
||||
sentry.CaptureException(lastErr) // temporary to see if we might need to raise the server limit
|
||||
return nil, lastErr
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/internal/mapsutil"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
|
|
@ -290,6 +292,9 @@ func LoadMatrixKey(privateKeyPath string, readFile func(string) ([]byte, error))
|
|||
// Derive generates data that is derived from various values provided in
|
||||
// the config file.
|
||||
func (config *Dendrite) Derive() error {
|
||||
// Replace selected config with env variables.
|
||||
config.replaceWithEnvVariables()
|
||||
|
||||
// Determine registrations flows based off config values
|
||||
|
||||
config.Derived.Registration.Params = make(map[string]interface{})
|
||||
|
|
@ -301,10 +306,20 @@ func (config *Dendrite) Derive() error {
|
|||
config.Derived.Registration.Params[authtypes.LoginTypeRecaptcha] = map[string]string{"public_key": config.ClientAPI.RecaptchaPublicKey}
|
||||
config.Derived.Registration.Flows = append(config.Derived.Registration.Flows,
|
||||
authtypes.Flow{Stages: []authtypes.LoginType{authtypes.LoginTypeRecaptcha}})
|
||||
} else {
|
||||
} else if !config.ClientAPI.PasswordAuthenticationDisabled {
|
||||
config.Derived.Registration.Flows = append(config.Derived.Registration.Flows,
|
||||
authtypes.Flow{Stages: []authtypes.LoginType{authtypes.LoginTypeDummy}})
|
||||
}
|
||||
if config.ClientAPI.PublicKeyAuthentication.Enabled() {
|
||||
pkFlows := config.ClientAPI.PublicKeyAuthentication.GetPublicKeyRegistrationFlows()
|
||||
if pkFlows != nil {
|
||||
config.Derived.Registration.Flows = append(config.Derived.Registration.Flows, pkFlows...)
|
||||
}
|
||||
pkParams := config.ClientAPI.PublicKeyAuthentication.GetPublicKeyRegistrationParams()
|
||||
if pkParams != nil {
|
||||
config.Derived.Registration.Params = mapsutil.MapsUnion(config.Derived.Registration.Params, pkParams)
|
||||
}
|
||||
}
|
||||
|
||||
// Load application service configuration files
|
||||
if err := loadAppServices(&config.AppServiceAPI, &config.Derived); err != nil {
|
||||
|
|
@ -565,6 +580,64 @@ func (config *Dendrite) SetupTracing(serviceName string) (closer io.Closer, err
|
|||
)
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
Replace selected config with environment variables
|
||||
*/
|
||||
|
||||
func (config *Dendrite) replaceWithEnvVariables() {
|
||||
// If env variable is set, get the value from the env
|
||||
// variable and replace it in each supported field.
|
||||
|
||||
err := godotenv.Load(".env")
|
||||
if err != nil {
|
||||
logrus.Errorln("error loading .env file", err)
|
||||
}
|
||||
|
||||
config.Global.ServerName = gomatrixserverlib.ServerName(
|
||||
replaceWithEnvVariables(string(config.Global.ServerName)),
|
||||
)
|
||||
logrus.Infof("Matrix ServerName=%s", config.Global.ServerName)
|
||||
|
||||
config.Global.DatabaseOptions.ConnectionString = DataSource(
|
||||
replaceWithEnvVariables(
|
||||
string(config.Global.DatabaseOptions.ConnectionString),
|
||||
),
|
||||
)
|
||||
|
||||
if config.ClientAPI.PublicKeyAuthentication.Ethereum.Enabled {
|
||||
config.ClientAPI.PublicKeyAuthentication.Ethereum.ConfigChainID =
|
||||
replaceWithEnvVariables(config.ClientAPI.PublicKeyAuthentication.Ethereum.ConfigChainID)
|
||||
|
||||
config.ClientAPI.PublicKeyAuthentication.Ethereum.NetworkUrl =
|
||||
replaceWithEnvVariables(config.ClientAPI.PublicKeyAuthentication.Ethereum.NetworkUrl)
|
||||
|
||||
config.ClientAPI.PublicKeyAuthentication.Ethereum.ConfigEnableAuthz =
|
||||
replaceWithEnvVariables(config.ClientAPI.PublicKeyAuthentication.Ethereum.ConfigEnableAuthz)
|
||||
|
||||
logrus.Infof(
|
||||
"Supported Ethereum chain_id=%v, network_url=%v, enable_authz=%v",
|
||||
config.ClientAPI.PublicKeyAuthentication.Ethereum.ConfigChainID,
|
||||
config.ClientAPI.PublicKeyAuthentication.Ethereum.NetworkUrl,
|
||||
config.ClientAPI.PublicKeyAuthentication.Ethereum.ConfigEnableAuthz,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var regexpEnvVariables = regexp.MustCompile(`\$\{(?P<Var>\w+)\}`)
|
||||
var varIndex = regexpEnvVariables.SubexpIndex("Var")
|
||||
|
||||
func replaceWithEnvVariables(value string) string {
|
||||
matches := regexpEnvVariables.FindAllStringSubmatch(value, -1)
|
||||
for _, m := range matches {
|
||||
if varIndex < len(m) {
|
||||
envValue := os.Getenv(m[varIndex])
|
||||
value = strings.ReplaceAll(value, fmt.Sprintf("${%s}", m[varIndex]), envValue)
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// logrusLogger is a small wrapper that implements jaeger.Logger using logrus.
|
||||
type logrusLogger struct {
|
||||
l *logrus.Logger
|
||||
|
|
|
|||
|
|
@ -55,6 +55,12 @@ type ClientAPI struct {
|
|||
RateLimiting RateLimiting `yaml:"rate_limiting"`
|
||||
|
||||
MSCs *MSCs `yaml:"-"`
|
||||
|
||||
// Disable password authentication.
|
||||
PasswordAuthenticationDisabled bool `yaml:"password_authentication_disabled"`
|
||||
|
||||
// Public key authentication
|
||||
PublicKeyAuthentication PublicKeyAuthentication `yaml:"public_key_authentication"`
|
||||
}
|
||||
|
||||
func (c *ClientAPI) Defaults(opts DefaultOpts) {
|
||||
|
|
|
|||
87
setup/config/config_publickey.go
Normal file
87
setup/config/config_publickey.go
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
)
|
||||
|
||||
type AuthParams interface {
|
||||
GetParams() interface{}
|
||||
}
|
||||
|
||||
type EthereumAuthParams struct {
|
||||
Version uint `json:"version"`
|
||||
ChainID int `json:"chain_id"`
|
||||
}
|
||||
|
||||
func (p EthereumAuthParams) GetParams() interface{} {
|
||||
return p
|
||||
}
|
||||
|
||||
type EthereumAuthConfig struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Version uint `yaml:"version"`
|
||||
NetworkUrl string `yaml:"network_url"` // Blockchain network provider URL
|
||||
ConfigChainID string `yaml:"chain_id"` // Blockchain chain ID. Env variable can replace this property.
|
||||
ConfigEnableAuthz string `yaml:"enable_authz"` // Enable / disable authorization during development. Will be removed when feature is done.
|
||||
chainID int
|
||||
enableAuthz bool
|
||||
}
|
||||
|
||||
func (c *EthereumAuthConfig) GetChainID() int {
|
||||
if c.ConfigChainID != "" {
|
||||
v := strings.TrimSpace(c.ConfigChainID)
|
||||
id, err := strconv.Atoi(v)
|
||||
if err == nil {
|
||||
c.chainID = id
|
||||
}
|
||||
// No need to do this again.
|
||||
c.ConfigChainID = ""
|
||||
}
|
||||
return c.chainID
|
||||
}
|
||||
|
||||
func (c *EthereumAuthConfig) GetEnableAuthZ() bool {
|
||||
if c.ConfigEnableAuthz != "" {
|
||||
v := strings.TrimSpace(c.ConfigEnableAuthz)
|
||||
boolValue, err := strconv.ParseBool(v)
|
||||
if err == nil {
|
||||
c.enableAuthz = boolValue
|
||||
}
|
||||
// No need to do this again.
|
||||
c.ConfigEnableAuthz = ""
|
||||
}
|
||||
return c.enableAuthz
|
||||
}
|
||||
|
||||
type PublicKeyAuthentication struct {
|
||||
Ethereum EthereumAuthConfig `yaml:"ethereum"`
|
||||
}
|
||||
|
||||
func (pk *PublicKeyAuthentication) Enabled() bool {
|
||||
return pk.Ethereum.Enabled
|
||||
}
|
||||
|
||||
func (pk *PublicKeyAuthentication) GetPublicKeyRegistrationFlows() []authtypes.Flow {
|
||||
var flows []authtypes.Flow
|
||||
if pk.Ethereum.Enabled {
|
||||
flows = append(flows, authtypes.Flow{Stages: []authtypes.LoginType{authtypes.LoginTypePublicKeyEthereum}})
|
||||
}
|
||||
|
||||
return flows
|
||||
}
|
||||
|
||||
func (pk *PublicKeyAuthentication) GetPublicKeyRegistrationParams() map[string]interface{} {
|
||||
params := make(map[string]interface{})
|
||||
if pk.Ethereum.Enabled {
|
||||
p := EthereumAuthParams{
|
||||
Version: pk.Ethereum.Version,
|
||||
ChainID: pk.Ethereum.GetChainID(),
|
||||
}
|
||||
params[authtypes.LoginTypePublicKeyEthereum] = p
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
|
@ -29,7 +29,11 @@ import (
|
|||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/syncapi/storage"
|
||||
"github.com/matrix-org/dendrite/syncapi/sync"
|
||||
|
||||
authz "github.com/matrix-org/dendrite/authorization"
|
||||
clientApiAuthz "github.com/matrix-org/dendrite/clientapi/authorization"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
zion "github.com/matrix-org/dendrite/zion"
|
||||
)
|
||||
|
||||
// Setup configures the given mux with sync-server listeners
|
||||
|
|
@ -42,9 +46,12 @@ func Setup(
|
|||
userAPI userapi.SyncUserAPI,
|
||||
rsAPI api.SyncRoomserverAPI,
|
||||
cfg *config.SyncAPI,
|
||||
clientCfg *config.ClientAPI,
|
||||
lazyLoadCache caching.LazyLoadCache,
|
||||
fts *fulltext.Search,
|
||||
) {
|
||||
syncAuthz := zion.SyncRoomserverStruct{SyncRoomserverAPI: rsAPI}
|
||||
authorization := clientApiAuthz.NewRoomserverAuthorization(clientCfg, syncAuthz)
|
||||
v1unstablemux := csMux.PathPrefix("/{apiversion:(?:v1|unstable)}/").Subrouter()
|
||||
v3mux := csMux.PathPrefix("/{apiversion:(?:r0|v3)}/").Subrouter()
|
||||
|
||||
|
|
@ -55,6 +62,19 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/rooms/{roomID}/messages", httputil.MakeAuthAPI("room_messages", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
||||
isAllowed, _ := authorization.IsAllowed(authz.AuthorizationArgs{
|
||||
RoomId: vars["roomID"],
|
||||
UserId: device.UserID,
|
||||
Permission: authz.PermissionRead,
|
||||
})
|
||||
|
||||
if !isAllowed {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.Forbidden("Unauthorised"),
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,9 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
|
||||
"github.com/matrix-org/dendrite/syncapi/storage"
|
||||
"github.com/matrix-org/dendrite/syncapi/types"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
type InviteStreamProvider struct {
|
||||
|
|
|
|||
|
|
@ -62,6 +62,14 @@ func (p *NotificationDataStreamProvider) IncrementalSync(
|
|||
}
|
||||
req.Response.Rooms.Join[roomID] = jr
|
||||
}
|
||||
// BEGIN ZION CODE but return all notifications regardless of whether they're in a room we're in.
|
||||
for roomID, counts := range countsByRoom {
|
||||
unreadNotificationsData := *types.NewUnreadNotificationsResponse()
|
||||
|
||||
unreadNotificationsData.HighlightCount = counts.UnreadHighlightCount
|
||||
unreadNotificationsData.NotificationCount = counts.UnreadNotificationCount
|
||||
req.Response.Rooms.UnreadNotifications[roomID] = unreadNotificationsData
|
||||
}
|
||||
// END ZION CODE
|
||||
return p.LatestPosition(ctx)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ func AddPublicRoutes(
|
|||
keyAPI keyapi.SyncKeyAPI,
|
||||
) {
|
||||
cfg := &base.Cfg.SyncAPI
|
||||
clientCfg := &base.Cfg.ClientAPI
|
||||
|
||||
js, natsClient := base.NATS.Prepare(base.ProcessContext, &cfg.Matrix.JetStream)
|
||||
|
||||
|
|
@ -132,6 +133,6 @@ func AddPublicRoutes(
|
|||
|
||||
routing.Setup(
|
||||
base.PublicClientAPIMux, requestPool, syncDB, userAPI,
|
||||
rsAPI, cfg, base.Caches, base.Fulltext,
|
||||
rsAPI, cfg, clientCfg, base.Caches, base.Fulltext,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -348,6 +348,7 @@ type RoomsResponse struct {
|
|||
Peek map[string]*JoinResponse `json:"peek,omitempty"`
|
||||
Invite map[string]*InviteResponse `json:"invite,omitempty"`
|
||||
Leave map[string]*LeaveResponse `json:"leave,omitempty"`
|
||||
UnreadNotifications map[string]UnreadNotificationsResponse `json:"unread_notifications,omitempty"`
|
||||
}
|
||||
|
||||
type ToDeviceResponse struct {
|
||||
|
|
@ -399,6 +400,7 @@ func (r *Response) HasUpdates() bool {
|
|||
len(r.Rooms.Join) > 0 ||
|
||||
len(r.Rooms.Leave) > 0 ||
|
||||
len(r.Rooms.Peek) > 0 ||
|
||||
len(r.Rooms.UnreadNotifications) > 0 ||
|
||||
len(r.ToDevice.Events) > 0 ||
|
||||
len(r.DeviceLists.Changed) > 0 ||
|
||||
len(r.DeviceLists.Left) > 0)
|
||||
|
|
@ -408,12 +410,14 @@ func (r *Response) HasUpdates() bool {
|
|||
func NewResponse() *Response {
|
||||
res := Response{}
|
||||
// Pre-initialise the maps. Synapse will return {} even if there are no rooms under a specific section,
|
||||
// so let's do the same thing. Bonus: this means we can't get dreaded 'assignment to entry in nil map' errors.
|
||||
// so let's do the same thing. Bonus this means we can't get dreaded 'assignment to entry in nil map' errors.
|
||||
|
||||
res.Rooms = &RoomsResponse{
|
||||
Join: map[string]*JoinResponse{},
|
||||
Peek: map[string]*JoinResponse{},
|
||||
Invite: map[string]*InviteResponse{},
|
||||
Leave: map[string]*LeaveResponse{},
|
||||
UnreadNotifications: map[string]UnreadNotificationsResponse{},
|
||||
}
|
||||
|
||||
// Also pre-intialise empty slices or else we'll insert 'null' instead of '[]' for the value.
|
||||
|
|
@ -435,11 +439,22 @@ func (r *Response) IsEmpty() bool {
|
|||
return len(r.Rooms.Join) == 0 &&
|
||||
len(r.Rooms.Invite) == 0 &&
|
||||
len(r.Rooms.Leave) == 0 &&
|
||||
len(r.Rooms.UnreadNotifications) == 0 &&
|
||||
len(r.AccountData.Events) == 0 &&
|
||||
len(r.Presence.Events) == 0 &&
|
||||
len(r.ToDevice.Events) == 0
|
||||
}
|
||||
|
||||
type UnreadNotificationsResponse struct {
|
||||
HighlightCount int `json:"highlight_count"`
|
||||
NotificationCount int `json:"notification_count"`
|
||||
}
|
||||
|
||||
func NewUnreadNotificationsResponse() *UnreadNotificationsResponse {
|
||||
res := UnreadNotificationsResponse{}
|
||||
return &res
|
||||
}
|
||||
|
||||
type UnreadNotifications struct {
|
||||
HighlightCount int `json:"highlight_count"`
|
||||
NotificationCount int `json:"notification_count"`
|
||||
|
|
|
|||
|
|
@ -46,3 +46,8 @@ If a device list update goes missing, the server resyncs on the next one
|
|||
# Might be a bug in the test because leaves do appear :-(
|
||||
|
||||
Leaves are present in non-gapped incremental syncs
|
||||
|
||||
# Fails due to fix / workaround for this issue https://github.com/matrix-org/dendrite/issues/2842
|
||||
|
||||
Newly joined room is included in an incremental sync
|
||||
Newly joined room is included in an incremental sync after invite
|
||||
|
|
@ -106,7 +106,6 @@ Can download filter
|
|||
Lazy loading parameters in the filter are strictly boolean
|
||||
Can sync
|
||||
Can sync a joined room
|
||||
Newly joined room is included in an incremental sync
|
||||
User is offline if they set_presence=offline in their sync
|
||||
Changes to state are included in an incremental sync
|
||||
A change to displayname should appear in incremental /sync
|
||||
|
|
@ -224,7 +223,6 @@ Can't deactivate account with wrong password
|
|||
After deactivating account, can't log in with password
|
||||
After deactivating account, can't log in with an email
|
||||
Remote room alias queries can handle Unicode
|
||||
Newly joined room is included in an incremental sync after invite
|
||||
Inbound /v1/make_join rejects remote attempts to join local users to rooms
|
||||
Local room members see posted message events
|
||||
Fetching eventstream a second time doesn't yield the message again
|
||||
|
|
|
|||
110
test/publickey_utils.go
Normal file
110
test/publickey_utils.go
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// 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 test
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
// This is to silence the conflict about which chainhash is used
|
||||
_ "github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/spruceid/siwe-go"
|
||||
)
|
||||
|
||||
const EthereumTestNetworkId int = 31337 // Localhost chain ID
|
||||
const TestServerName = "localhost"
|
||||
|
||||
type EthereumTestWallet struct {
|
||||
Eip155UserId string
|
||||
PublicAddress string
|
||||
PrivateKey *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
// https://goethereumbook.org/wallet-generate/
|
||||
func CreateTestAccount() (*EthereumTestWallet, error) {
|
||||
// Create a new public / private key pair.
|
||||
privateKey, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the public key
|
||||
publicKey := privateKey.Public()
|
||||
|
||||
// Transform public key to the Ethereum address
|
||||
publicKeyEcdsa, ok := publicKey.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, errors.New("error casting public key to ECDSA")
|
||||
}
|
||||
|
||||
address := crypto.PubkeyToAddress(*publicKeyEcdsa).Hex()
|
||||
eip155UserId := fmt.Sprintf("eip155=3a%d=3a%s", EthereumTestNetworkId, address)
|
||||
|
||||
return &EthereumTestWallet{
|
||||
PublicAddress: address,
|
||||
PrivateKey: privateKey,
|
||||
Eip155UserId: eip155UserId,
|
||||
},
|
||||
nil
|
||||
}
|
||||
|
||||
func CreateEip4361TestMessage(
|
||||
publicAddress string,
|
||||
) (*siwe.Message, error) {
|
||||
options := make(map[string]interface{})
|
||||
options["chainId"] = EthereumTestNetworkId
|
||||
options["statement"] = "This is a test statement"
|
||||
message, err := siwe.InitMessage(
|
||||
TestServerName,
|
||||
publicAddress,
|
||||
"https://localhost/login",
|
||||
siwe.GenerateNonce(),
|
||||
options,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
|
||||
func FromEip4361MessageToString(message *siwe.Message) string {
|
||||
// Escape the formatting characters to
|
||||
// prevent unmarshal exceptions.
|
||||
str := strings.ReplaceAll(message.String(), "\n", "\\n")
|
||||
str = strings.ReplaceAll(str, "\t", "\\t")
|
||||
return str
|
||||
}
|
||||
|
||||
// https://goethereumbook.org/signature-generate/
|
||||
func SignMessage(message string, privateKey *ecdsa.PrivateKey) (string, error) {
|
||||
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(message), message)
|
||||
data := []byte(msg)
|
||||
hash := crypto.Keccak256Hash(data)
|
||||
|
||||
signature, err := crypto.Sign(hash.Bytes(), privateKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// https://github.com/ethereum/go-ethereum/blob/55599ee95d4151a2502465e0afc7c47bd1acba77/internal/ethapi/api.go#L442
|
||||
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
|
||||
return hexutil.Encode(signature), nil
|
||||
}
|
||||
5
zion/README.md
Normal file
5
zion/README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Purpose
|
||||
|
||||
Additional packages added for the Zion project, nothing in here should be in the Matrix Dendrite upstream, nor in the herenotthere/dendrite-fork.
|
||||
|
||||
The zion_space_manager_(mainnet|rinkeby|localhost).go files are generated as new versions of the smart contracts are build and deployed. The bindings are in this location so they can be built alongside the dendrite server in the build process.
|
||||
22
zion/contract_addresses.go
Normal file
22
zion/contract_addresses.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package zion
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type SpaceManagerContractAddresses struct {
|
||||
Spacemanager string `json:"spaceManager"`
|
||||
Usergranted string `json:"usergranted"`
|
||||
Tokengranted string `json:"tokengranted"`
|
||||
}
|
||||
|
||||
func loadSpaceManagerAddresses(byteValue []byte) (*SpaceManagerContractAddresses, error) {
|
||||
var addresses SpaceManagerContractAddresses
|
||||
|
||||
err := json.Unmarshal(byteValue, &addresses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &addresses, nil
|
||||
}
|
||||
1
zion/contracts/goerli/addresses/council.json
Normal file
1
zion/contracts/goerli/addresses/council.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"councilnft": "0x4bc01028a7f1a56e267ecabd735567cdb1fc7744"}
|
||||
1
zion/contracts/goerli/addresses/space-manager.json
Normal file
1
zion/contracts/goerli/addresses/space-manager.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"spacemanager": "0xb194C2E006aEeC94BC8bAa39B8578992134deF80","usergranted": "0x68dc242a7E1Da4d72D5eCE53e055D1E6e44Bb288","tokengranted": "0x9B424DAd142087168ECB8C3809d4B7187d4110ce", "spacenft": "0x17Bb2187E5Dd062Da0dbA3A097943343cABfCD8E", "permission": "0x07e71c115aE5F2929E13940148015284242c3f07", "rolemanager": "0xf941F1f08E0EA9A747E7Eef53C57dc13425aA8af"}
|
||||
1156
zion/contracts/goerli/zion_goerli/zion_space_manager_goerli.go
Normal file
1156
zion/contracts/goerli/zion_goerli/zion_space_manager_goerli.go
Normal file
File diff suppressed because one or more lines are too long
1
zion/contracts/localhost/addresses/council.json
Normal file
1
zion/contracts/localhost/addresses/council.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"councilnft": "0x9a676e781a523b5d0c0e43731313a708cb607508"}
|
||||
1
zion/contracts/localhost/addresses/space-manager.json
Normal file
1
zion/contracts/localhost/addresses/space-manager.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"spacemanager": "0xdc64a140aa3e981100a9beca4e685f962f0cf6c9","usergranted": "0x0165878a594ca255338adfa4d48449f69242eb8f","tokengranted": "0xa513e6e4b8f2a923d98304ec87f64353c4d5c853"}
|
||||
File diff suppressed because one or more lines are too long
172
zion/store.go
Normal file
172
zion/store.go
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
Convenient function for space info mapping between Matrix room and Space contract
|
||||
*/
|
||||
package zion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
roomserver "github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
type ClientRoomserverStore struct {
|
||||
rsAPI roomserver.ClientRoomserverAPI
|
||||
}
|
||||
|
||||
type SyncRoomserverStore struct {
|
||||
rsAPI roomserver.SyncRoomserverAPI
|
||||
}
|
||||
|
||||
type StoreAPI interface {
|
||||
GetRoomInfo(roomId string, userId UserIdentifier) RoomInfo
|
||||
}
|
||||
|
||||
func (s *ClientRoomserverStore) GetRoomInfo(roomId string, userId UserIdentifier) RoomInfo {
|
||||
result := RoomInfo{
|
||||
QueryUserId: userId.MatrixUserId,
|
||||
SpaceNetworkId: "",
|
||||
ChannelNetworkId: "",
|
||||
RoomType: Unknown,
|
||||
IsOwner: false,
|
||||
}
|
||||
|
||||
createTuple := gomatrixserverlib.StateKeyTuple{
|
||||
EventType: gomatrixserverlib.MRoomCreate,
|
||||
StateKey: "",
|
||||
}
|
||||
|
||||
spaceChildTuple := gomatrixserverlib.StateKeyTuple{
|
||||
EventType: ConstSpaceChildEventType,
|
||||
StateKey: "*",
|
||||
}
|
||||
|
||||
spaceParentTuple := gomatrixserverlib.StateKeyTuple{
|
||||
EventType: ConstSpaceParentEventType,
|
||||
StateKey: "*",
|
||||
}
|
||||
|
||||
var roomEvents roomserver.QueryCurrentStateResponse
|
||||
err := s.rsAPI.QueryCurrentState(context.Background(), &roomserver.QueryCurrentStateRequest{
|
||||
RoomID: roomId,
|
||||
AllowWildcards: true,
|
||||
StateTuples: []gomatrixserverlib.StateKeyTuple{
|
||||
createTuple,
|
||||
spaceParentTuple,
|
||||
spaceChildTuple,
|
||||
},
|
||||
}, &roomEvents)
|
||||
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
//TODO: replace with HydrateRoomInfoWithStateEvents when you have a practical way to flatten roomEvents map
|
||||
for _, state := range roomEvents.StateEvents {
|
||||
switch state.Type() {
|
||||
case gomatrixserverlib.MRoomCreate:
|
||||
var creatorEvent CreatorEvent
|
||||
err := json.Unmarshal(roomEvents.StateEvents[createTuple].Content(), &creatorEvent)
|
||||
result.IsOwner = strings.HasPrefix(
|
||||
creatorEvent.Creator,
|
||||
userId.LocalPart,
|
||||
)
|
||||
if err == nil {
|
||||
result.RoomType = Space
|
||||
result.SpaceNetworkId = roomId
|
||||
}
|
||||
case ConstSpaceChildEventType:
|
||||
result.RoomType = Space
|
||||
result.SpaceNetworkId = roomId
|
||||
case ConstSpaceParentEventType:
|
||||
result.RoomType = Channel
|
||||
result.SpaceNetworkId = *state.StateKey()
|
||||
result.ChannelNetworkId = roomId
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SyncRoomserverStore) GetRoomInfo(roomId string, userId UserIdentifier) RoomInfo {
|
||||
result := RoomInfo{
|
||||
QueryUserId: userId.MatrixUserId,
|
||||
SpaceNetworkId: "",
|
||||
ChannelNetworkId: "",
|
||||
RoomType: Unknown,
|
||||
IsOwner: false,
|
||||
}
|
||||
|
||||
createTuple := gomatrixserverlib.StateKeyTuple{
|
||||
EventType: gomatrixserverlib.MRoomCreate,
|
||||
StateKey: "",
|
||||
}
|
||||
|
||||
spaceChildTuple := gomatrixserverlib.StateKeyTuple{
|
||||
EventType: ConstSpaceChildEventType,
|
||||
StateKey: "*",
|
||||
}
|
||||
|
||||
spaceParentTuple := gomatrixserverlib.StateKeyTuple{
|
||||
EventType: ConstSpaceParentEventType,
|
||||
StateKey: "*",
|
||||
}
|
||||
|
||||
var roomEvents roomserver.QueryLatestEventsAndStateResponse
|
||||
err := s.rsAPI.QueryLatestEventsAndState(context.Background(),
|
||||
&roomserver.QueryLatestEventsAndStateRequest{
|
||||
RoomID: roomId,
|
||||
StateToFetch: []gomatrixserverlib.StateKeyTuple{
|
||||
createTuple,
|
||||
spaceParentTuple,
|
||||
spaceChildTuple,
|
||||
},
|
||||
}, &roomEvents)
|
||||
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
|
||||
HydrateRoomInfoWithStateEvents(roomId, userId, &result, roomEvents.StateEvents)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func HydrateRoomInfoWithStateEvents(roomId string, userId UserIdentifier, r *RoomInfo, stateEvents []*gomatrixserverlib.HeaderedEvent) {
|
||||
for _, state := range stateEvents {
|
||||
switch state.Type() {
|
||||
case gomatrixserverlib.MRoomCreate:
|
||||
var creatorEvent CreatorEvent
|
||||
err := json.Unmarshal(state.Content(), &creatorEvent)
|
||||
r.IsOwner = strings.HasPrefix(
|
||||
creatorEvent.Creator,
|
||||
userId.LocalPart,
|
||||
)
|
||||
if err == nil {
|
||||
r.RoomType = Space
|
||||
r.SpaceNetworkId = roomId
|
||||
}
|
||||
case ConstSpaceChildEventType:
|
||||
r.RoomType = Space
|
||||
r.SpaceNetworkId = roomId
|
||||
case ConstSpaceParentEventType:
|
||||
r.RoomType = Channel
|
||||
r.SpaceNetworkId = *state.StateKey()
|
||||
r.ChannelNetworkId = roomId
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func NewClientRoomserverStore(rsAPI roomserver.ClientRoomserverAPI) StoreAPI {
|
||||
return &ClientRoomserverStore{
|
||||
rsAPI: rsAPI,
|
||||
}
|
||||
}
|
||||
|
||||
func NewSyncRoomserverStore(rsAPI roomserver.SyncRoomserverAPI) StoreAPI {
|
||||
return &SyncRoomserverStore{
|
||||
rsAPI: rsAPI,
|
||||
}
|
||||
}
|
||||
42
zion/store_types.go
Normal file
42
zion/store_types.go
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
Store types
|
||||
*/
|
||||
package zion
|
||||
|
||||
const (
|
||||
ConstSpaceChildEventType = "m.space.child"
|
||||
ConstSpaceParentEventType = "m.space.parent"
|
||||
)
|
||||
|
||||
// Define enum for RoomType
|
||||
type RoomType int64
|
||||
|
||||
const (
|
||||
Space RoomType = iota
|
||||
Channel
|
||||
Unknown
|
||||
)
|
||||
|
||||
func (r RoomType) String() string {
|
||||
switch r {
|
||||
case Space:
|
||||
return "space"
|
||||
case Channel:
|
||||
return "channel"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
type RoomInfo struct {
|
||||
QueryUserId string // Matrix User ID
|
||||
SpaceNetworkId string
|
||||
ChannelNetworkId string
|
||||
RoomType RoomType
|
||||
IsOwner bool
|
||||
}
|
||||
|
||||
type CreatorEvent struct {
|
||||
Creator string `json:"creator"`
|
||||
Type string `json:"type"`
|
||||
RoomVersion string `json:"room_version"`
|
||||
}
|
||||
45
zion/user_identifier.go
Normal file
45
zion/user_identifier.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package zion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var regexpMatrixId = regexp.MustCompile(`^@eip155=3a(?P<ChainId>[0-9]+)=3a(?P<Address>0x[0-9a-fA-F]+):(?P<HomeServer>.*)$`)
|
||||
var chainIdIndex = regexpMatrixId.SubexpIndex("ChainId")
|
||||
var addressIndex = regexpMatrixId.SubexpIndex("Address")
|
||||
|
||||
//var homeServerIndex = regexpMatrixId.SubexpIndex("HomeServer")
|
||||
|
||||
type UserIdentifier struct {
|
||||
AccountAddress common.Address
|
||||
ChainId int
|
||||
MatrixUserId string
|
||||
LocalPart string
|
||||
}
|
||||
|
||||
func CreateUserIdentifier(matrixUserId string) UserIdentifier {
|
||||
matches := regexpMatrixId.FindStringSubmatch(matrixUserId)
|
||||
address := ""
|
||||
chainId := -1
|
||||
localPart := ""
|
||||
|
||||
if chainIdIndex < len(matches) {
|
||||
chainId, _ = strconv.Atoi(matches[chainIdIndex])
|
||||
}
|
||||
|
||||
if addressIndex < len(matches) {
|
||||
address = matches[addressIndex]
|
||||
localPart = fmt.Sprintf("@eip155=3a%d=3a%s", chainId, address)
|
||||
}
|
||||
|
||||
return UserIdentifier{
|
||||
AccountAddress: common.HexToAddress(address),
|
||||
ChainId: chainId,
|
||||
MatrixUserId: matrixUserId,
|
||||
LocalPart: localPart,
|
||||
}
|
||||
}
|
||||
14
zion/web3_util.go
Normal file
14
zion/web3_util.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package zion
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
)
|
||||
|
||||
func GetEthClient(networkUrl string) (*ethclient.Client, error) {
|
||||
client, err := ethclient.Dial(networkUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
3
zion/zion.go
Normal file
3
zion/zion.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package zion
|
||||
|
||||
import _ "github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
204
zion/zion_authorization.go
Normal file
204
zion/zion_authorization.go
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
package zion
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/matrix-org/dendrite/authorization"
|
||||
roomserver "github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
zion_goerli "github.com/matrix-org/dendrite/zion/contracts/goerli/zion_goerli"
|
||||
zion_localhost "github.com/matrix-org/dendrite/zion/contracts/localhost/zion_localhost"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
//go:embed contracts/localhost/addresses/space-manager.json
|
||||
var localhostJson []byte
|
||||
|
||||
//go:embed contracts/goerli/addresses/space-manager.json
|
||||
var goerliJson []byte
|
||||
|
||||
type ZionAuthorization struct {
|
||||
store StoreAPI
|
||||
spaceManagerLocalhost *zion_localhost.ZionSpaceManagerLocalhost
|
||||
spaceManagerGoerli *zion_goerli.ZionSpaceManagerGoerli
|
||||
chainId int
|
||||
}
|
||||
type ClientRoomserverStruct struct {
|
||||
roomserver.ClientRoomserverAPI
|
||||
}
|
||||
|
||||
type SyncRoomserverStruct struct {
|
||||
roomserver.SyncRoomserverAPI
|
||||
}
|
||||
|
||||
type RoomserverStoreAPI interface {
|
||||
roomserver.QueryLatestEventsAndStateAPI
|
||||
NewRoomserverStore() StoreAPI
|
||||
}
|
||||
|
||||
func (c ClientRoomserverStruct) NewRoomserverStore() StoreAPI {
|
||||
return &ClientRoomserverStore{
|
||||
rsAPI: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (c SyncRoomserverStruct) NewRoomserverStore() StoreAPI {
|
||||
return &SyncRoomserverStore{
|
||||
rsAPI: c,
|
||||
}
|
||||
}
|
||||
|
||||
func NewZionAuthorization(cfg *config.ClientAPI, rsAPI RoomserverStoreAPI) (authorization.Authorization, error) {
|
||||
if cfg.PublicKeyAuthentication.Ethereum.NetworkUrl == "" {
|
||||
log.Errorf("No blockchain network url specified in config\n")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var auth ZionAuthorization
|
||||
|
||||
auth.chainId = cfg.PublicKeyAuthentication.Ethereum.GetChainID()
|
||||
auth.store = rsAPI.NewRoomserverStore()
|
||||
|
||||
switch auth.chainId {
|
||||
case 1337, 31337:
|
||||
localhost, err := newZionSpaceManagerLocalhost(cfg.PublicKeyAuthentication.Ethereum.NetworkUrl)
|
||||
if err != nil {
|
||||
log.Errorln("error instantiating ZionSpaceManagerLocalhost", err)
|
||||
}
|
||||
auth.spaceManagerLocalhost = localhost
|
||||
|
||||
case 5:
|
||||
goerli, err := newZionSpaceManagerGoerli(cfg.PublicKeyAuthentication.Ethereum.NetworkUrl)
|
||||
if err != nil {
|
||||
log.Errorln("error instantiating ZionSpaceManagerGoerli", err)
|
||||
}
|
||||
auth.spaceManagerGoerli = goerli
|
||||
|
||||
default:
|
||||
log.Errorf("Unsupported chain id: %d\n", auth.chainId)
|
||||
}
|
||||
|
||||
return &auth, nil
|
||||
}
|
||||
|
||||
func (za *ZionAuthorization) IsAllowed(args authorization.AuthorizationArgs) (bool, error) {
|
||||
userIdentifier := CreateUserIdentifier(args.UserId)
|
||||
|
||||
// Find out if roomId is a space or a channel.
|
||||
roomInfo := za.store.GetRoomInfo(args.RoomId, userIdentifier)
|
||||
|
||||
// Owner of the space / channel is always allowed to proceed.
|
||||
if roomInfo.IsOwner {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
switch za.chainId {
|
||||
case 1337, 31337:
|
||||
return za.isAllowedLocalhost(roomInfo, userIdentifier.AccountAddress, args.Permission)
|
||||
case 5:
|
||||
return za.isAllowedGoerli(roomInfo, userIdentifier.AccountAddress, args.Permission)
|
||||
default:
|
||||
log.Errorf("Unsupported chain id: %d", userIdentifier.ChainId)
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (za *ZionAuthorization) isAllowedLocalhost(
|
||||
roomInfo RoomInfo,
|
||||
user common.Address,
|
||||
permission authorization.Permission,
|
||||
) (bool, error) {
|
||||
if za.spaceManagerLocalhost != nil {
|
||||
permission := zion_localhost.DataTypesPermission{
|
||||
Name: permission.String(),
|
||||
}
|
||||
|
||||
isEntitled, err := za.spaceManagerLocalhost.IsEntitled(
|
||||
nil,
|
||||
roomInfo.SpaceNetworkId,
|
||||
roomInfo.ChannelNetworkId,
|
||||
user,
|
||||
permission,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return isEntitled, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (za *ZionAuthorization) isAllowedGoerli(
|
||||
roomInfo RoomInfo,
|
||||
user common.Address,
|
||||
permission authorization.Permission,
|
||||
) (bool, error) {
|
||||
if za.spaceManagerGoerli != nil {
|
||||
permission := zion_goerli.DataTypesPermission{
|
||||
Name: permission.String(),
|
||||
}
|
||||
|
||||
isEntitled, err := za.spaceManagerGoerli.IsEntitled(
|
||||
nil,
|
||||
roomInfo.SpaceNetworkId,
|
||||
roomInfo.ChannelNetworkId,
|
||||
user,
|
||||
permission,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return isEntitled, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func newZionSpaceManagerLocalhost(endpointUrl string) (*zion_localhost.ZionSpaceManagerLocalhost, error) {
|
||||
addresses, err := loadSpaceManagerAddresses(localhostJson)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
address := common.HexToAddress(addresses.Spacemanager)
|
||||
|
||||
client, err := GetEthClient(endpointUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spaceManager, err := zion_localhost.NewZionSpaceManagerLocalhost(address, client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return spaceManager, nil
|
||||
}
|
||||
|
||||
func newZionSpaceManagerGoerli(endpointUrl string) (*zion_goerli.ZionSpaceManagerGoerli, error) {
|
||||
addresses, err := loadSpaceManagerAddresses(goerliJson)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
address := common.HexToAddress((addresses.Spacemanager))
|
||||
|
||||
client, err := GetEthClient(endpointUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spaceManager, err := zion_goerli.NewZionSpaceManagerGoerli(address, client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return spaceManager, nil
|
||||
}
|
||||
Loading…
Reference in a new issue