mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-28 09:13:09 -06:00
Pass custom certificates via config file to be added to system pool, fix CreateSession to be conformant to V1 api
This commit is contained in:
parent
01ce9abc5b
commit
d975de3c05
|
|
@ -19,7 +19,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"crypto/tls"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -40,6 +39,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/threepid"
|
||||||
"github.com/matrix-org/dendrite/clientapi/userutil"
|
"github.com/matrix-org/dendrite/clientapi/userutil"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/matrix-org/dendrite/userapi/storage/accounts"
|
"github.com/matrix-org/dendrite/userapi/storage/accounts"
|
||||||
|
|
@ -161,33 +161,10 @@ type authDict struct {
|
||||||
// Recaptcha
|
// Recaptcha
|
||||||
Response string `json:"response"`
|
Response string `json:"response"`
|
||||||
// m.login.email.identity and m.login.msisdn
|
// m.login.email.identity and m.login.msisdn
|
||||||
ThreePidCreds *threepidCreds `json:"threepidCreds"`
|
ThreePidCreds *threepid.Credentials `json:"threepidCreds"`
|
||||||
// TODO: Lots of custom keys depending on the type
|
// TODO: Lots of custom keys depending on the type
|
||||||
}
|
}
|
||||||
|
|
||||||
type threepidCreds struct {
|
|
||||||
Sid string `json:"sid"`
|
|
||||||
ClientSecret string `json:"client_secret"`
|
|
||||||
IdServer string `json:"id_server"`
|
|
||||||
IdAccessToken string `json:"id_access_token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *threepidCreds) validate() *jsonerror.MatrixError {
|
|
||||||
if c.Sid == "" {
|
|
||||||
return jsonerror.BadJSON("sid field in threepidCreds is required")
|
|
||||||
}
|
|
||||||
if c.ClientSecret == "" {
|
|
||||||
return jsonerror.BadJSON("client_secret in threepidCreds is required")
|
|
||||||
}
|
|
||||||
if c.IdServer == "" {
|
|
||||||
return jsonerror.BadJSON("id_server in threepidCreds is required")
|
|
||||||
}
|
|
||||||
if c.IdAccessToken == "" {
|
|
||||||
return jsonerror.BadJSON("id_access_token in threepidCreds is required")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#user-interactive-authentication-api
|
// 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"`
|
Flows []authtypes.Flow `json:"flows"`
|
||||||
|
|
@ -352,15 +329,19 @@ func validateRecaptcha(
|
||||||
|
|
||||||
func validateEmailIdentity(
|
func validateEmailIdentity(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
cred *threepidCreds,
|
cred *threepid.Credentials,
|
||||||
|
cfg *config.ClientAPI,
|
||||||
) *util.JSONResponse {
|
) *util.JSONResponse {
|
||||||
|
if err := isTrusted(cred.IDServer, cfg); err != nil {
|
||||||
|
return &util.JSONResponse{
|
||||||
|
Code: http.StatusForbidden,
|
||||||
|
JSON: jsonerror.NotTrusted(cred.IDServer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
util.GetLogger(ctx).Infof("conecting to identity server: %s", cred.IDServer)
|
||||||
url := fmt.Sprintf(
|
url := fmt.Sprintf(
|
||||||
"https://%s/_matrix/identity/api/v1/3pid/getValidated3pid",
|
"https://%s/_matrix/identity/api/v1/3pid/getValidated3pid",
|
||||||
cred.IdServer)
|
cred.IDServer)
|
||||||
tr := &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
||||||
}
|
|
||||||
client := &http.Client{Transport: tr}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "POST", url, nil)
|
req, err := http.NewRequestWithContext(ctx, "POST", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
|
|
@ -369,11 +350,11 @@ func validateEmailIdentity(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
q := req.URL.Query()
|
q := req.URL.Query()
|
||||||
q.Add("client_secret", cred.ClientSecret)
|
q.Add("client_secret", cred.Secret)
|
||||||
q.Add("sid", cred.Sid)
|
q.Add("sid", cred.SID)
|
||||||
req.URL.RawQuery = q.Encode()
|
req.URL.RawQuery = q.Encode()
|
||||||
req.Header.Add("Authorization", "Bearer swordfish")
|
req.Header.Add("Authorization", "Bearer swordfish")
|
||||||
resp, err := client.Do(req)
|
resp, err := cfg.Derived.HttpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(ctx).WithError(err).Error("failed conecting to identity server")
|
util.GetLogger(ctx).WithError(err).Error("failed conecting to identity server")
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
|
|
@ -403,6 +384,18 @@ func validateEmailIdentity(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isTrusted checks if a given identity server is part of the list of trusted
|
||||||
|
// identity servers in the configuration file.
|
||||||
|
// Returns an error if the server isn't trusted.
|
||||||
|
func isTrusted(idServer string, cfg *config.ClientAPI) error {
|
||||||
|
for _, server := range cfg.Matrix.TrustedIDServers {
|
||||||
|
if idServer == server {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return threepid.ErrNotTrusted
|
||||||
|
}
|
||||||
|
|
||||||
// UserIDIsWithinApplicationServiceNamespace checks to see if a given userID
|
// UserIDIsWithinApplicationServiceNamespace checks to see if a given userID
|
||||||
// falls within any of the namespaces of a given Application Service. If no
|
// falls within any of the namespaces of a given Application Service. If no
|
||||||
// Application Service is given, it will check to see if it matches any
|
// Application Service is given, it will check to see if it matches any
|
||||||
|
|
@ -759,13 +752,13 @@ func handleRegistrationFlow(
|
||||||
JSON: jsonerror.BadJSON("threepidCreds not found in auth field"),
|
JSON: jsonerror.BadJSON("threepidCreds not found in auth field"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := r.Auth.ThreePidCreds.validate(); err != nil {
|
if err := r.Auth.ThreePidCreds.Validate(); err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: err,
|
JSON: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := validateEmailIdentity(req.Context(), r.Auth.ThreePidCreds); err != nil {
|
if err := validateEmailIdentity(req.Context(), r.Auth.ThreePidCreds, cfg); err != nil {
|
||||||
return *err
|
return *err
|
||||||
}
|
}
|
||||||
AddCompletedSessionStage(sessionID, authtypes.LoginTypeEmailIdentity)
|
AddCompletedSessionStage(sessionID, authtypes.LoginTypeEmailIdentity)
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,16 @@
|
||||||
package threepid
|
package threepid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -48,6 +49,19 @@ type Credentials struct {
|
||||||
Secret string `json:"client_secret"`
|
Secret string `json:"client_secret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Credentials) Validate() *jsonerror.MatrixError {
|
||||||
|
if c.SID == "" {
|
||||||
|
return jsonerror.BadJSON("sid field in threepidCreds is required")
|
||||||
|
}
|
||||||
|
if c.Secret == "" {
|
||||||
|
return jsonerror.BadJSON("client_secret in threepidCreds is required")
|
||||||
|
}
|
||||||
|
if c.IDServer == "" {
|
||||||
|
return jsonerror.BadJSON("id_server in threepidCreds is required")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// CreateSession creates a session on an identity server.
|
// CreateSession creates a session on an identity server.
|
||||||
// Returns the session's ID.
|
// Returns the session's ID.
|
||||||
// Returns an error if there was a problem sending the request or decoding the
|
// Returns an error if there was a problem sending the request or decoding the
|
||||||
|
|
@ -62,22 +76,23 @@ func CreateSession(
|
||||||
// Create a session on the ID server
|
// Create a session on the ID server
|
||||||
postURL := fmt.Sprintf("https://%s/_matrix/identity/api/v1/validate/email/requestToken", req.IDServer)
|
postURL := fmt.Sprintf("https://%s/_matrix/identity/api/v1/validate/email/requestToken", req.IDServer)
|
||||||
|
|
||||||
data := url.Values{}
|
b := bytes.Buffer{}
|
||||||
data.Add("client_secret", req.Secret)
|
enc := json.NewEncoder(&b)
|
||||||
data.Add("email", req.Email)
|
err := enc.Encode(req)
|
||||||
data.Add("send_attempt", strconv.Itoa(req.SendAttempt))
|
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodPost, postURL, strings.NewReader(data.Encode()))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
request, err := http.NewRequest(http.MethodPost, postURL, &b)
|
||||||
|
|
||||||
client := http.Client{}
|
|
||||||
resp, err := client.Do(request.WithContext(ctx))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
request.Header.Add("Content-Type", "application/json")
|
||||||
|
|
||||||
|
resp, err := cfg.Derived.HttpClient.Do(request.WithContext(ctx))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
// Error if the status isn't OK
|
// Error if the status isn't OK
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
|
@ -112,11 +127,11 @@ func CheckAssociation(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", "", err
|
return false, "", "", err
|
||||||
}
|
}
|
||||||
resp, err := http.DefaultClient.Do(req.WithContext(ctx))
|
resp, err := cfg.Derived.HttpClient.Do(req.WithContext(ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", "", err
|
return false, "", "", err
|
||||||
}
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
var respBody struct {
|
var respBody struct {
|
||||||
Medium string `json:"medium"`
|
Medium string `json:"medium"`
|
||||||
ValidatedAt int64 `json:"validated_at"`
|
ValidatedAt int64 `json:"validated_at"`
|
||||||
|
|
@ -160,8 +175,7 @@ func PublishAssociation(creds Credentials, userID string, cfg *config.ClientAPI)
|
||||||
}
|
}
|
||||||
request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
client := http.Client{}
|
resp, err := cfg.Derived.HttpClient.Do(request)
|
||||||
resp, err := client.Do(request)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,14 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
@ -97,6 +101,9 @@ type Derived struct {
|
||||||
Params map[string]interface{} `json:"params"`
|
Params map[string]interface{} `json:"params"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used for request to identity server
|
||||||
|
HttpClient *http.Client
|
||||||
|
|
||||||
// Application services parsed from their config files
|
// Application services parsed from their config files
|
||||||
// The paths of which were given above in the main config file
|
// The paths of which were given above in the main config file
|
||||||
ApplicationServices []ApplicationService
|
ApplicationServices []ApplicationService
|
||||||
|
|
@ -289,6 +296,26 @@ func (config *Dendrite) Derive() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.ClientAPI.CustomCaPath != "" {
|
||||||
|
// Add custom CA cert
|
||||||
|
rootCAs, err := x509.SystemCertPool()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
certs, err := ioutil.ReadFile(config.ClientAPI.CustomCaPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ok := rootCAs.AppendCertsFromPEM(certs); !ok {
|
||||||
|
return errors.New("failed to append custom certificate")
|
||||||
|
}
|
||||||
|
trConfig := &tls.Config{
|
||||||
|
RootCAs: rootCAs,
|
||||||
|
}
|
||||||
|
tr := &http.Transport{TLSClientConfig: trConfig}
|
||||||
|
config.Derived.HttpClient = &http.Client{Transport: tr}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ type ClientAPI struct {
|
||||||
Matrix *Global `yaml:"-"`
|
Matrix *Global `yaml:"-"`
|
||||||
Derived *Derived `yaml:"-"` // TODO: Nuke Derived from orbit
|
Derived *Derived `yaml:"-"` // TODO: Nuke Derived from orbit
|
||||||
|
|
||||||
|
// The path to file of custom CA certificate to be added to root CA
|
||||||
|
CustomCaPath string `yaml:"custom_ca_path"`
|
||||||
|
|
||||||
InternalAPI InternalAPIOptions `yaml:"internal_api"`
|
InternalAPI InternalAPIOptions `yaml:"internal_api"`
|
||||||
ExternalAPI ExternalAPIOptions `yaml:"external_api"`
|
ExternalAPI ExternalAPIOptions `yaml:"external_api"`
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue