dendrite/userapi/internal/threepid.go
2021-08-11 10:10:49 +02:00

134 lines
3.5 KiB
Go

package internal
import (
"context"
"database/sql"
"errors"
"net/url"
"time"
"github.com/matrix-org/dendrite/internal"
"github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/dendrite/userapi/mail"
"github.com/matrix-org/dendrite/userapi/storage/threepid"
)
const (
sessionIdByteLength = 32
tokenByteLength = 48
)
var ErrBadSession = errors.New("provided sid, client_secret and token does not point to valid session")
func (a *UserInternalAPI) CreateSession(ctx context.Context, req *api.CreateSessionRequest, res *api.CreateSessionResponse) error {
s, err := a.ThreePidDB.GetSessionByThreePidAndSecret(ctx, req.ThreePid, req.ClientSecret)
if err != nil {
if err == sql.ErrNoRows {
token, errB := internal.GenerateBlob(tokenByteLength)
if errB != nil {
return errB
}
sid, errB := internal.GenerateBlob(sessionIdByteLength)
if errB != nil {
return errB
}
s = &api.Session{
Sid: sid,
ClientSecret: req.ClientSecret,
ThreePid: req.ThreePid,
SendAttempt: req.SendAttempt,
Token: token,
NextLink: req.NextLink,
}
err = a.ThreePidDB.InsertSession(ctx, s)
if err != nil {
return err
}
} else {
return err
}
} else {
if req.SendAttempt > s.SendAttempt {
err = a.ThreePidDB.UpdateSendAttemptNextLink(ctx, s.Sid, req.NextLink)
if err != nil {
return err
}
} else {
res.Sid = s.Sid
return nil
}
}
res.Sid = s.Sid
query := url.Values{
"sid": []string{s.Sid},
"client_secret": []string{s.ClientSecret},
"token": []string{s.Token},
}
link := url.URL{
Scheme: "https",
Host: string(a.ServerName),
Path: req.SessionType.SubmitPath(),
RawQuery: query.Encode(),
}
// TODO - if we fail sending email, send_attempt for next requests must be bumped,
// otherwise we will just return nil from this function and not sent email
return a.Mail.Send(&mail.Mail{
To: s.ThreePid,
Link: link.String(),
Token: s.Token,
Extra: req.Extra,
}, req.SessionType)
}
func (a *UserInternalAPI) ValidateSession(ctx context.Context, req *api.ValidateSessionRequest, res struct{}) error {
s, err := getSessionByOwnership(ctx, &req.SessionOwnership, a.ThreePidDB)
if err != nil {
return err
}
if s.Token != req.Token {
return ErrBadSession
}
return a.ThreePidDB.ValidateSession(ctx, s.Sid, int(time.Now().Unix()))
}
func (a *UserInternalAPI) GetThreePidForSession(ctx context.Context, req *api.SessionOwnership, res *api.GetThreePidForSessionResponse) error {
s, err := getSessionByOwnership(ctx, req, a.ThreePidDB)
if err != nil {
return err
}
res.ThreePid = s.ThreePid
return nil
}
func (a *UserInternalAPI) DeleteSession(ctx context.Context, req *api.SessionOwnership, res struct{}) error {
s, err := getSessionByOwnership(ctx, req, a.ThreePidDB)
if err != nil {
return err
}
return a.ThreePidDB.DeleteSession(ctx, s.Sid)
}
func (a *UserInternalAPI) IsSessionValidated(ctx context.Context, req *api.SessionOwnership, res *api.IsSessionValidatedResponse) error {
s, err := getSessionByOwnership(ctx, req, a.ThreePidDB)
if err != nil {
return err
}
res.Validated = s.Validated
res.ValidatedAt = s.ValidatedAt
return nil
}
func getSessionByOwnership(ctx context.Context, so *api.SessionOwnership, d threepid.Database) (*api.Session, error) {
s, err := d.GetSession(ctx, so.Sid)
if err != nil {
if err == sql.ErrNoRows {
return nil, ErrBadSession
}
return nil, err
}
if s.ClientSecret != so.ClientSecret {
return nil, ErrBadSession
}
return s, err
}