master
Sven Slootweg 5 years ago
parent e3ac9edfe4
commit 4cce6bab09

@ -54,6 +54,7 @@ let InvalidParameter = InvalidData.extend("InvalidParameter", { errorCode: "M_IN
let MissingParameter = InvalidData.extend("MissingParameter", { errorCode: "M_MISSING_PARAM" });
let IncompatibleRoomVersion = InvalidData.extend("IncompatibleRoomVersion", { errorCode: "M_INCOMPATIBLE_ROOM_VERSION" });
let WeakPassword = InvalidData.extend("WeakPassword", { errorCode: "M_WEAK_PASSWORD" });
let ExclusiveResource = InvalidData.extend("ExclusiveResource", { errorCode: "M_EXCLUSIVE" });
/*** HTTP 429: Too Many Requests ***/
@ -65,10 +66,6 @@ let InternalServerError = HttpError.extend("InternalServerError", { statusCode:
let UnknownError = InternalServerError.extend("UnknownError", { errorCode: "M_UNKNOWN" });
let Unreachable = InternalServerError.extend("Unreachable"); /* FIXME: How to represent this with an error code? This should crash the process anyway. */
/*** HTTP 503: Service Unavailable ***/
// This describes reserved namespaces and such (eg. for registration), so probably should be a client error instead
let ExclusiveResource = HttpError.extend("ExclusiveResource", { statusCode: 503, errorCode: "M_EXCLUSIVE" });
module.exports = {
HttpError, Unauthorized, MissingAccessToken, InvalidAccessToken, NotJson, BadJson, NotFound, TooManyRequests, MissingCaptcha, InvalidCaptcha, ServerNotTrusted, NoGuestAccess, UserExists, RoomExists, RequestTooLarge, InvalidUsername, InvalidRoomVersion, InvalidRoomState, InvalidStateChange, InvalidParameter, MissingParameter, IncompatibleRoomVersion, InternalServerError, UnknownError, ExclusiveResource, ResourceExists, Forbidden, BadRequest, ExternalIdentifierNotFound, ExternalIdentifierExists, ExternalIdentifierAuthenticationFailed, InvalidExternalIdentifier, WeakPassword, Unreachable
};

@ -9,6 +9,7 @@ module.exports = function(state) {
let router = expressPromiseRouter();
router.use(require("./login")(state));
router.use(require("./register")(state))
router.post("/r0/logout", requireAccessToken, (req, res) => {
// https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-logout
@ -25,12 +26,6 @@ module.exports = function(state) {
// Checks to see if a username is available, and valid, for the server.
});
router.post("/r0/register", (req, res) => {
// https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-register
// Register for an account on this homeserver. This API endpoint uses the User-Interactive Authentication API.
// Optionally allows user-interactive auth
});
router.post("/r0/register/:source/requestToken", (req, res) => {
// source === "email"
// https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-register-email-requesttoken

@ -57,7 +57,7 @@ module.exports = function(state) {
});
function validateLoginPayload(payload) {
let {assertProperties, isPresent, isString, isOneOf} = require("../../../validator-lib.js");
let {assertProperties, isPresent, isString, isOneOf} = require("../../../validator-lib");
assertProperties(payload, {
type: [ isPresent, isOneOf("m.login.password", "m.login.token") ],
@ -118,7 +118,7 @@ module.exports = function(state) {
access_token: device.token,
user_id: fullyQualifyUser(user.username),
home_server: configuration.hostname,
device_id: device.deviceId
device_id: device.device_id
});
});
});

@ -0,0 +1,85 @@
"use strict";
const Promise = require("bluebird");
const defaultValue = require("default-value");
const errors = require("../../../errors");
const validate = require("../../../middlewares/validate-payload");
const processDeviceID = require("../../../process-device-id");
module.exports = function({ db, knex, configuration, fullyQualifyUser }) {
let router = require("express-promise-router")();
function validateRegisterPayload(payload) {
let {assertProperties, isPresent, isString, isOneOf, isBoolean} = require("../../../validator-lib");
assertProperties(payload, {
kind: [ isOneOf("user", "guest") ],
device_id: [ isString ],
initial_device_display_name: [ isString ],
username: [ isPresent, isString, (username) => {
if (!/^[a-z0-9._=/-]+$/.test(username)) {
throw new errors.InvalidUsername("Usernames may only contain a-z, 0-9, and . _ = - /");
} else if (username.startsWith("_")) {
throw new errors.ExclusiveResource("Usernames starting with _ are reserved for bridges");
}
} ],
password: [ isPresent, isString ], /* FIXME: Password strength */
inhibit_login: [ isBoolean ],
bind_email: [ isBoolean ]
});
}
router.post("/r0/register", validate(validateRegisterPayload), (req, res) => {
// https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-register
// Register for an account on this homeserver. This API endpoint uses the User-Interactive Authentication API.
/* FIXME: Support User-Interactive Authentication (eg. for registration CAPTCHAs) */
/* FIXME: Rate-limit */
if (req.body.kind === "guest") {
/* TODO: Re-evaluate this, once it becomes clearer how to deal with guest users and peeking across federation (ref. https://github.com/matrix-org/matrix-doc/pull/1777) */
throw new errors.Forbidden("Guest registrations are disabled on this server");
} else {
let inhibitLogin = defaultValue(req.body.inhibit_login, false);
/* FILEBUG: Default for bind_email is not clearly specified in spec */
/* FIXME: Actually implement e-mail binding on identity server */
let bindEmail = defaultValue(req.body.bind_email, false);
let deviceId = processDeviceID(req.body.deviceID);
return knex.transaction((transaction) => {
return Promise.try(() => {
return db.users.create({
type: "user",
username: req.body.username,
password: req.body.password
}, transaction);
}).then((user) => {
return Promise.try(() => {
if (!inhibitLogin) {
return db.devices.createDeviceSession({
name: req.body.initial_device_display_name,
deviceId: deviceId,
userId: user.id
}, transaction);
}
}).then((device) => {
let sessionDetails = (device != null)
? { access_token: device.token, device_id: device.device_id }
: {};
res.send({
user_id: fullyQualifyUser(user.username),
home_server: configuration.hostname,
...sessionDetails
});
});
});
}).catch({ name: "UniqueConstraintViolationError", table: "users", column: "username" }, (_error) => {
throw new errors.UserExists("That username is already taken");
});
}
});
return router;
}

@ -16,6 +16,12 @@ function isString(value) {
}
}
function isBoolean(value) {
if (value != null && typeof value !== "boolean") {
throw new ValidationError("Value is not a boolean", { errorType: "isBoolean" });
}
}
function isOneOf(...acceptableValues) {
let valueSet = new Set(acceptableValues);
@ -104,7 +110,7 @@ function validate(validator, value) {
module.exports = {
validate, assertProperties, ValidationError,
isPresent, isString, isOneOf, arrayOf
isPresent, isString, isOneOf, arrayOf, isBoolean
};
// try {

Loading…
Cancel
Save