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 MissingParameter = InvalidData.extend("MissingParameter", { errorCode: "M_MISSING_PARAM" });
let IncompatibleRoomVersion = InvalidData.extend("IncompatibleRoomVersion", { errorCode: "M_INCOMPATIBLE_ROOM_VERSION" }); let IncompatibleRoomVersion = InvalidData.extend("IncompatibleRoomVersion", { errorCode: "M_INCOMPATIBLE_ROOM_VERSION" });
let WeakPassword = InvalidData.extend("WeakPassword", { errorCode: "M_WEAK_PASSWORD" }); let WeakPassword = InvalidData.extend("WeakPassword", { errorCode: "M_WEAK_PASSWORD" });
let ExclusiveResource = InvalidData.extend("ExclusiveResource", { errorCode: "M_EXCLUSIVE" });
/*** HTTP 429: Too Many Requests ***/ /*** HTTP 429: Too Many Requests ***/
@ -65,10 +66,6 @@ let InternalServerError = HttpError.extend("InternalServerError", { statusCode:
let UnknownError = InternalServerError.extend("UnknownError", { errorCode: "M_UNKNOWN" }); 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. */ 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 = { 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 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(); let router = expressPromiseRouter();
router.use(require("./login")(state)); router.use(require("./login")(state));
router.use(require("./register")(state))
router.post("/r0/logout", requireAccessToken, (req, res) => { router.post("/r0/logout", requireAccessToken, (req, res) => {
// https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-logout // 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. // 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) => { router.post("/r0/register/:source/requestToken", (req, res) => {
// source === "email" // source === "email"
// https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-register-email-requesttoken // 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) { function validateLoginPayload(payload) {
let {assertProperties, isPresent, isString, isOneOf} = require("../../../validator-lib.js"); let {assertProperties, isPresent, isString, isOneOf} = require("../../../validator-lib");
assertProperties(payload, { assertProperties(payload, {
type: [ isPresent, isOneOf("m.login.password", "m.login.token") ], type: [ isPresent, isOneOf("m.login.password", "m.login.token") ],
@ -118,7 +118,7 @@ module.exports = function(state) {
access_token: device.token, access_token: device.token,
user_id: fullyQualifyUser(user.username), user_id: fullyQualifyUser(user.username),
home_server: configuration.hostname, 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) { function isOneOf(...acceptableValues) {
let valueSet = new Set(acceptableValues); let valueSet = new Set(acceptableValues);
@ -104,7 +110,7 @@ function validate(validator, value) {
module.exports = { module.exports = {
validate, assertProperties, ValidationError, validate, assertProperties, ValidationError,
isPresent, isString, isOneOf, arrayOf isPresent, isString, isOneOf, arrayOf, isBoolean
}; };
// try { // try {

Loading…
Cancel
Save