mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-10 16:08:20 -05:00
Continue work on API key
This commit is contained in:
@@ -4,7 +4,10 @@ WORKDIR /app
|
||||
|
||||
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 . .
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
"version": "1.0.0",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"prepare": "cd .. && npm install",
|
||||
"start": "npm run build && node build/index.js",
|
||||
"dev": "nodemon",
|
||||
"build": "rimraf ./build && tsc && cp -R ./src/templates ./build",
|
||||
|
||||
40
backend/src/middleware/requireAPIKeyDataAuth.ts
Normal file
40
backend/src/middleware/requireAPIKeyDataAuth.ts
Normal file
@@ -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 { ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD } from '../variables';
|
||||
|
||||
// TODO: add scopes
|
||||
|
||||
export interface IAPIKey {
|
||||
export interface IAPIKeyData {
|
||||
name: string;
|
||||
workspace: string;
|
||||
environment: string;
|
||||
workspaces: {
|
||||
workspace: Types.ObjectId,
|
||||
environments: string[]
|
||||
}[];
|
||||
expiresAt: Date;
|
||||
prefix: string;
|
||||
apiKeyHash: string;
|
||||
@@ -15,19 +15,22 @@ export interface IAPIKey {
|
||||
tag: string;
|
||||
}
|
||||
|
||||
const apiKeySchema = new Schema<IAPIKey>(
|
||||
const apiKeyDataSchema = new Schema<IAPIKeyData>(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
workspace: {
|
||||
type: String
|
||||
},
|
||||
environment: {
|
||||
type: String,
|
||||
enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD]
|
||||
},
|
||||
workspaces: [{
|
||||
workspace: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Workspace'
|
||||
},
|
||||
environments: [{
|
||||
type: String,
|
||||
enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD]
|
||||
}]
|
||||
}],
|
||||
expiresAt: {
|
||||
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 UserAction, { IUserAction } from './userAction';
|
||||
import Workspace, { IWorkspace } from './workspace';
|
||||
import APIKey, { IAPIKey } from './apiKey';
|
||||
import APIKeyData, { IAPIKeyData } from './apiKeyData';
|
||||
|
||||
export {
|
||||
BackupPrivateKey,
|
||||
@@ -49,6 +49,6 @@ export {
|
||||
IUserAction,
|
||||
Workspace,
|
||||
IWorkspace,
|
||||
APIKey,
|
||||
IAPIKey,
|
||||
APIKeyData,
|
||||
IAPIKeyData,
|
||||
};
|
||||
|
||||
@@ -4,16 +4,14 @@ import {
|
||||
requireAuth
|
||||
} from '../middleware';
|
||||
import {
|
||||
APIKey
|
||||
APIKeyData
|
||||
} from '../models';
|
||||
import { body } from 'express-validator';
|
||||
import { param, body, query } from 'express-validator';
|
||||
import crypto from 'crypto';
|
||||
import bcrypt from 'bcrypt';
|
||||
// import * as bcrypt from 'bcrypt';
|
||||
// const bcrypt = require('bcrypt');
|
||||
import * as Sentry from '@sentry/node';
|
||||
|
||||
// POST /api/v1/api-key
|
||||
// TODO: middleware
|
||||
router.post(
|
||||
'/',
|
||||
requireAuth,
|
||||
@@ -25,7 +23,7 @@ router.post(
|
||||
body('tag'),
|
||||
body('expiresAt'),
|
||||
async (req, res) => {
|
||||
let savedAPIKey;
|
||||
let apiKey, apiKeyData;
|
||||
try {
|
||||
const {
|
||||
name,
|
||||
@@ -37,14 +35,13 @@ router.post(
|
||||
expiresAt
|
||||
} = req.body;
|
||||
|
||||
// api-key: 38 characters
|
||||
// 6-char: prefix
|
||||
// 32-char: remaining
|
||||
const apiKey = crypto.randomBytes(19).toString('hex');
|
||||
const saltRounds = 10; // config?
|
||||
// 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);
|
||||
|
||||
savedAPIKey = await new APIKey({
|
||||
apiKeyData = await new APIKeyData({
|
||||
name,
|
||||
workspace,
|
||||
environment,
|
||||
@@ -55,22 +52,72 @@ router.post(
|
||||
iv,
|
||||
tag
|
||||
}).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) {
|
||||
Sentry.setUser({ email: req.user.email });
|
||||
Sentry.captureException(err);
|
||||
return res.status(400).send({
|
||||
message: 'xxx'
|
||||
message: 'Failed to create workspace API Key'
|
||||
});
|
||||
}
|
||||
|
||||
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