mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 21:27:10 +00:00
Continue work on API key
This commit is contained in:
+4
-1
@@ -4,7 +4,10 @@ WORKDIR /app
|
|||||||
|
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
|
|
||||||
RUN npm ci --only-production --ignore-scripts
|
# RUN npm ci --only-production --ignore-scripts
|
||||||
|
# "prepare": "cd .. && npm install"
|
||||||
|
|
||||||
|
RUN npm ci --only-production
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "cd .. && npm install",
|
|
||||||
"start": "npm run build && node build/index.js",
|
"start": "npm run build && node build/index.js",
|
||||||
"dev": "nodemon",
|
"dev": "nodemon",
|
||||||
"build": "rimraf ./build && tsc && cp -R ./src/templates ./build",
|
"build": "rimraf ./build && tsc && cp -R ./src/templates ./build",
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
import { APIKeyData } from '../models';
|
||||||
|
import { validateMembership } from '../helpers/membership';
|
||||||
|
import { AccountNotFoundError } from '../utils/errors';
|
||||||
|
|
||||||
|
type req = 'params' | 'body' | 'query';
|
||||||
|
|
||||||
|
const requireAPIKeyDataAuth = ({
|
||||||
|
acceptedRoles,
|
||||||
|
acceptedStatuses,
|
||||||
|
location = 'params'
|
||||||
|
}: {
|
||||||
|
acceptedRoles: string[];
|
||||||
|
acceptedStatuses: string[];
|
||||||
|
location?: req;
|
||||||
|
}) => {
|
||||||
|
return async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
|
||||||
|
// req.user
|
||||||
|
|
||||||
|
const apiKeyData = await APIKeyData.findById(req[location].apiKeyDataId);
|
||||||
|
|
||||||
|
if (!apiKeyData) {
|
||||||
|
return next(AccountNotFoundError({message: 'Failed to locate API Key data'}));
|
||||||
|
}
|
||||||
|
|
||||||
|
await validateMembership({
|
||||||
|
userId: req.user._id.toString(),
|
||||||
|
workspaceId: apiKeyData?.workspace.toString(),
|
||||||
|
acceptedRoles,
|
||||||
|
acceptedStatuses
|
||||||
|
});
|
||||||
|
|
||||||
|
req.apiKeyData = '' // ??
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default requireAPIKeyDataAuth;
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { Schema, model, Types } from 'mongoose';
|
import { Schema, model, Types } from 'mongoose';
|
||||||
import { ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD } from '../variables';
|
import { ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD } from '../variables';
|
||||||
|
|
||||||
// TODO: add scopes
|
export interface IAPIKeyData {
|
||||||
|
|
||||||
export interface IAPIKey {
|
|
||||||
name: string;
|
name: string;
|
||||||
workspace: string;
|
workspaces: {
|
||||||
environment: string;
|
workspace: Types.ObjectId,
|
||||||
|
environments: string[]
|
||||||
|
}[];
|
||||||
expiresAt: Date;
|
expiresAt: Date;
|
||||||
prefix: string;
|
prefix: string;
|
||||||
apiKeyHash: string;
|
apiKeyHash: string;
|
||||||
@@ -15,19 +15,22 @@ export interface IAPIKey {
|
|||||||
tag: string;
|
tag: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiKeySchema = new Schema<IAPIKey>(
|
const apiKeyDataSchema = new Schema<IAPIKeyData>(
|
||||||
{
|
{
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
workspace: {
|
workspaces: [{
|
||||||
type: String
|
workspace: {
|
||||||
},
|
type: Schema.Types.ObjectId,
|
||||||
environment: {
|
ref: 'Workspace'
|
||||||
type: String,
|
},
|
||||||
enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD]
|
environments: [{
|
||||||
},
|
type: String,
|
||||||
|
enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD]
|
||||||
|
}]
|
||||||
|
}],
|
||||||
expiresAt: {
|
expiresAt: {
|
||||||
type: Date
|
type: Date
|
||||||
},
|
},
|
||||||
@@ -58,6 +61,6 @@ const apiKeySchema = new Schema<IAPIKey>(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const APIKey = model<IAPIKey>('APIKey', apiKeySchema);
|
const APIKeyData = model<IAPIKeyData>('APIKeyData', apiKeyDataSchema);
|
||||||
|
|
||||||
export default APIKey;
|
export default APIKeyData;
|
||||||
@@ -14,7 +14,7 @@ import Token, { IToken } from './token';
|
|||||||
import User, { IUser } from './user';
|
import User, { IUser } from './user';
|
||||||
import UserAction, { IUserAction } from './userAction';
|
import UserAction, { IUserAction } from './userAction';
|
||||||
import Workspace, { IWorkspace } from './workspace';
|
import Workspace, { IWorkspace } from './workspace';
|
||||||
import APIKey, { IAPIKey } from './apiKey';
|
import APIKeyData, { IAPIKeyData } from './apiKeyData';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
BackupPrivateKey,
|
BackupPrivateKey,
|
||||||
@@ -49,6 +49,6 @@ export {
|
|||||||
IUserAction,
|
IUserAction,
|
||||||
Workspace,
|
Workspace,
|
||||||
IWorkspace,
|
IWorkspace,
|
||||||
APIKey,
|
APIKeyData,
|
||||||
IAPIKey,
|
IAPIKeyData,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,16 +4,14 @@ import {
|
|||||||
requireAuth
|
requireAuth
|
||||||
} from '../middleware';
|
} from '../middleware';
|
||||||
import {
|
import {
|
||||||
APIKey
|
APIKeyData
|
||||||
} from '../models';
|
} from '../models';
|
||||||
import { body } from 'express-validator';
|
import { param, body, query } from 'express-validator';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from 'bcrypt';
|
||||||
// import * as bcrypt from 'bcrypt';
|
|
||||||
// const bcrypt = require('bcrypt');
|
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
|
|
||||||
// POST /api/v1/api-key
|
// TODO: middleware
|
||||||
router.post(
|
router.post(
|
||||||
'/',
|
'/',
|
||||||
requireAuth,
|
requireAuth,
|
||||||
@@ -25,7 +23,7 @@ router.post(
|
|||||||
body('tag'),
|
body('tag'),
|
||||||
body('expiresAt'),
|
body('expiresAt'),
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
let savedAPIKey;
|
let apiKey, apiKeyData;
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
@@ -37,14 +35,13 @@ router.post(
|
|||||||
expiresAt
|
expiresAt
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
// api-key: 38 characters
|
// create 38-char API key with first 6-char being the prefix
|
||||||
// 6-char: prefix
|
apiKey = crypto.randomBytes(19).toString('hex');
|
||||||
// 32-char: remaining
|
|
||||||
const apiKey = crypto.randomBytes(19).toString('hex');
|
const saltRounds = 10; // TODO: add as config envar
|
||||||
const saltRounds = 10; // config?
|
|
||||||
const apiKeyHash = await bcrypt.hash(apiKey, saltRounds);
|
const apiKeyHash = await bcrypt.hash(apiKey, saltRounds);
|
||||||
|
|
||||||
savedAPIKey = await new APIKey({
|
apiKeyData = await new APIKeyData({
|
||||||
name,
|
name,
|
||||||
workspace,
|
workspace,
|
||||||
environment,
|
environment,
|
||||||
@@ -55,22 +52,72 @@ router.post(
|
|||||||
iv,
|
iv,
|
||||||
tag
|
tag
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
// 1. generate api key
|
|
||||||
// 2. hash api key with bcrypt
|
|
||||||
// 3. store hash and api key info in db
|
|
||||||
// 4. return api key
|
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser({ email: req.user.email });
|
Sentry.setUser({ email: req.user.email });
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'xxx'
|
message: 'Failed to create workspace API Key'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
apiKey: savedAPIKey
|
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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user