Merge pull request #45 from calcom/feat/api-keys-auth

This commit is contained in:
Agusti Fernandez
2022-04-18 23:36:43 +02:00
committed by GitHub
41 changed files with 191 additions and 22 deletions
+2 -1
View File
@@ -1 +1,2 @@
API_KEY_PREFIX=cal_
API_KEY_PREFIX=cal_
DATABASE_URL="postgresql://postgres:@localhost:5450/calendso"
+19 -19
View File
@@ -4,31 +4,31 @@ import { hashAPIKey } from "@calcom/ee/lib/api/apiKeys";
import prisma from "@calcom/prisma";
// Used to check if the API key is not expired, could be extracted if reused. but not for now.
export const dateInPast = function (firstDate: Date, secondDate: Date) {
if (firstDate.setHours(0, 0, 0, 0) <= secondDate.setHours(0, 0, 0, 0)) {
export const dateInPast = function (date: Date) {
const now = new Date();
if (now.setHours(0, 0, 0, 0) <= date.setHours(0, 0, 0, 0)) {
return true;
}
};
const today = new Date();
// This verifies the API key and sets the user if it is valid.
export const verifyApiKey: NextMiddleware = async (req, res, next) => {
if (!req.query.apiKey) res.status(401).json({ message: "No API key provided" });
if (!req.query.apiKey) res.status(401).json({ message: "No api key provided" });
// We remove the prefix from the user provided api_key. If no env set default to "cal_"
const strippedApiKey = `${req.query.apiKey}`.replace(process.env.API_KEY_PREFIX || "cal_", "");
// Hash the key again before matching against the database records.
const hashedKey = hashAPIKey(strippedApiKey);
await prisma.apiKey
.findUnique({ where: { hashedKey } })
.then(async (apiKey) => {
if (!apiKey) {
res.status(401).json({ error: "You did not provide an api key" });
throw new Error("No api key found");
}
if (apiKey.userId) res.setHeader("X-Calcom-User-ID", apiKey.userId);
if (apiKey.expiresAt && apiKey.userId && dateInPast(today, apiKey.expiresAt)) await next();
})
.catch((error) => {
res.status(401).json({ error: "Your api key is not valid" });
});
// Check if the hashed api key exists in database.
await prisma.apiKey.findUnique({ where: { hashedKey } }).then(async (apiKey) => {
// If we cannot find any api key. Throw a 401 Unauthorized.
if (!apiKey) res.status(401).json({ error: "Your api key is not valid" });
if (apiKey && apiKey.expiresAt && dateInPast(apiKey.expiresAt) && apiKey.userId) {
// Right now API Keys are user centric, we only allow resources related to this userId throughout the application.
// if the api key is not expired, and the user id is present in the database.
// Set the user in the request. as x-calcom-user-id.
res.setHeader("X-Calcom-User-ID", apiKey.userId);
// Pass the request to the next middleware.
await next();
}
});
};
+6
View File
@@ -23,6 +23,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the attendee to get
* security:
* - ApiKeyAuth: []
* tags:
* - attendees
* responses:
@@ -50,6 +52,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the attendee to edit
* security:
* - ApiKeyAuth: []
* tags:
* - attendees
* responses:
@@ -69,6 +73,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the attendee to delete
* security:
* - ApiKeyAuth: []
* tags:
* - attendees
* responses:
+4
View File
@@ -12,6 +12,8 @@ import { schemaAttendeeBodyParams, schemaAttendeePublic, withValidAttendee } fro
* /v1/attendees:
* get:
* summary: Get all attendees
* security:
* - ApiKeyAuth: []
* tags:
* - attendees
* responses:
@@ -23,6 +25,8 @@ import { schemaAttendeeBodyParams, schemaAttendeePublic, withValidAttendee } fro
* description: No attendees were found
* post:
* summary: Creates a new attendee
* security:
* - ApiKeyAuth: []
* tags:
* - attendees
* responses:
+6
View File
@@ -23,6 +23,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the availability to get
* security:
* - ApiKeyAuth: []
* tags:
* - availabilities
* responses:
@@ -50,6 +52,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the availability to edit
* security:
* - ApiKeyAuth: []
* tags:
* - availabilities
* responses:
@@ -69,6 +73,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the availability to delete
* security:
* - ApiKeyAuth: []
* tags:
* - availabilities
* responses:
+4
View File
@@ -12,6 +12,8 @@ import { schemaAvailabilityBodyParams, schemaAvailabilityPublic } from "@lib/val
* /v1/availabilities:
* get:
* summary: Get all availabilities
* security:
* - ApiKeyAuth: []
* tags:
* - availabilities
* responses:
@@ -23,6 +25,8 @@ import { schemaAvailabilityBodyParams, schemaAvailabilityPublic } from "@lib/val
* description: No availabilities were found
* post:
* summary: Creates a new availability
* security:
* - ApiKeyAuth: []
* tags:
* - availabilities
* responses:
+6
View File
@@ -27,6 +27,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the daily event reference to get
* security:
* - ApiKeyAuth: []
* tags:
* - booking-references
* responses:
@@ -54,6 +56,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the daily event reference to edit
* security:
* - ApiKeyAuth: []
* tags:
* - booking-references
* responses:
@@ -73,6 +77,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the daily event reference to delete
* security:
* - ApiKeyAuth: []
* tags:
* - booking-references
* responses:
+4
View File
@@ -15,6 +15,8 @@ import {
* /v1/booking-references:
* get:
* summary: Get all booking references
* security:
* - ApiKeyAuth: []
* tags:
* - booking-references
* responses:
@@ -26,6 +28,8 @@ import {
* description: No booking references were found
* post:
* summary: Creates a new booking reference
* security:
* - ApiKeyAuth: []
* tags:
* - booking-references
* responses:
+6
View File
@@ -23,6 +23,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the booking to get
* security:
* - ApiKeyAuth: []
* tags:
* - bookings
* responses:
@@ -50,6 +52,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the booking to edit
* security:
* - ApiKeyAuth: []
* tags:
* - bookings
* responses:
@@ -69,6 +73,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the booking to delete
* security:
* - ApiKeyAuth: []
* tags:
* - bookings
* responses:
+4
View File
@@ -12,6 +12,8 @@ import { schemaBookingBodyParams, schemaBookingPublic, withValidBooking } from "
* /v1/bookings:
* get:
* summary: Get all bookings
* security:
* - ApiKeyAuth: []
* tags:
* - bookings
* responses:
@@ -23,6 +25,8 @@ import { schemaBookingBodyParams, schemaBookingPublic, withValidBooking } from "
* description: No bookings were found
* post:
* summary: Creates a new booking
* security:
* - ApiKeyAuth: []
* tags:
* - bookings
* responses:
+6
View File
@@ -23,6 +23,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the credential to get
* security:
* - ApiKeyAuth: []
* tags:
* - credentials
* responses:
@@ -50,6 +52,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the credential to edit
* security:
* - ApiKeyAuth: []
* tags:
* - credentials
* responses:
@@ -69,6 +73,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the credential to delete
* security:
* - ApiKeyAuth: []
* tags:
* - credentials
* responses:
+4
View File
@@ -12,6 +12,8 @@ import { schemaCredentialBodyParams, schemaCredentialPublic } from "@lib/validat
* /api/credentials:
* get:
* summary: Get all credentials
* security:
* - ApiKeyAuth: []
* tags:
* - credentials
* responses:
@@ -23,6 +25,8 @@ import { schemaCredentialBodyParams, schemaCredentialPublic } from "@lib/validat
* description: No credentials were found
* post:
* summary: Creates a new credential
* security:
* - ApiKeyAuth: []
* tags:
* - credentials
* responses:
+6
View File
@@ -25,6 +25,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the daily event reference to get
* security:
* - ApiKeyAuth: []
* tags:
* - daily-event-references
* responses:
@@ -52,6 +54,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the daily event reference to edit
* security:
* - ApiKeyAuth: []
* tags:
* - daily-event-references
* responses:
@@ -71,6 +75,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the daily event reference to delete
* security:
* - ApiKeyAuth: []
* tags:
* - daily-event-references
* responses:
@@ -14,6 +14,8 @@ import {
* /v1/daily-event-references:
* get:
* summary: Get all daily event reference
* security:
* - ApiKeyAuth: []
* tags:
* - daily-event-reference
* responses:
@@ -25,6 +27,8 @@ import {
* description: No daily event references were found
* post:
* summary: Creates a new daily event reference
* security:
* - ApiKeyAuth: []
* tags:
* - daily-event-reference
* responses:
+6
View File
@@ -25,6 +25,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the destination calendar to get
* security:
* - ApiKeyAuth: []
* tags:
* - destination-calendars
* responses:
@@ -52,6 +54,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the destination calendar to edit
* security:
* - ApiKeyAuth: []
* tags:
* - destination-calendars
* responses:
@@ -71,6 +75,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the destination calendar to delete
* security:
* - ApiKeyAuth: []
* tags:
* - destination-calendars
* responses:
+4
View File
@@ -14,6 +14,8 @@ import {
* /v1/destination-calendars:
* get:
* summary: Get all destination calendars
* security:
* - ApiKeyAuth: []
* tags:
* - destination-calendars
* responses:
@@ -25,6 +27,8 @@ import {
* description: No destination calendars were found
* post:
* summary: Creates a new destination calendar
* security:
* - ApiKeyAuth: []
* tags:
* - destination-calendars
* responses:
+4 -2
View File
@@ -9,8 +9,10 @@ const swaggerHandler = withSwagger({
title: `${pjson.name}: ${pjson.description}`,
version: pjson.version,
},
components: { schemas: { ...jsonSchema.definitions } },
definitions: jsonSchema.definitions,
components: {
securitySchemes: { ApiKeyAuth: { type: "apiKey", in: "query", name: "apiKey" } },
schemas: { ...jsonSchema.definitions },
},
},
apiFolder: "pages/api",
tags: ["users", "teams", "memeberships"],
@@ -25,6 +25,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the eventTypeCustomInput to get
* security:
* - ApiKeyAuth: []
* tags:
* - event-type-custom-inputs
* responses:
@@ -52,6 +54,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the eventTypeCustomInput to edit
* security:
* - ApiKeyAuth: []
* tags:
* - event-type-custom-inputs
* responses:
@@ -71,6 +75,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the eventTypeCustomInput to delete
* security:
* - ApiKeyAuth: []
* tags:
* - event-type-custom-inputs
* responses:
@@ -14,6 +14,8 @@ import {
* /v1/event-type-custom-inputs:
* get:
* summary: Get all eventTypeCustomInputs
* security:
* - ApiKeyAuth: []
* tags:
* - event-type-custom-inputs
* responses:
@@ -25,6 +27,8 @@ import {
* description: No eventTypeCustomInputs were found
* post:
* summary: Creates a new eventTypeCustomInput
* security:
* - ApiKeyAuth: []
* tags:
* - event-type-custom-inputs
* responses:
+6
View File
@@ -22,6 +22,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the eventType to get
* security:
* - ApiKeyAuth: []
* tags:
* - event-types
* responses:
@@ -49,6 +51,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the eventType to edit
* security:
* - ApiKeyAuth: []
* tags:
* - event-types
* responses:
@@ -68,6 +72,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the eventType to delete
* security:
* - ApiKeyAuth: []
* tags:
* - event-types
* responses:
+4
View File
@@ -11,6 +11,8 @@ import { schemaEventTypeBodyParams, schemaEventTypePublic } from "@lib/validatio
* /v1/event-types:
* get:
* summary: Get all event types
* security:
* - ApiKeyAuth: []
* tags:
* - event-types
* responses:
@@ -22,6 +24,8 @@ import { schemaEventTypeBodyParams, schemaEventTypePublic } from "@lib/validatio
* description: No event types were found
* post:
* summary: Creates a new event type
* security:
* - ApiKeyAuth: []
* tags:
* - event-types
* responses:
+6
View File
@@ -25,6 +25,8 @@ import { schemaQueryIdAsString, withValidQueryIdString } from "@lib/validations/
* type: integer
* required: true
* description: Numeric teamId of the membership to get
* security:
* - ApiKeyAuth: []
* tags:
* - memberships
* responses:
@@ -58,6 +60,8 @@ import { schemaQueryIdAsString, withValidQueryIdString } from "@lib/validations/
* type: integer
* required: true
* description: Numeric teamId of the membership to get
* security:
* - ApiKeyAuth: []
* tags:
* - memberships
* responses:
@@ -83,6 +87,8 @@ import { schemaQueryIdAsString, withValidQueryIdString } from "@lib/validations/
* type: integer
* required: true
* description: Numeric teamId of the membership to get
* security:
* - ApiKeyAuth: []
* tags:
* - memberships
* responses:
+4
View File
@@ -11,6 +11,8 @@ import { schemaMembershipBodyParams, schemaMembershipPublic } from "@lib/validat
* /v1/memberships:
* get:
* summary: Get all memberships
* security:
* - ApiKeyAuth: []
* tags:
* - memberships
* responses:
@@ -22,6 +24,8 @@ import { schemaMembershipBodyParams, schemaMembershipPublic } from "@lib/validat
* description: No memberships were found
* post:
* summary: Creates a new membership
* security:
* - ApiKeyAuth: []
* tags:
* - memberships
* responses:
+2
View File
@@ -23,6 +23,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the payment to get
* security:
* - ApiKeyAuth: []
* tags:
* - payments
* responses:
+2
View File
@@ -12,6 +12,8 @@ import { schemaPaymentPublic } from "@lib/validations/payment";
* /v1/payments:
* get:
* summary: Get all payments
* security:
* - ApiKeyAuth: []
* tags:
* - payments
* responses:
+6
View File
@@ -22,6 +22,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the reminderMail to get
* security:
* - ApiKeyAuth: []
* tags:
* - reminder-mails
* responses:
@@ -49,6 +51,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the reminderMail to edit
* security:
* - ApiKeyAuth: []
* tags:
* - reminder-mails
* responses:
@@ -68,6 +72,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the reminderMail to delete
* security:
* - ApiKeyAuth: []
* tags:
* - reminder-mails
* responses:
+4
View File
@@ -15,6 +15,8 @@ import {
* /v1/reminder-mails:
* get:
* summary: Get all reminder mails
* security:
* - ApiKeyAuth: []
* tags:
* - reminder-mails
* responses:
@@ -26,6 +28,8 @@ import {
* description: No reminder mails were found
* post:
* summary: Creates a new reminder mail
* security:
* - ApiKeyAuth: []
* tags:
* - reminder-mails
* responses:
+6
View File
@@ -22,6 +22,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the schedule to get
* security:
* - ApiKeyAuth: []
* tags:
* - schedules
* responses:
@@ -49,6 +51,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the schedule to edit
* security:
* - ApiKeyAuth: []
* tags:
* - schedules
* responses:
@@ -68,6 +72,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the schedule to delete
* security:
* - ApiKeyAuth: []
* tags:
* - schedules
* responses:
+4
View File
@@ -11,6 +11,8 @@ import { schemaScheduleBodyParams, schemaSchedulePublic, withValidSchedule } fro
* /v1/schedules:
* get:
* summary: Get all schedules
* security:
* - ApiKeyAuth: []
* tags:
* - schedules
* responses:
@@ -22,6 +24,8 @@ import { schemaScheduleBodyParams, schemaSchedulePublic, withValidSchedule } fro
* description: No schedules were found
* post:
* summary: Creates a new schedule
* security:
* - ApiKeyAuth: []
* tags:
* - schedules
* responses:
+6
View File
@@ -34,6 +34,8 @@ import { schemaQueryIdAsString, withValidQueryIdString } from "@lib/validations/
* type: string
* required: true
* description: integration of the selected calendar to get
* security:
* - ApiKeyAuth: []
* tags:
* - selected-calendars
* responses:
@@ -73,6 +75,8 @@ import { schemaQueryIdAsString, withValidQueryIdString } from "@lib/validations/
* type: string
* required: true
* description: integration of the selected calendar to get
* security:
* - ApiKeyAuth: []
* tags:
* - selected-calendars
* responses:
@@ -104,6 +108,8 @@ import { schemaQueryIdAsString, withValidQueryIdString } from "@lib/validations/
* type: string
* required: true
* description: integration of the selected calendar to get
* security:
* - ApiKeyAuth: []
* tags:
* - selected-calendars
* responses:
+4
View File
@@ -15,6 +15,8 @@ import {
* /v1/selected-calendars:
* get:
* summary: Get all selected calendars
* security:
* - ApiKeyAuth: []
* tags:
* - selected-calendars
* responses:
@@ -26,6 +28,8 @@ import {
* description: No selected calendars were found
* post:
* summary: Creates a new selected calendar
* security:
* - ApiKeyAuth: []
* tags:
* - selected-calendars
* responses:
+6
View File
@@ -23,6 +23,8 @@ import { schemaTeamBodyParams, schemaTeamPublic } from "@lib/validations/team";
* type: integer
* required: true
* description: Numeric ID of the team to get
* security:
* - ApiKeyAuth: []
* tags:
* - teams
* responses:
@@ -50,6 +52,8 @@ import { schemaTeamBodyParams, schemaTeamPublic } from "@lib/validations/team";
* type: integer
* required: true
* description: Numeric ID of the team to edit
* security:
* - ApiKeyAuth: []
* tags:
* - teams
* responses:
@@ -69,6 +73,8 @@ import { schemaTeamBodyParams, schemaTeamPublic } from "@lib/validations/team";
* type: integer
* required: true
* description: Numeric ID of the team to delete
* security:
* - ApiKeyAuth: []
* tags:
* - teams
* responses:
+4
View File
@@ -12,6 +12,8 @@ import { schemaTeamBodyParams, schemaTeamPublic, withValidTeam } from "@lib/vali
* /v1/teams:
* get:
* summary: Get all teams
* security:
* - ApiKeyAuth: []
* tags:
* - teams
* responses:
@@ -23,6 +25,8 @@ import { schemaTeamBodyParams, schemaTeamPublic, withValidTeam } from "@lib/vali
* description: No teams were found
* post:
* summary: Creates a new team
* security:
* - ApiKeyAuth: []
* tags:
* - teams
* responses:
+6
View File
@@ -23,6 +23,8 @@ import { schemaUserBodyParams, schemaUserPublic } from "@lib/validations/user";
* type: integer
* required: true
* description: Numeric ID of the user to get
* security:
* - ApiKeyAuth: []
* tags:
* - users
* responses:
@@ -50,6 +52,8 @@ import { schemaUserBodyParams, schemaUserPublic } from "@lib/validations/user";
* type: integer
* required: true
* description: Numeric ID of the user to edit
* security:
* - ApiKeyAuth: []
* tags:
* - users
* responses:
@@ -69,6 +73,8 @@ import { schemaUserBodyParams, schemaUserPublic } from "@lib/validations/user";
* type: integer
* required: true
* description: Numeric ID of the user to delete
* security:
* - ApiKeyAuth: []
* tags:
* - users
* responses:
+2
View File
@@ -12,6 +12,8 @@ import { schemaUserPublic } from "@lib/validations/user";
* /v1/users:
* get:
* summary: Get all users (admin only), returns your user if regular user.
* security:
* - ApiKeyAuth: []
* tags:
* - users
* responses:
+2
View File
@@ -21,6 +21,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the resource to delete
* security:
* - ApiKeyAuth: []
* tags:
* - resources
* responses:
+2
View File
@@ -22,6 +22,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the resource to edit
* security:
* - ApiKeyAuth: []
* tags:
* - resources
* responses:
+2
View File
@@ -22,6 +22,8 @@ import {
* type: integer
* required: true
* description: Numeric ID of the resource to get
* security:
* - ApiKeyAuth: []
* tags:
* - resources
* responses:
+2
View File
@@ -11,6 +11,8 @@ import { schemaResourcePublic } from "@lib/validations/resource";
* /v1/resources:
* get:
* summary: Get all resources
* security:
* - ApiKeyAuth: []
* tags:
* - resources
* responses:
+4
View File
@@ -11,6 +11,8 @@ import { schemaPaymentBodyParams, schemaPaymentPublic } from "@lib/validations/p
* /v1/payments:
* get:
* summary: Get all payments
* security:
* - ApiKeyAuth: []
* tags:
* - payments
* responses:
@@ -22,6 +24,8 @@ import { schemaPaymentBodyParams, schemaPaymentPublic } from "@lib/validations/p
* description: No payments were found
* post:
* summary: Creates a new payment
* security:
* - ApiKeyAuth: []
* tags:
* - payments
* responses:
+2
View File
@@ -18,6 +18,8 @@ import { schemaResourceBodyParams, schemaResourcePublic, withValidResource } fro
* application/json:
* schema:
* $ref: '#/components/schemas/Resource'
* security:
* - ApiKeyAuth: []
* tags:
* - resources
* responses: