Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(babel): Fix opentelemetry api wrapping and allow it to be disabled #9298

Merged
merged 8 commits into from
Oct 26, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import gql from 'graphql-tag'

import { createValidatorDirective } from '@redwoodjs/graphql-server'

export const schema = gql`
"""
Use to skip authentication checks and allow public access.
"""
directive @skipAuth on FIELD_DEFINITION
`

const skipAuth = createValidatorDirective(schema, () => {
return
})

export default skipAuth
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { trace as RW_OTEL_WRAPPER_TRACE } from '@opentelemetry/api'
import gql from 'graphql-tag'
import { createValidatorDirective } from '@redwoodjs/graphql-server'
export const schema = gql`
"""
Use to skip authentication checks and allow public access.
"""
directive @skipAuth on FIELD_DEFINITION
`
const skipAuth = createValidatorDirective(schema, () => {
return
})
export default skipAuth
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { DbAuthHandler, DbAuthHandlerOptions } from '@redwoodjs/auth-dbauth-api'

import { db } from 'src/lib/db'

export const handler = async (
event,
context
) => {
const forgotPasswordOptions = {
handler: (user) => {
return user
},

expires: 60 * 60 * 24,

errors: {
usernameNotFound: 'Username not found',
usernameRequired: 'Username is required',
},
}

const loginOptions = {
handler: (user) => {
return user
},

errors: {
usernameOrPasswordMissing: 'Both username and password are required',
usernameNotFound: 'Username ${username} not found',
incorrectPassword: 'Incorrect password for ${username}',
},

expires: 60 * 60 * 24 * 365 * 10,
}

const resetPasswordOptions = {
handler: (_user) => {
return true
},

allowReusedPassword: true,

errors: {
resetTokenExpired: 'resetToken is expired',
resetTokenInvalid: 'resetToken is invalid',
resetTokenRequired: 'resetToken is required',
reusedPassword: 'Must choose a new password',
},
}

const signupOptions = {
handler: ({ username, hashedPassword, salt, userAttributes }) => {
return db.user.create({
data: {
email: username,
hashedPassword: hashedPassword,
salt: salt,
fullName: userAttributes['full-name'],
},
})
},

passwordValidation: (_password) => {
return true
},

errors: {
fieldMissing: '${field} is required',
usernameTaken: 'Username `${username}` already in use',
},
}

const authHandler = new DbAuthHandler(event, context, {
db: db,

authModelAccessor: 'user',

authFields: {
id: 'id',
username: 'email',
hashedPassword: 'hashedPassword',
salt: 'salt',
resetToken: 'resetToken',
resetTokenExpiresAt: 'resetTokenExpiresAt',
},

cookie: {
HttpOnly: true,
Path: '/',
SameSite: 'Strict',
Secure: process.env.NODE_ENV !== 'development',

},

forgotPassword: forgotPasswordOptions,
login: loginOptions,
resetPassword: resetPasswordOptions,
signup: signupOptions,
})

return await authHandler.invoke()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { trace as RW_OTEL_WRAPPER_TRACE } from '@opentelemetry/api'
import { DbAuthHandler, DbAuthHandlerOptions } from '@redwoodjs/auth-dbauth-api'
import { db } from 'src/lib/db'
export const handler = async (event, context) => {
const __handler = async (event, context) => {
const forgotPasswordOptions = {
handler: (user) => {
return user
},
expires: 60 * 60 * 24,
errors: {
usernameNotFound: 'Username not found',
usernameRequired: 'Username is required',
},
}
const loginOptions = {
handler: (user) => {
return user
},
errors: {
usernameOrPasswordMissing: 'Both username and password are required',
usernameNotFound: 'Username ${username} not found',
incorrectPassword: 'Incorrect password for ${username}',
},
expires: 60 * 60 * 24 * 365 * 10,
}
const resetPasswordOptions = {
handler: (_user) => {
return true
},
allowReusedPassword: true,
errors: {
resetTokenExpired: 'resetToken is expired',
resetTokenInvalid: 'resetToken is invalid',
resetTokenRequired: 'resetToken is required',
reusedPassword: 'Must choose a new password',
},
}
const signupOptions = {
handler: ({ username, hashedPassword, salt, userAttributes }) => {
return db.user.create({
data: {
email: username,
hashedPassword: hashedPassword,
salt: salt,
fullName: userAttributes['full-name'],
},
})
},
passwordValidation: (_password) => {
return true
},
errors: {
fieldMissing: '${field} is required',
usernameTaken: 'Username `${username}` already in use',
},
}
const authHandler = new DbAuthHandler(event, context, {
db: db,
authModelAccessor: 'user',
authFields: {
id: 'id',
username: 'email',
hashedPassword: 'hashedPassword',
salt: 'salt',
resetToken: 'resetToken',
resetTokenExpiresAt: 'resetTokenExpiresAt',
},
cookie: {
HttpOnly: true,
Path: '/',
SameSite: 'Strict',
Secure: process.env.NODE_ENV !== 'development',
},
forgotPassword: forgotPasswordOptions,
login: loginOptions,
resetPassword: resetPasswordOptions,
signup: signupOptions,
})
return await authHandler.invoke()
}
const RW_OTEL_WRAPPER_TRACER = RW_OTEL_WRAPPER_TRACE.getTracer('redwoodjs')
const RW_OTEL_WRAPPER_RESULT = await RW_OTEL_WRAPPER_TRACER.startActiveSpan(
'redwoodjs:api:__MOCKED_API_FOLDER__:handler',
async (span) => {
span.setAttribute('code.function', 'handler')
span.setAttribute('code.filepath', '__MOCKED_FILENAME__')
try {
const RW_OTEL_WRAPPER_INNER_RESULT = await __handler(event, context)
span.end()
return RW_OTEL_WRAPPER_INNER_RESULT
} catch (error) {
span.recordException(error)
span.setStatus({
code: 2,
message:
error?.message?.split('\n')[0] ?? error?.toString()?.split('\n')[0],
})
span.end()
throw error
}
}
)
return RW_OTEL_WRAPPER_RESULT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { authDecoder } from '@redwoodjs/auth-dbauth-api'
import { createGraphQLHandler } from '@redwoodjs/graphql-server'

import directives from 'src/directives/**/*.{js,ts}'
import sdls from 'src/graphql/**/*.sdl.{js,ts}'
import services from 'src/services/**/*.{js,ts}'

import { getCurrentUser } from 'src/lib/auth'
import { db } from 'src/lib/db'
import { logger } from 'src/lib/logger'

export const handler = createGraphQLHandler({
authDecoder,
getCurrentUser,
loggerConfig: { logger, options: {} },
directives,
sdls,
services,
onException: () => {
// Disconnect from your database with an unhandled exception.
db.$disconnect()
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { trace as RW_OTEL_WRAPPER_TRACE } from '@opentelemetry/api'
import { authDecoder } from '@redwoodjs/auth-dbauth-api'
import { createGraphQLHandler } from '@redwoodjs/graphql-server'
import directives from 'src/directives/**/*.{js,ts}'
import sdls from 'src/graphql/**/*.sdl.{js,ts}'
import services from 'src/services/**/*.{js,ts}'
import { getCurrentUser } from 'src/lib/auth'
import { db } from 'src/lib/db'
import { logger } from 'src/lib/logger'
export const handler = createGraphQLHandler({
authDecoder,
getCurrentUser,
loggerConfig: {
logger,
options: {},
},
directives,
sdls,
services,
onException: () => {
// Disconnect from your database with an unhandled exception.
db.$disconnect()
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { AuthenticationError, ForbiddenError } from '@redwoodjs/graphql-server'

import { db } from './db'

export const getCurrentUser = async (session) => {
if (!session || typeof session.id !== 'number') {
throw new Error('Invalid session')
}

return await db.user.findUnique({
where: { id: session.id },
select: { id: true, roles: true, email: true },
})
}

export const isAuthenticated = () => {
return !!context.currentUser
}

export const hasRole = (roles) => {
if (!isAuthenticated()) {
return false
}

const currentUserRoles = context.currentUser?.roles

if (typeof roles === 'string') {
if (typeof currentUserRoles === 'string') {
// roles to check is a string, currentUser.roles is a string
return currentUserRoles === roles
} else if (Array.isArray(currentUserRoles)) {
// roles to check is a string, currentUser.roles is an array
return currentUserRoles?.some((allowedRole) => roles === allowedRole)
}
}

if (Array.isArray(roles)) {
if (Array.isArray(currentUserRoles)) {
// roles to check is an array, currentUser.roles is an array
return currentUserRoles?.some((allowedRole) =>
roles.includes(allowedRole)
)
} else if (typeof currentUserRoles === 'string') {
// roles to check is an array, currentUser.roles is a string
return roles.some((allowedRole) => currentUserRoles === allowedRole)
}
}

// roles not found
return false
}

export const requireAuth = ({ roles } = {}) => {
if (!isAuthenticated()) {
throw new AuthenticationError("You don't have permission to do that.")
}

if (roles && !hasRole(roles)) {
throw new ForbiddenError("You don't have access to do that.")
}
}
Loading
Loading