Skip to content
This repository has been archived by the owner on Apr 19, 2023. It is now read-only.

Commit

Permalink
✨ Support for organization domains
Browse files Browse the repository at this point in the history
  • Loading branch information
AnandChowdhary committed Jul 22, 2019
1 parent a71315c commit 3cb7e01
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 35 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "staart-manager",
"version": "1.0.90",
"version": "1.0.91",
"main": "index.js",
"repository": "[email protected]:AnandChowdhary/staart.git",
"author": "Anand Chowdhary <[email protected]>",
Expand Down Expand Up @@ -135,5 +135,5 @@
"setup"
],
"snyk": true,
"staart-version": "1.0.90"
"staart-version": "1.0.91"
}
17 changes: 14 additions & 3 deletions schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
Target Server Version : 100221
File Encoding : 65001
Date: 18/07/2019 11:18:22
Date: 22/07/2019 12:00:53
*/

SET NAMES utf8mb4;
Expand Down Expand Up @@ -62,6 +62,18 @@ CREATE TABLE `staart-backup-codes` (
KEY `id` (`userId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

-- ----------------------------
-- Table structure for staart-domains
-- ----------------------------
DROP TABLE IF EXISTS `staart-domains`;
CREATE TABLE `staart-domains` (
`id` int(11) NOT NULL,
`organizationId` int(11) NOT NULL,
`domain` varchar(255) COLLATE utf8mb4_bin NOT NULL,
`isVerified` int(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

-- ----------------------------
-- Table structure for staart-emails
-- ----------------------------
Expand Down Expand Up @@ -91,7 +103,7 @@ CREATE TABLE `staart-events` (
`userAgent` text COLLATE utf8mb4_bin DEFAULT NULL,
`createdAt` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

-- ----------------------------
-- Table structure for staart-memberships
Expand Down Expand Up @@ -133,7 +145,6 @@ CREATE TABLE `staart-organizations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`username` varchar(255) COLLATE utf8mb4_bin NOT NULL,
`invitationDomain` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`stripeCustomerId` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`ipRestrictions` text COLLATE utf8mb4_bin DEFAULT NULL,
`forceTwoFactor` int(1) NOT NULL DEFAULT 0,
Expand Down
133 changes: 126 additions & 7 deletions src/controllers/organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ import {
createApiKeyForUser,
getOrganizationApiKeyForUser,
updateApiKeyForUser,
deleteApiKeyForUser
deleteApiKeyForUser,
getOrganizationDomainsForUser,
createDomainForUser,
getOrganizationDomainForUser,
updateDomainForUser,
deleteDomainForUser
} from "../rest/organization";
import {
Get,
Expand Down Expand Up @@ -57,8 +62,7 @@ export class OrganizationController {
@Middleware(
validator(
{
name: Joi.string().required(),
invitationDomain: Joi.string()
name: Joi.string().required()
},
"body"
)
Expand Down Expand Up @@ -88,10 +92,7 @@ export class OrganizationController {
name: Joi.string(),
username: Joi.string(),
forceTwoFactor: Joi.boolean(),
ipRestrictions: Joi.string(),
invitationDomain: Joi.string().regex(
/([a-z])([a-z0-9]+\.)*[a-z0-9]+\.[a-z.]+/
)
ipRestrictions: Joi.string()
},
"body"
)
Expand Down Expand Up @@ -634,4 +635,122 @@ export class OrganizationController {
)
);
}

@Get(":id/domains")
async getUserDomains(req: Request, res: Response) {
const id = await organizationUsernameToId(req.params.id);
joiValidate(
{ id: [Joi.string().required(), Joi.number().required()] },
{ id }
);
const domainParams = { ...req.query };
joiValidate(
{
start: Joi.string(),
itemsPerPage: Joi.number()
},
domainParams
);
res.json(
await getOrganizationDomainsForUser(
localsToTokenOrKey(res),
id,
domainParams
)
);
}

@Put(":id/domains")
@Middleware(
validator(
{
domain: Joi.string()
},
"body"
)
)
async putUserDomains(req: Request, res: Response) {
const id = await organizationUsernameToId(req.params.id);
joiValidate(
{ id: [Joi.string().required(), Joi.number().required()] },
{ id }
);
res
.status(CREATED)
.json(
await createDomainForUser(
localsToTokenOrKey(res),
id,
req.body,
res.locals
)
);
}

@Get(":id/domains/:domainId")
async getUserDomain(req: Request, res: Response) {
const id = await organizationUsernameToId(req.params.id);
const domainId = req.params.domainId;
joiValidate(
{
id: [Joi.string().required(), Joi.number().required()],
domainId: Joi.number().required()
},
{ id, domainId }
);
res.json(
await getOrganizationDomainForUser(localsToTokenOrKey(res), id, domainId)
);
}

@Patch(":id/domains/:domainId")
@Middleware(
validator(
{
domain: Joi.string()
},
"body"
)
)
async patchUserDomain(req: Request, res: Response) {
const id = await organizationUsernameToId(req.params.id);
const domainId = req.params.domainId;
joiValidate(
{
id: [Joi.string().required(), Joi.number().required()],
domainId: Joi.number().required()
},
{ id, domainId }
);
res.json(
await updateDomainForUser(
localsToTokenOrKey(res),
id,
domainId,
req.body,
res.locals
)
);
}

@Delete(":id/domains/:domainId")
async deleteUserDomain(req: Request, res: Response) {
const id = await organizationUsernameToId(req.params.id);
const domainId = req.params.domainId;
joiValidate(
{
id: [Joi.string().required(), Joi.number().required()],
domainId: Joi.number().required()
},
{ id, domainId }
);
res.json(
await deleteDomainForUser(
localsToTokenOrKey(res),
id,
domainId,
res.locals
)
);
}
}
80 changes: 79 additions & 1 deletion src/crud/organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
removeReadOnlyValues,
tableName
} from "../helpers/mysql";
import { Organization } from "../interfaces/tables/organization";
import { Organization, Domain } from "../interfaces/tables/organization";
import { capitalizeFirstAndLastLetter, createSlug } from "../helpers/utils";
import { KeyValue } from "../interfaces/general";
import { cachedQuery, deleteItemFromCache } from "../helpers/cache";
Expand All @@ -14,6 +14,7 @@ import { ApiKey } from "../interfaces/tables/organization";
import { getPaginatedData } from "./data";
import { apiKeyToken, invalidateToken } from "../helpers/jwt";
import { TOKEN_EXPIRY_API_KEY_MAX } from "../config";
import { InsertResult } from "../interfaces/mysql";

