Begin service token data refactor

This commit is contained in:
Tuan Dang
2022-12-30 23:57:21 +03:00
parent addf04d54d
commit 01d969190b
14 changed files with 226 additions and 165 deletions
-127
View File
@@ -1,127 +0,0 @@
import express from 'express';
const router = express.Router();
import {
requireAuth
} from '../middleware';
import {
APIKeyData
} from '../models';
import { param, body, query } from 'express-validator';
import crypto from 'crypto';
import bcrypt from 'bcrypt';
import * as Sentry from '@sentry/node';
// TODO: middleware
router.post(
'/',
requireAuth,
body('name').exists().trim(),
body('workspace'),
body('environment'),
body('encryptedKey'),
body('iv'),
body('tag'),
body('expiresAt'),
async (req, res) => {
let apiKey, apiKeyData;
try {
const {
name,
workspace,
environment,
encryptedKey,
iv,
tag,
expiresAt
} = req.body;
// create 38-char API key with first 6-char being the prefix
apiKey = crypto.randomBytes(19).toString('hex');
const saltRounds = 10; // TODO: add as config envar
const apiKeyHash = await bcrypt.hash(apiKey, saltRounds);
apiKeyData = await new APIKeyData({
name,
workspace,
environment,
expiresAt,
prefix: apiKey.substring(0, 6),
apiKeyHash,
encryptedKey,
iv,
tag
}).save();
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to create workspace API Key'
});
}
return res.status(200).send({
apiKey,
apiKeyData
});
}
);
// TODO: middleware
router.get(
'/',
requireAuth,
query('workspaceId').exists().trim(),
async (req, res) => {
let apiKeyData;
try {
const { workspaceId } = req.query;
apiKeyData = await APIKeyData.find({
workspace: workspaceId
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspace API Key data'
});
}
return res.status(200).send({
apiKeyData
});
}
);
// TODO: middleware
router.delete(
':apiKeyDataId',
requireAuth,
// TODO: requireAPIKeyDataAuth,
param('apiKeyDataId').exists().trim(),
async (req, res) => {
let apiKeyData;
try {
const { apiKeyDataId } = req.params;
apiKeyData = await APIKeyData.findByIdAndDelete(apiKeyDataId);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to delete API key data'
});
}
return res.status(200).send({
apiKeyData
});
}
);
// INFISICAL TOKEN = <API_KEY>.<KEY>
export default router;
+2 -2
View File
@@ -15,7 +15,7 @@ import password from './password';
import stripe from './stripe';
import integration from './integration';
import integrationAuth from './integrationAuth';
import apiKey from './apiKey';
import serviceTokenData from './serviceTokenData';
export {
signup,
@@ -35,5 +35,5 @@ export {
stripe,
integration,
integrationAuth,
apiKey
serviceTokenData
};
+144
View File
@@ -0,0 +1,144 @@
import express from 'express';
const router = express.Router();
import crypto from 'crypto';
import bcrypt from 'bcrypt';
import * as Sentry from '@sentry/node';
import {
requireAuth,
requireWorkspaceAuth,
requireServiceTokenDataAuth,
validateRequest
} from '../middleware';
import {
ServiceTokenData
} from '../models';
import { param, body, query } from 'express-validator';
import {
SALT_ROUNDS
} from '../config';
import {
ADMIN,
MEMBER,
COMPLETED,
GRANTED
} from '../variables';
// TODO: move logic into separate controller (probably after pull with latest routing)
/**
* 2 different concepts that we should distinguish between:
* - API key (user) - allows user to perform queries and mutations on whatever
* their account could access (better than JWT because it has ACL and scoping).
* - Service token (bound to a workspace and environment).
*/
/**
* Service token flow?
* 1. Post service token data details including project key encrypted under <symmetric_key> on cient-side.
* 2. Construct <infisical_token> on client-side as <infisical_token>=<service_token>.<symmetric_key>
* 3. Need for CLI to be able to get back service token details
*/
router.post(
'/',
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
acceptedStatuses: [COMPLETED, GRANTED],
location: 'body'
}),
requireAuth,
body('name').exists().trim(),
body('workspace'),
body('environment'),
body('encryptedKey'),
body('iv'),
body('tag'),
body('expiresAt'),
validateRequest,
async (req, res) => {
let serviceToken, serviceTokenData;
try {
const {
name,
workspace,
environment,
encryptedKey,
iv,
tag,
expiresAt
} = req.body;
// create 38-char service token with first 6-char being the prefix
serviceToken = crypto.randomBytes(19).toString('hex');
const serviceTokenHash = await bcrypt.hash(serviceToken, SALT_ROUNDS);
serviceTokenData = await new ServiceTokenData({
name,
workspace,
environment,
expiresAt,
prefix: serviceToken.substring(0, 6),
serviceTokenHash,
encryptedKey,
iv,
tag
}).save();
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to create service token data'
});
}
return res.status(200).send({
serviceToken,
serviceTokenData
});
}
);
// TODO: CLI has to get service token details without needing a JWT
router.get(
'/:serviceTokenDataId',
requireAuth,
requireServiceTokenDataAuth,
param('serviceTokenDataId').exists().trim(),
validateRequest,
async (req, res) => {
return ({
serviceTokenData: req.serviceTokenData
});
}
);
router.delete(
'/:serviceTokenDataId',
requireAuth,
requireServiceTokenDataAuth,
param('serviceTokenDataId').exists().trim(),
validateRequest,
async (req, res) => {
let serviceTokenData;
try {
const { serviceTokenDataId } = req.params;
serviceTokenData = await ServiceTokenData.findByIdAndDelete(serviceTokenDataId);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to delete service token data'
});
}
return res.status(200).send({
serviceTokenData
});
}
);
export default router;
+13 -1
View File
@@ -119,7 +119,7 @@ router.get(
);
router.get(
'/:workspaceId/service-tokens',
'/:workspaceId/service-tokens', // deprecate
requireAuth,
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -130,4 +130,16 @@ router.get(
workspaceController.getWorkspaceServiceTokens
);
router.get(
'/:workspaceId/service-token-data',
requireAuth,
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
acceptedStatuses: [GRANTED]
}),
param('workspaceId').exists().trim(),
validateRequest,
workspaceController.getWorkspaceServiceTokenData
);
export default router;