mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-16 11:23:11 -06:00
Guest user registration
Signed-off-by: Thibaut CHARLES cromfr@gmail.com
This commit is contained in:
parent
145921f207
commit
2f2c18ae84
|
|
@ -118,7 +118,7 @@ func generateAppServiceAccount(
|
|||
ctx := context.Background()
|
||||
|
||||
// Create an account for the application service
|
||||
acc, err := accountsDB.CreateAccount(ctx, as.SenderLocalpart, "", as.ID)
|
||||
acc, err := accountsDB.CreateAccount(ctx, false, as.SenderLocalpart, "", as.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if acc == nil {
|
||||
|
|
|
|||
|
|
@ -36,16 +36,18 @@ CREATE TABLE IF NOT EXISTS account_accounts (
|
|||
-- The password hash for this account. Can be NULL if this is a passwordless account.
|
||||
password_hash TEXT,
|
||||
-- Identifies which application service this account belongs to, if any.
|
||||
appservice_id TEXT
|
||||
appservice_id TEXT,
|
||||
-- Whether it is a guest account with limited access or not
|
||||
is_guest BOOL
|
||||
-- TODO:
|
||||
-- is_guest, is_admin, upgraded_ts, devices, any email reset stuff?
|
||||
-- is_admin, upgraded_ts, devices, any email reset stuff?
|
||||
);
|
||||
-- Create sequence for autogenerated numeric usernames
|
||||
CREATE SEQUENCE IF NOT EXISTS numeric_username_seq START 1;
|
||||
`
|
||||
|
||||
const insertAccountSQL = "" +
|
||||
"INSERT INTO account_accounts(localpart, created_ts, password_hash, appservice_id) VALUES ($1, $2, $3, $4)"
|
||||
"INSERT INTO account_accounts(localpart, created_ts, password_hash, appservice_id, is_guest) VALUES ($1, $2, $3, $4, $5)"
|
||||
|
||||
const selectAccountByLocalpartSQL = "" +
|
||||
"SELECT localpart, appservice_id FROM account_accounts WHERE localpart = $1"
|
||||
|
|
@ -91,16 +93,16 @@ func (s *accountsStatements) prepare(db *sql.DB, server gomatrixserverlib.Server
|
|||
// this account will be passwordless. Returns an error if this account already exists. Returns the account
|
||||
// on success.
|
||||
func (s *accountsStatements) insertAccount(
|
||||
ctx context.Context, localpart, hash, appserviceID string,
|
||||
ctx context.Context, isGuest bool, localpart, hash, appserviceID string,
|
||||
) (*authtypes.Account, error) {
|
||||
createdTimeMS := time.Now().UnixNano() / 1000000
|
||||
stmt := s.insertAccountStmt
|
||||
|
||||
var err error
|
||||
if appserviceID == "" {
|
||||
_, err = stmt.ExecContext(ctx, localpart, createdTimeMS, hash, nil)
|
||||
_, err = stmt.ExecContext(ctx, localpart, createdTimeMS, hash, nil, isGuest)
|
||||
} else {
|
||||
_, err = stmt.ExecContext(ctx, localpart, createdTimeMS, hash, appserviceID)
|
||||
_, err = stmt.ExecContext(ctx, localpart, createdTimeMS, hash, appserviceID, isGuest)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ func (d *Database) SetDisplayName(
|
|||
// for this account. If no password is supplied, the account will be a passwordless account. If the
|
||||
// account already exists, it will return nil, nil.
|
||||
func (d *Database) CreateAccount(
|
||||
ctx context.Context, localpart, plaintextPassword, appserviceID string,
|
||||
ctx context.Context, isGuest bool, localpart, plaintextPassword, appserviceID string,
|
||||
) (*authtypes.Account, error) {
|
||||
var err error
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ func (d *Database) CreateAccount(
|
|||
}
|
||||
return nil, err
|
||||
}
|
||||
return d.accounts.insertAccount(ctx, localpart, hash, appserviceID)
|
||||
return d.accounts.insertAccount(ctx, isGuest, localpart, hash, appserviceID)
|
||||
}
|
||||
|
||||
// SaveMembership saves the user matching a given localpart as a member of a given
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@ var (
|
|||
Help: "Total number of registered users",
|
||||
},
|
||||
)
|
||||
amtGuestUsers = prometheus.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Name: "dendrite_clientapi_guest_users_total",
|
||||
Help: "Total number of guest users",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -68,6 +74,7 @@ const (
|
|||
func init() {
|
||||
// Register prometheus metrics. They must be registered to be exposed.
|
||||
prometheus.MustRegister(amtRegUsers)
|
||||
prometheus.MustRegister(amtGuestUsers)
|
||||
}
|
||||
|
||||
// sessionsDict keeps track of completed auth stages for each session.
|
||||
|
|
@ -443,18 +450,24 @@ func Register(
|
|||
deviceDB *devices.Database,
|
||||
cfg *config.Dendrite,
|
||||
) util.JSONResponse {
|
||||
|
||||
var r registerRequest
|
||||
resErr := httputil.UnmarshalJSONRequest(req, &r)
|
||||
if resErr != nil {
|
||||
return *resErr
|
||||
}
|
||||
|
||||
// Retrieve or generate the sessionID
|
||||
sessionID := r.Auth.Session
|
||||
if sessionID == "" {
|
||||
// Generate a new, random session ID
|
||||
sessionID = util.RandomString(sessionIDLength)
|
||||
var isGuest bool
|
||||
switch req.URL.Query().Get("kind") {
|
||||
case "user", "":
|
||||
isGuest = false
|
||||
case "guest":
|
||||
isGuest = true
|
||||
// Guest registration ignores all JSON body except initial_device_display_name
|
||||
r = registerRequest{
|
||||
InitialDisplayName: r.InitialDisplayName,
|
||||
}
|
||||
default:
|
||||
return util.MessageResponse(http.StatusBadRequest, "kind must be either 'user' or 'guest'")
|
||||
}
|
||||
|
||||
// Don't allow numeric usernames less than MAX_INT64.
|
||||
|
|
@ -497,12 +510,27 @@ func Register(
|
|||
|
||||
logger := util.GetLogger(req.Context())
|
||||
logger.WithFields(log.Fields{
|
||||
"guest": isGuest,
|
||||
"username": r.Username,
|
||||
"auth.type": r.Auth.Type,
|
||||
"session_id": r.Auth.Session,
|
||||
}).Info("Processing registration request")
|
||||
|
||||
return handleRegistrationFlow(req, r, sessionID, cfg, accountDB, deviceDB)
|
||||
if isGuest {
|
||||
// Immediately register a guest account
|
||||
return handleGuestRegistration(req, r, cfg, accountDB, deviceDB)
|
||||
} else {
|
||||
// Start registration flow
|
||||
|
||||
// Retrieve or generate the sessionID
|
||||
sessionID := r.Auth.Session
|
||||
if sessionID == "" {
|
||||
// Generate a new, random session ID
|
||||
sessionID = util.RandomString(sessionIDLength)
|
||||
}
|
||||
|
||||
return handleRegistrationFlow(req, r, sessionID, cfg, accountDB, deviceDB)
|
||||
}
|
||||
}
|
||||
|
||||
// handleRegistrationFlow will direct and complete registration flow stages
|
||||
|
|
@ -597,6 +625,27 @@ func handleRegistrationFlow(
|
|||
req, r, sessionID, cfg, accountDB, deviceDB)
|
||||
}
|
||||
|
||||
// Registers immediately a guest account, if guest registration is enabled on the server
|
||||
func handleGuestRegistration(
|
||||
req *http.Request,
|
||||
r registerRequest,
|
||||
cfg *config.Dendrite,
|
||||
accountDB *accounts.Database,
|
||||
deviceDB *devices.Database,
|
||||
) util.JSONResponse {
|
||||
|
||||
// Exit if guest registration is forbidden
|
||||
if cfg.Matrix.GuestAccessDisabled {
|
||||
return util.MessageResponse(http.StatusForbidden, "Guest access has been disabled")
|
||||
}
|
||||
|
||||
// Create guest user
|
||||
return completeRegistration(
|
||||
req.Context(), accountDB, deviceDB, true, r.Username, r.Password, "",
|
||||
r.InhibitLogin, r.InitialDisplayName, r.DeviceID,
|
||||
)
|
||||
}
|
||||
|
||||
// handleApplicationServiceRegistration handles the registration of an
|
||||
// application service's user by validating the AS from its access token and
|
||||
// registering the user. Its two first parameters must be the two return values
|
||||
|
|
@ -636,7 +685,7 @@ func handleApplicationServiceRegistration(
|
|||
// Don't need to worry about appending to registration stages as
|
||||
// application service registration is entirely separate.
|
||||
return completeRegistration(
|
||||
req.Context(), accountDB, deviceDB, r.Username, "", appserviceID,
|
||||
req.Context(), accountDB, deviceDB, false, r.Username, "", appserviceID,
|
||||
r.InhibitLogin, r.InitialDisplayName, r.DeviceID,
|
||||
)
|
||||
}
|
||||
|
|
@ -656,7 +705,7 @@ func checkAndCompleteFlow(
|
|||
if checkFlowCompleted(flow, cfg.Derived.Registration.Flows) {
|
||||
// This flow was completed, registration can continue
|
||||
return completeRegistration(
|
||||
req.Context(), accountDB, deviceDB, r.Username, r.Password, "",
|
||||
req.Context(), accountDB, deviceDB, false, r.Username, r.Password, "",
|
||||
r.InhibitLogin, r.InitialDisplayName, r.DeviceID,
|
||||
)
|
||||
}
|
||||
|
|
@ -708,10 +757,10 @@ func LegacyRegister(
|
|||
return util.MessageResponse(http.StatusForbidden, "HMAC incorrect")
|
||||
}
|
||||
|
||||
return completeRegistration(req.Context(), accountDB, deviceDB, r.Username, r.Password, "", false, nil, nil)
|
||||
return completeRegistration(req.Context(), accountDB, deviceDB, false, r.Username, r.Password, "", false, nil, nil)
|
||||
case authtypes.LoginTypeDummy:
|
||||
// there is nothing to do
|
||||
return completeRegistration(req.Context(), accountDB, deviceDB, r.Username, r.Password, "", false, nil, nil)
|
||||
return completeRegistration(req.Context(), accountDB, deviceDB, false, r.Username, r.Password, "", false, nil, nil)
|
||||
default:
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusNotImplemented,
|
||||
|
|
@ -759,6 +808,7 @@ func completeRegistration(
|
|||
ctx context.Context,
|
||||
accountDB *accounts.Database,
|
||||
deviceDB *devices.Database,
|
||||
isGuest bool,
|
||||
username, password, appserviceID string,
|
||||
inhibitLogin common.WeakBoolean,
|
||||
displayName, deviceID *string,
|
||||
|
|
@ -777,7 +827,7 @@ func completeRegistration(
|
|||
}
|
||||
}
|
||||
|
||||
acc, err := accountDB.CreateAccount(ctx, username, password, appserviceID)
|
||||
acc, err := accountDB.CreateAccount(ctx, isGuest, username, password, appserviceID)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
|
|
@ -791,7 +841,11 @@ func completeRegistration(
|
|||
}
|
||||
|
||||
// Increment prometheus counter for created users
|
||||
amtRegUsers.Inc()
|
||||
if isGuest {
|
||||
amtRegUsers.Inc()
|
||||
} else {
|
||||
amtGuestUsers.Inc()
|
||||
}
|
||||
|
||||
// Check whether inhibit_login option is set. If so, don't create an access
|
||||
// token or a device for this user
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
account, err := accountDB.CreateAccount(context.Background(), *username, *password, "")
|
||||
account, err := accountDB.CreateAccount(context.Background(), false, *username, *password, "")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(1)
|
||||
|
|
|
|||
|
|
@ -99,6 +99,9 @@ type Dendrite struct {
|
|||
// If set disables new users from registering (except via shared
|
||||
// secrets)
|
||||
RegistrationDisabled bool `yaml:"registration_disabled"`
|
||||
// If set disables new users from registering (except via shared
|
||||
// secrets)
|
||||
GuestAccessDisabled bool `yaml:"guest_access_disabled"`
|
||||
} `yaml:"matrix"`
|
||||
|
||||
// The configuration specific to the media repostitory.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ matrix:
|
|||
- vector.im
|
||||
- matrix.org
|
||||
|
||||
# Setting this to false will allow guest user to register on the server
|
||||
guest_access_disabled: true
|
||||
|
||||
# The media repository config
|
||||
media:
|
||||
# The base path to where the media files will be stored. May be relative or absolute.
|
||||
|
|
|
|||
17
testfile
17
testfile
|
|
@ -182,3 +182,20 @@ Regular users cannot create room aliases within the AS namespace
|
|||
Deleting a non-existent alias should return a 404
|
||||
Users can't delete other's aliases
|
||||
Outbound federation can query room alias directory
|
||||
Guest user cannot call /events globally
|
||||
Guest users can join guest_access rooms
|
||||
Guest user can set display names
|
||||
Guest user cannot upgrade other users
|
||||
Guest non-joined user cannot call /events on shared room
|
||||
Guest non-joined user cannot call /events on invited room
|
||||
Guest non-joined user cannot call /events on joined room
|
||||
Guest non-joined user cannot call /events on default room
|
||||
Guest non-joined users can get state for world_readable rooms
|
||||
Guest non-joined users can get individual state for world_readable rooms
|
||||
Guest non-joined users cannot room initalSync for non-world_readable rooms
|
||||
Guest non-joined users can get individual state for world_readable rooms after leaving
|
||||
Guest non-joined users cannot send messages to guest_access rooms if not joined
|
||||
Guest users can sync from world_readable guest_access rooms if joined
|
||||
Guest users can sync from default guest_access rooms if joined
|
||||
Real non-joined users cannot room initalSync for non-world_readable rooms
|
||||
Newly joined room is included in an incremental sync after invite
|
||||
Loading…
Reference in a new issue