realm-object-server
Version:
962 lines • 43.2 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs-extra");
const path = require("path");
const URI = require("urijs");
const moment = require("moment");
const crypto = require("crypto");
const useragent = require("express-useragent");
const Queue = require("js-queue");
const _ = require("lodash");
const util_1 = require("../shared/util");
const decorators_1 = require("../decorators");
const Token_1 = require("../shared/Token");
const errors = require("../errors");
const ConfigurableServiceBase_1 = require("./ConfigurableServiceBase");
const realms_1 = require("../realms");
const Constants_1 = require("../shared/Constants");
const events_1 = require("events");
let AuthService = class AuthService extends ConfigurableServiceBase_1.ConfigurableServiceBase {
constructor(config = {}) {
super(config);
this.providers = [];
this.providerQueue = new Queue();
events_1.EventEmitter.call(this);
}
setConfigCore(config = {}) {
this.runtimeConfigurationEnabled = config.enableRuntimeConfiguration;
this.refreshTokenTtl = config.refreshTokenTtl || moment.duration(10, "years").asSeconds();
this.userTokenTtl = config.userTokenTtl || moment.duration(1, "hours").asSeconds();
this.accessTokenTtl = config.accessTokenTtl || moment.duration(6, "minutes").asSeconds();
this.allowAnyUserToRetrieveUserInfo = config.allowAnyUserToRetrieveUserInfo;
}
addProvider(provider) {
this.providers.push(provider);
}
setDefaultProviders(providers) {
this.defaultProviders = providers;
}
getUserByProviderId(provider, providerId) {
const accounts = this.adminRealm.objects("Account").filtered("provider = $0 AND providerId = $1", provider, providerId);
if (accounts.length > 0) {
return accounts[0].users[0];
}
return undefined;
}
createOrUpdateUser(providerId, provider, isAdmin, metadata = {}, userId) {
return __awaiter(this, void 0, void 0, function* () {
const missingParameters = new Array();
if (!provider) {
missingParameters.push("provider");
}
if (!providerId) {
missingParameters.push("providerId");
}
if (missingParameters.length > 0) {
throw new errors.realm.MissingParameters(...missingParameters);
}
if (userId) {
const encodedUserId = encodeURIComponent(userId);
if (userId !== encodedUserId) {
throw new errors.realm.InvalidParameters({
name: "userId",
reason: `Realm requires the userId that does not require URI encoding. Supplied: '${userId}', encoded: '${encodedUserId}'`,
});
}
}
let user = this.getUserByProviderId(provider, providerId);
if (user && userId && userId !== user.userId) {
throw new errors.realm.InvalidParameters({
name: "userId",
reason: "userId does not match the user found by provider/providerId"
});
}
yield util_1.downloadAllServerChanges(this.adminRealm);
this.adminRealm.beginTransaction();
if (!user) {
user = this.getUserByProviderId(provider, providerId);
}
let hasChanges = false;
if (user) {
if (isAdmin !== undefined && user.isAdmin !== isAdmin) {
user.isAdmin = isAdmin;
hasChanges = true;
}
user.created = false;
}
else {
hasChanges = true;
user = this.adminRealm.create("User", {
userId: userId || this.generateUniqueId(),
isAdmin: isAdmin || false,
}, true);
user.accounts.push({
provider: provider,
providerId: providerId
});
user.created = true;
}
const totalUsers = this.adminRealm.objects("User").length;
hasChanges = this.updateUserMetadataInTransaction(user, metadata) || hasChanges;
if (hasChanges) {
this.adminRealm.commitTransaction();
yield util_1.uploadAllLocalChanges(this.adminRealm);
}
else {
this.adminRealm.cancelTransaction();
}
if (user.created) {
this.emit("userCreated", { user, totalUsers });
}
return user;
});
}
updateUserMetadata(user, metadata = {}) {
return util_1.writeAsync(this.adminRealm, () => this.updateUserMetadataInTransaction(user, metadata));
}
updateUserMetadataInTransaction(user, metadata = {}) {
const realmUser = (typeof user === "string") ? this.adminRealm.objectForPrimaryKey("User", user) : user;
const oldMetadata = {};
for (const row of realmUser.metadata) {
oldMetadata[row.key] = row.value;
}
if (_.isEqual(oldMetadata, metadata)) {
return false;
}
this.adminRealm.delete(realmUser.metadata);
for (const key in metadata) {
realmUser.metadata.push({
key: key,
value: metadata[key]
});
}
return true;
}
startCore(server) {
return __awaiter(this, void 0, void 0, function* () {
this.logger = server.logger.withContext({ service: "auth" });
this.server = server;
this.privateKey = server.privateKey;
this.adminRealm = yield server.openRealm(realms_1.AdminRealm);
this.tokenRevocationRealm = yield server.openRealm(realms_1.TokenRevocationRealm);
this.realmDirectoryClient = server.realmDirectoryClient;
this.authCounter = this.stats.counter({
name: "ros_auth_requests_total",
help: "Counter of total auth requests, with provider",
labelNames: ["provider", "identity"],
});
try {
let foundUser = this.adminRealm.objectForPrimaryKey("User", Constants_1.Constants.AdminUserId);
if (!foundUser) {
foundUser = yield this.createOrUpdateUser(Constants_1.Constants.AdminUserId, "realm", true, {}, Constants_1.Constants.AdminUserId);
this.logger.info(`Autocreated admin token user: ${Constants_1.Constants.AdminUserId}`);
}
}
catch (err) {
this.logger.error("Failed to autocreate admin token user", err);
}
yield this.pruneRevocationTokens();
this.cleanupInterval = setInterval(this.pruneRevocationTokens.bind(this), 1000 * 60 * 60 * 24);
if (this.runtimeConfigurationEnabled === undefined) {
const hasSetProviders = !util_1.isArrayEmpty(this.providers);
const hasSetDefaultProviders = !util_1.isArrayEmpty(this.defaultProviders);
if (hasSetProviders && hasSetDefaultProviders) {
throw new Error("Setting providers with addProvider is incompatible with using the runtime configuration API (using setDefaultProviders).");
}
this.runtimeConfigurationEnabled = !hasSetProviders;
}
if (this.runtimeConfigurationEnabled) {
const providerConfigs = this.configurationRealm.objects("AuthProviderConfig");
if (providerConfigs.length === 0 || this.defaultProviders.findIndex(p => p.forceCodeConfig) > -1) {
const providers = (this.defaultProviders || []).filter(p => providerConfigs.length === 0 || p.forceCodeConfig);
let shouldCommitTransaction = false;
this.configurationRealm.beginTransaction();
for (const provider of providers) {
const name = provider.name || provider.type;
const config = provider.config ? JSON.stringify(provider.config) : null;
const forceCodeConfig = provider.forceCodeConfig === true;
const existing = this.configurationRealm.objectForPrimaryKey("AuthProviderConfig", name);
const hasChanges = !existing ||
existing.name !== name ||
existing.type !== provider.type ||
existing.forceCodeConfig !== forceCodeConfig ||
existing.config !== config;
if (hasChanges) {
this.configurationRealm.create("AuthProviderConfig", {
name: name,
type: provider.type,
config: config,
forceCodeConfig: forceCodeConfig
}, true);
shouldCommitTransaction = true;
}
}
if (shouldCommitTransaction) {
this.configurationRealm.commitTransaction();
}
else {
this.configurationRealm.cancelTransaction();
}
}
for (const providerConfig of providerConfigs) {
try {
const provider = this.createProvider(providerConfig);
this.providers.push(provider);
}
catch (err) {
this.logger.error(`Could not initialize auth provider '${providerConfig.name}': ${err.message}`);
}
}
providerConfigs.addListener((collection, changes) => {
const containsChanges = changes.deletions.length ||
changes.modifications.length ||
changes.insertions.length;
if (containsChanges) {
this.enqueueProviderOperation(() => __awaiter(this, void 0, void 0, function* () {
yield Promise.all(this.providers.map(p => this.stopProvider(p)));
this.providers.length = 0;
yield Promise.all(providerConfigs.map((config) => __awaiter(this, void 0, void 0, function* () {
try {
const provider = this.createProvider(config);
this.providers.push(provider);
yield this.startProvider(provider);
}
catch (err) {
this.logger.error(`Failed to start provider ${config.type}(${config.name}) with config ${config.config}. Error: ${err}`);
}
})));
}));
}
});
}
yield new Promise((resolve) => {
this.enqueueProviderOperation(() => __awaiter(this, void 0, void 0, function* () {
const promises = this.providers.map((p) => __awaiter(this, void 0, void 0, function* () {
try {
this.logger.detail(`Starting auth provider '${p.name}'`);
yield this.startProvider(p);
}
catch (err) {
this.logger.error(`Failed to start provider ${p.name} during initial boot. Error: ${err}`);
}
}));
yield Promise.all(promises);
resolve();
}));
});
});
}
stopping() {
return __awaiter(this, void 0, void 0, function* () {
yield this.stopProviderQueue();
yield Promise.all(this.providers.map(provider => {
return this.stopProvider(provider);
}));
delete (this.providers);
});
}
stopCore() {
return __awaiter(this, void 0, void 0, function* () {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
}
if (this.tokenRevocationRealm) {
this.tokenRevocationRealm.close();
delete this.tokenRevocationRealm;
}
if (this.adminRealm) {
this.adminRealm.close();
delete this.adminRealm;
}
});
}
authenticate(req, body) {
return __awaiter(this, void 0, void 0, function* () {
const providerName = body.provider;
const appId = body["app_id"] || "io.realm.Auth";
if (!providerName) {
throw new errors.realm.MissingParameters("provider");
}
if (providerName === "realm") {
const data = body.data;
if (!data) {
throw new errors.realm.MissingParameters("data");
}
let refreshToken;
try {
const token = this.server.tokenValidator.parse(data);
if (!(token instanceof Token_1.RefreshToken)) {
throw new errors.realm.AccessDenied();
}
refreshToken = token;
}
catch (err) {
if (err instanceof errors.realm.RealmProblem) {
throw err;
}
throw new errors.realm.AccessDenied({
title: "Failed to parse token",
detail: err.message
});
}
const user = this.adminRealm.objectForPrimaryKey("User", refreshToken.identity);
this.validateStatus(user);
this.authCounter.inc({ provider: providerName, identity: refreshToken.identity });
if (body["path"]) {
const path = util_1.validateRealmPath(body["path"], refreshToken.identity);
return this.accessToken(user, path, refreshToken, data, appId);
}
return this.userToken(user, refreshToken, appId);
}
const provider = this.providers.find((p) => p.name === providerName);
if (!provider) {
throw new errors.realm.InvalidParameters("provider");
}
const user = yield provider.authenticateOrCreateUser(body);
this.validateStatus(user);
const refreshToken = new Token_1.RefreshToken({
appId,
identity: user.userId,
isAdmin: user.isAdmin,
expires: moment().add(this.refreshTokenTtl, "second").unix(),
isEmailConfirmed: user.isEmailConfirmed()
});
req.socket.identity = refreshToken.identity;
this.authCounter.inc({ provider: providerName, identity: user.userId });
return {
refresh_token: {
token: refreshToken.sign(this.privateKey),
token_data: refreshToken.toJSON(),
}
};
});
}
userToken(user, token, app_id) {
return __awaiter(this, void 0, void 0, function* () {
const userToken = new Token_1.UserToken({
identity: token.identity,
isAdmin: user.isAdmin,
appId: app_id,
expires: moment().add(this.userTokenTtl, "seconds").unix(),
});
return {
user_token: {
token: userToken.sign(this.privateKey),
token_data: userToken.toJSON(),
}
};
});
}
accessToken(user, path, token, data, app_id) {
return __awaiter(this, void 0, void 0, function* () {
path = path.replace("~", token.identity);
const partialInfo = util_1.extractPartialInfo(path);
if (partialInfo.isPartial) {
path = partialInfo.canonicalPath;
}
let mayRead = false;
let mayWrite = false;
let mayManage = false;
let syncLabel = "";
const shouldCreate = true;
if (user.isAdmin) {
mayRead = true;
mayWrite = true;
mayManage = true;
if (partialInfo.isPartial) {
const realmResponseReference = yield this.server.realmDirectoryClient.findByPath({
realmPath: path,
shouldCreate,
token: data,
realmType: realms_1.RealmType.reference
});
util_1.ensureRealmOfType(realms_1.RealmType.reference, realmResponseReference.realmType);
const realmResponsePartial = yield this.server.realmDirectoryClient.findByPath({
realmPath: partialInfo.fullPath,
shouldCreate,
token: data,
realmType: realms_1.RealmType.partial
});
syncLabel = realmResponsePartial.syncLabel;
}
else {
const realmResponse = yield this.server.realmDirectoryClient.findByPath({
realmPath: path,
shouldCreate,
token: data,
realmType: realms_1.RealmType.full
});
syncLabel = realmResponse.syncLabel;
}
}
else {
if (partialInfo.isPartial) {
mayRead = true;
mayWrite = true;
const realmResponsePartial = yield this.server.realmDirectoryClient.findByPath({
realmPath: partialInfo.fullPath,
shouldCreate,
token: data,
realmType: realms_1.RealmType.partial
});
syncLabel = realmResponsePartial.syncLabel;
}
else {
const realmResponse = yield this.server.realmDirectoryClient.findByPath({
realmPath: path,
shouldCreate,
token: data,
realmType: realms_1.RealmType.full
});
util_1.ensureRealmOfType(realms_1.RealmType.full, realmResponse.realmType);
syncLabel = realmResponse.syncLabel;
yield util_1.downloadAllServerChanges(this.adminRealm);
const realmFile = this.adminRealm.objectForPrimaryKey("RealmFile", path);
if (!realmFile) {
throw new Error(`could not find realmfile: ${path}`);
}
const permissions = realmFile.permissions.filtered("user = null OR user.userId = $0 DISTINCT (mayRead, mayWrite, mayManage)", token.identity);
permissions.forEach(permission => {
mayRead = mayRead || permission.mayRead;
mayWrite = mayWrite || permission.mayWrite;
mayManage = mayManage || permission.mayManage;
});
if (!mayRead && !mayWrite && !mayManage) {
throw new errors.realm.AccessDenied();
}
}
}
const access = [];
if (mayRead) {
access.push("download");
}
if (mayWrite) {
access.push("upload");
}
if (mayManage) {
access.push("manage");
}
const isAskingForTildePermission = path === `/${token.identity}/__permission`;
if (isAskingForTildePermission) {
yield this.server.permissionsClient.onDemand(data);
}
const appId = app_id || "io.realm.Auth";
const accessToken = new Token_1.AccessToken({
identity: token.identity,
appId,
access: access,
path: partialInfo.fullPath,
expires: moment().add(this.accessTokenTtl, "seconds").unix(),
syncLabel: syncLabel,
});
return {
access_token: {
token: accessToken.sign(this.privateKey),
token_data: accessToken.toJSON(),
}
};
});
}
validateStatus(user) {
if (!user || user.status === realms_1.UserStatus.suspended) {
throw new errors.realm.AccessDenied({
title: "User account has been suspended. Please contact us for more information.",
detail: `User ${user ? `with Id: '${user.userId}' has been suspended` : "does not exist"}.`,
});
}
}
revokeToken(tokenData, req) {
return __awaiter(this, void 0, void 0, function* () {
if (!tokenData || tokenData === "") {
throw new errors.realm.MissingParameters("token");
}
const token = this.server.tokenValidator.parse(tokenData);
this.requireAdminOrUserId(req, token.identity);
const expires = token.expires ? new Date(token.expires * 1000) : null;
yield util_1.writeAsync(this.tokenRevocationRealm, () => {
this.tokenRevocationRealm.create("TokenRevocation", {
expires,
token: token.getRevocationId(),
revoked: new Date(),
}, true);
});
this.logger.detail(`Revoked token for userId ${token.identity}. Token id: ${token.getRevocationId()}`);
return {};
});
}
getUserById(userId, req) {
if (!this.allowAnyUserToRetrieveUserInfo) {
this.requireAdminOrUserId(req, userId);
}
const user = this.adminRealm.objectForPrimaryKey("User", userId);
if (!user) {
throw new errors.realm.UnknownAccount();
}
return user;
}
getUserByProviderIdApi(provider, providerId, req) {
const decodedProviderId = URI.decode(providerId);
const user = this.getUserByProviderId(provider, decodedProviderId);
if (user && (this.allowAnyUserToRetrieveUserInfo || this.server.tokenValidator.isAdminToken(req.authToken) || user.userId === req.authToken.identity)) {
return user;
}
throw new errors.realm.UnknownAccount();
}
createOrUpdateUserApi(req, providerId, provider, isAdmin, metadata = {}, userId) {
return __awaiter(this, void 0, void 0, function* () {
this.requireAdminOrUserId(req);
return this.createOrUpdateUser(providerId, provider, isAdmin, metadata, userId);
});
}
patchUserApi(req, userId, status) {
return __awaiter(this, void 0, void 0, function* () {
this.requireAdminOrUserId(req);
const user = this.adminRealm.objectForPrimaryKey("User", userId);
if (!user) {
throw new errors.realm.UnknownUser();
}
if (!(status in realms_1.UserStatus)) {
throw new errors.realm.InvalidParameters("status");
}
if (user.status !== status) {
this.adminRealm.write(() => {
user.status = status;
});
}
return user;
});
}
getProviders(req) {
this.requireAdminOrUserId(req);
this.requireRuntimeConfiguration();
return this.providers.map(p => {
let type;
let config;
let name;
name = p.providerConfigName;
const configObject = this.configurationRealm.objectForPrimaryKey("AuthProviderConfig", name);
if (configObject) {
type = configObject.type;
config = configObject.config ? JSON.parse(configObject.config) : {};
}
return {
name: name,
type: type || "unknown",
config: config
};
});
}
addOrUpdateProviderApi(req, type, config, name) {
return __awaiter(this, void 0, void 0, function* () {
this.requireAdminOrUserId(req);
this.requireRuntimeConfiguration();
yield util_1.writeAsync(this.configurationRealm, () => {
this.configurationRealm.create("AuthProviderConfig", {
name: name || type,
type: type,
config: config ? JSON.stringify(config) : null,
forceCodeConfig: false
}, true);
});
return {};
});
}
removeProviderApi(req, name) {
return __awaiter(this, void 0, void 0, function* () {
this.requireAdminOrUserId(req);
this.requireRuntimeConfiguration();
this.configurationRealm.beginTransaction();
const config = this.configurationRealm.objectForPrimaryKey("AuthProviderConfig", name);
if (!config) {
this.configurationRealm.cancelTransaction();
throw new errors.JSONError({
status: 404,
title: "Provider not found.",
detail: `The provider ${name} was not found in the list of active providers.`
});
}
this.configurationRealm.delete(config);
this.configurationRealm.commitTransaction();
return {};
});
}
updateProviderData(providerName, userId, data, req) {
return __awaiter(this, void 0, void 0, function* () {
if (!userId) {
userId = req.authToken.identity;
}
this.requireAdminOrUserId(req, userId);
const provider = this.providers.find((p) => p.name === providerName);
if (!provider) {
throw new errors.realm.InvalidParameters("provider");
}
if (!data) {
throw new errors.realm.MissingParameters("data");
}
const user = this.adminRealm.objectForPrimaryKey("User", userId);
if (!user) {
throw new errors.realm.UnknownAccount();
}
if (provider.update) {
return provider.update(user, data);
}
return {};
});
}
updateProviderAccount(providerName, data, req, providerId) {
return __awaiter(this, void 0, void 0, function* () {
const provider = this.providers.find((p) => p.name === providerName);
if (!provider) {
throw new errors.realm.InvalidParameters("provider");
}
if (!provider.updateProviderAccount) {
throw new errors.realm.InvalidParameters({ name: "provider", reason: "this provider does not support updating user accounts." });
}
const user = providerId && this.getUserByProviderId(providerName, providerId);
const isAuthenticated = !!(req.authToken && (this.server.tokenValidator.isAdminToken(req.authToken) || (user && req.authToken.identity === user.userId)));
const userAgent = useragent.parse(req.get("user-agent") || "");
const ip = req.get("x-forwarded-for") || req.connection.remoteAddress;
return provider.updateProviderAccount(providerId, user, data, isAuthenticated, userAgent, ip);
});
}
deleteUser(userId, req) {
return __awaiter(this, void 0, void 0, function* () {
this.requireAdminOrUserId(req, userId);
this.logger.debug(`User ${userId} is requested to be deleted`);
yield this.deleteUserWithUserId(userId);
this.logger.debug(`User ${userId} has been deleted`);
return {};
});
}
enhanceLog(getToken, body) {
if (getToken("method") === "POST" && getToken("url") === "/auth") {
const logSegments = [`provider: ${body.provider}`];
if (body.provider === "realm") {
logSegments.push(`path: '${body.path}'`);
}
else {
const provider = this.providers.find(p => p.name === body.provider);
if (!provider) {
logSegments.push("INVALID");
}
else if (provider.enhanceLog) {
logSegments.push(provider.enhanceLog(body));
}
}
return `[${logSegments.filter(l => l).join(", ")}]`;
}
return null;
}
createProvider(config) {
let parsedConfig;
if (!config.config) {
parsedConfig = {};
}
else {
let configString = config.config;
const matches = configString.match(/%%[^%]*%%/g);
if (matches) {
for (const match of matches) {
const replacement = process.env[match.replace(/%%/g, "")];
configString = configString.replace(match, replacement);
}
}
parsedConfig = JSON.parse(configString);
}
const Providers = require("../auth");
const result = new Providers[config.type](parsedConfig);
result.providerConfigName = config.name;
return result;
}
startProvider(provider) {
return __awaiter(this, void 0, void 0, function* () {
Reflect.set(provider, "service", this);
if (provider.start) {
yield provider.start();
}
if (provider.name === "nickname") {
this.logger.warn("The 'Nickname' auth provider has been enabled. This provider is not secure and should never be used " +
"in production deployments. If you're still developing your application and experimenting, it is not " +
"a problem, but if this instance is hosting production data, please disable it immediately.");
}
});
}
stopProvider(provider) {
return __awaiter(this, void 0, void 0, function* () {
let toStop;
if (typeof provider === "string") {
const index = this.providers.findIndex(p => p.providerConfigName === provider);
if (index !== -1) {
toStop = this.providers.splice(index, 1)[0];
}
}
else {
toStop = provider;
}
if (toStop) {
if (toStop.stop) {
yield toStop.stop();
}
Reflect.set(toStop, "service", undefined);
}
});
}
pruneRevocationTokens() {
return __awaiter(this, void 0, void 0, function* () {
const deadRevocations = this.tokenRevocationRealm.objects("TokenRevocation").filtered("expires < $0", new Date());
if (deadRevocations.length > 0) {
yield util_1.writeAsync(this.tokenRevocationRealm, () => {
this.tokenRevocationRealm.delete(deadRevocations);
});
}
});
}
enqueueProviderOperation(operation) {
this.providerQueue.add(() => __awaiter(this, void 0, void 0, function* () {
try {
this.hasPendingProviderOperations = true;
yield operation();
}
catch (err) {
this.logger.log("error", `Provider modification operation failed: ${err}`);
}
finally {
this.hasPendingProviderOperations = false;
this.providerQueue.next();
}
}));
}
stopProviderQueue() {
return __awaiter(this, void 0, void 0, function* () {
this.providerQueue.stop = true;
yield util_1.waitAsync(() => this.hasPendingProviderOperations, p => !p, 10000);
});
}
generateUniqueId() {
return crypto.randomBytes(16).toString("hex");
}
requireAdminOrUserId(req, userId) {
if (!this.server.tokenValidator.isAdminToken(req.authToken)) {
if (!userId) {
throw new errors.realm.AccessDenied();
}
if (userId && userId !== req.authToken.identity) {
throw new errors.realm.AccessDenied();
}
}
}
requireRuntimeConfiguration() {
if (!this.runtimeConfigurationEnabled) {
throw new errors.JSONError({
title: "Runtime configuration disabled",
status: 403,
detail: "The runtime configuration API has been disabled."
});
}
}
deleteUserWithUserId(userId) {
return __awaiter(this, void 0, void 0, function* () {
const adminRealm = this.adminRealm;
const user = adminRealm.objectForPrimaryKey("User", userId);
if (!user) {
throw new errors.realm.UnknownUser();
}
const filesToDelete = adminRealm.objects("RealmFile")
.filtered("owner.userId = $0", userId)
.map((realmFile) => {
return {
path: realmFile.path
};
});
let providerNames = [];
yield util_1.writeAsync(adminRealm, () => {
const user = adminRealm.objectForPrimaryKey("User", userId);
if (!user) {
throw new errors.realm.UnknownUser();
}
adminRealm.delete(user.metadata);
providerNames = user.accounts.map((account) => {
return account.provider;
});
adminRealm.delete(user.accounts);
const permissions = adminRealm.objects("Permission")
.filtered("user.userId = $0", userId);
adminRealm.delete(permissions);
adminRealm.delete(user);
});
const deleteUserPromises = [];
providerNames.forEach((providerName) => {
const provider = this.providers.find((p) => p.name === providerName);
if (provider) {
deleteUserPromises.push(provider.deleteUser(userId));
}
});
yield Promise.all(deleteUserPromises);
yield Promise.all(filesToDelete.map((file) => __awaiter(this, void 0, void 0, function* () {
yield this.server["realmFactory"].forceCloseRealm(file.path, `Force closing Realm at path ${file.path} due to user deletion of ${userId}.`, true);
try {
yield this.realmDirectoryClient.delete(file.path);
}
catch (err) {
this.logger.error(`Deletion of a Realm on the sync server failed, path = ${file.path}`);
}
})));
const localUserDir = path.join(this.server.dataPath, "realms", userId);
try {
yield fs.remove(localUserDir);
}
catch (err) {
this.logger.error(`Unable to remove user directory: '${localUserDir}'`);
}
});
}
};
__decorate([
decorators_1.Stopping(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], AuthService.prototype, "stopping", null);
__decorate([
decorators_1.Post("/", { allowAnonymous: true }),
__param(0, decorators_1.Request()),
__param(1, decorators_1.Body()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], AuthService.prototype, "authenticate", null);
__decorate([
decorators_1.Post("/revoke"),
__param(0, decorators_1.Body("token")),
__param(1, decorators_1.Request()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object]),
__metadata("design:returntype", Promise)
], AuthService.prototype, "revokeToken", null);
__decorate([
decorators_1.Get("/users/:user_id"),
__param(0, decorators_1.Params("user_id")),
__param(1, decorators_1.Request()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object]),
__metadata("design:returntype", realms_1.User)
], AuthService.prototype, "getUserById", null);
__decorate([
decorators_1.Get("/users/:provider/:provider_id"),
__param(0, decorators_1.Params("provider")),
__param(1, decorators_1.Params("provider_id")),
__param(2, decorators_1.Request()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String, Object]),
__metadata("design:returntype", realms_1.User)
], AuthService.prototype, "getUserByProviderIdApi", null);
__decorate([
decorators_1.Put("/users"),
__param(0, decorators_1.Request()),
__param(1, decorators_1.Body("provider_id")),
__param(2, decorators_1.Body("provider")),
__param(3, decorators_1.Body("is_admin")),
__param(4, decorators_1.Body("metadata")),
__param(5, decorators_1.Body("user_id")),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String, String, Boolean, Object, String]),
__metadata("design:returntype", Promise)
], AuthService.prototype, "createOrUpdateUserApi", null);
__decorate([
decorators_1.Patch("/users/:user_id"),
__param(0, decorators_1.Request()),
__param(1, decorators_1.Params("user_id")),
__param(2, decorators_1.Body("status")),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String, String]),
__metadata("design:returntype", Promise)
], AuthService.prototype, "patchUserApi", null);
__decorate([
decorators_1.Get("/providers"),
__param(0, decorators_1.Request()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Array)
], AuthService.prototype, "getProviders", null);
__decorate([
decorators_1.Post("/providers"),
__param(0, decorators_1.Request()),
__param(1, decorators_1.Body("type")),
__param(2, decorators_1.Body("config")),
__param(3, decorators_1.Body("name")),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String, Object, String]),
__metadata("design:returntype", Promise)
], AuthService.prototype, "addOrUpdateProviderApi", null);
__decorate([
decorators_1.Delete("/providers/:name"),
__param(0, decorators_1.Request()),
__param(1, decorators_1.Params("name")),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], AuthService.prototype, "removeProviderApi", null);
__decorate([
decorators_1.Put("/:provider"),
__param(0, decorators_1.Params("provider")),
__param(1, decorators_1.Body("user_id")),
__param(2, decorators_1.Body("data")),
__param(3, decorators_1.Request()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String, Object, Object]),
__metadata("design:returntype", Promise)
], AuthService.prototype, "updateProviderData", null);
__decorate([
decorators_1.Post("/:provider/updateAccount", { allowAnonymous: true }),
__param(0, decorators_1.Params("provider")),
__param(1, decorators_1.Body("data")),
__param(2, decorators_1.Request()),
__param(3, decorators_1.Body("provider_id")),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object, Object, String]),
__metadata("design:returntype", Promise)
], AuthService.prototype, "updateProviderAccount", null);
__decorate([
decorators_1.Delete("/user/:user_id"),
decorators_1.Delete("/users/:user_id"),
__param(0, decorators_1.Params("user_id")),
__param(1, decorators_1.Request()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object]),
__metadata("design:returntype", Promise)
], AuthService.prototype, "deleteUser", null);
__decorate([
decorators_1.EnhanceLog(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Function, Object]),
__metadata("design:returntype", String)
], AuthService.prototype, "enhanceLog", null);
AuthService = __decorate([
decorators_1.BaseRoute("/auth", { allowAnonymous: false }),
decorators_1.ServiceName("auth"),
decorators_1.Cors("/"),
__metadata("design:paramtypes", [Object])
], AuthService);
exports.AuthService = AuthService;
Object.assign(AuthService.prototype, events_1.EventEmitter.prototype);
//# sourceMappingURL=AuthService.js.map