/*
* Create a new organization for a user
Expand Down Expand Up @@ -200,3 +201,80 @@ export const deleteApiKey = async (
[apiKeyId, organizationId]
);
};

/**
* Get a list of domains for an organization
*/
export const getOrganizationDomains = async (
organizationId: number,
query: KeyValue
) => {
return await getPaginatedData({
table: "domains",
conditions: {
organizationId
},
...query
});
};

/**
* Get a domain
*/
export const getDomain = async (organizationId: number, domainId: number) => {
return (<Domain[]>(
await query(
`SELECT * FROM ${tableName(
"domains"
)} WHERE id = ? AND organizationId = ? LIMIT 1`,
[domainId, organizationId]
)
))[0];
};

/**
* Create a domain
*/
export const createDomain = async (domain: Domain): Promise<InsertResult> => {
domain.createdAt = new Date();
domain.updatedAt = domain.createdAt;
return await query(
`INSERT INTO ${tableName("domains")} ${tableValues(domain)}`,
Object.values(domain)
);
};

/**
* Update a domain
*/
export const updateDomain = async (
organizationId: number,
domainId: number,
data: KeyValue
) => {
data.updatedAt = new Date();
data = removeReadOnlyValues(data);
const domain = await getDomain(organizationId, domainId);
return await query(
`UPDATE ${tableName("domains")} SET ${setValues(
data
)} WHERE id = ? AND organizationId = ?`,
[...Object.values(data), domainId, organizationId]
);
};

/**
* Delete a domain
*/
export const deleteDomain = async (
organizationId: number,
domainId: number
) => {
const currentDomain = await getDomain(organizationId, domainId);
return await query(
`DELETE FROM ${tableName(
"domains"
)} WHERE id = ? AND organizationId = ? LIMIT 1`,
[domainId, organizationId]
);
};
3 changes: 1 addition & 2 deletions src/crud/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
removeReadOnlyValues,
tableName
} from "../helpers/mysql";
import { User, ApprovedLocation } from "../interfaces/tables/user";
import { User, ApprovedLocation, BackupCode } from "../interfaces/tables/user";
import {
capitalizeFirstAndLastLetter,
deleteSensitiveInfoUser,
Expand All @@ -22,7 +22,6 @@ import { getEmail, getVerifiedEmailObject } from "./email";
import { cachedQuery, deleteItemFromCache } from "../helpers/cache";
import md5 from "md5";
import randomInt from "random-int";
import { BackupCode } from "../interfaces/tables/backup-codes";

/**
* Get a list of all ${tableName("users")}
Expand Down
6 changes: 3 additions & 3 deletions src/helpers/mysql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import {
DB_DATABASE,
DB_TABLE_PREFIX
} from "../config";
import { User } from "../interfaces/tables/user";
import { BackupCode } from "../interfaces/tables/backup-codes";
import { User, BackupCode } from "../interfaces/tables/user";
import { Email } from "../interfaces/tables/emails";
import { Membership } from "../interfaces/tables/memberships";
import { Organization } from "../interfaces/tables/organization";
import { Event } from "../interfaces/tables/events";
import { KeyValue } from "../interfaces/general";
import { boolValues, jsonValues, dateValues, readOnlyValues } from "./utils";
import { getUserPrimaryEmailObject } from "../crud/email";
import { InsertResult } from "../interfaces/mysql";

export const pool = createPool({
host: DB_HOST,
Expand All @@ -31,7 +31,7 @@ export const pool = createPool({
export const query = (
queryString: string,
values?: (string | number | boolean | Date | undefined)[]
) =>
): InsertResult | any =>
new Promise((resolve, reject) => {
pool.getConnection((error, connection) => {
if (error) return reject(error);
Expand Down
9 changes: 9 additions & 0 deletions src/interfaces/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,12 @@ export interface Locals {
ipAddress: string;
referrer?: string;
}

export interface Row {
createdAt?: Date;
updatedAt?: Date;
}

export interface IdRow extends Row {
id?: number;
}
7 changes: 0 additions & 7 deletions src/interfaces/tables/backup-codes.ts

This file was deleted.

Loading

0 comments on commit 3cb7e01

Please sign in to comment.