Add server side ownership enforcement for profile editing
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
// import { createUserInsecure } from 'src/services/users/users.js'
|
import { createUserInsecure } from 'src/services/users/users.js'
|
||||||
|
import { db } from 'src/lib/db'
|
||||||
|
|
||||||
export const handler = async (req, _context) => {
|
export const handler = async (req, _context) => {
|
||||||
const body = JSON.parse(req.body)
|
const body = JSON.parse(req.body)
|
||||||
@@ -61,12 +62,27 @@ export const handler = async (req, _context) => {
|
|||||||
// image: '',
|
// image: '',
|
||||||
// bio: ''
|
// bio: ''
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
const generateUniqueUserName = async (seed, count = 0) => {
|
||||||
|
const isUnique = !(await db.user.findOne({
|
||||||
|
where: { userName: seed },
|
||||||
|
}))
|
||||||
|
if(isUnique) {
|
||||||
|
return seed
|
||||||
|
}
|
||||||
|
count += 1
|
||||||
|
const newSeed = count === 1 ? `${seed}_${count}` : seed.slice(0,-1) + count
|
||||||
|
return generateUniqueUserName(newSeed, count)
|
||||||
|
}
|
||||||
|
const userNameSeed = email.split('@')[0]
|
||||||
|
const userName = await generateUniqueUserName(userNameSeed) // TODO maybe come up with a better default userName?
|
||||||
const input = {
|
const input = {
|
||||||
email,
|
email,
|
||||||
bio: 'default bio'
|
userName,
|
||||||
// full_name: user.user_metadata.full_name
|
name: user.user_metadata && user.user_metadata.full_name,
|
||||||
|
id: user.id,
|
||||||
}
|
}
|
||||||
// await createUserInsecure({input})
|
await createUserInsecure({input})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
|||||||
31
api/src/lib/owner.js
Normal file
31
api/src/lib/owner.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { AuthenticationError, ForbiddenError, parseJWT } from '@redwoodjs/api'
|
||||||
|
import { db } from 'src/lib/db'
|
||||||
|
|
||||||
|
export const requireOwnership = async ({ id, userName } = {}) => {
|
||||||
|
if (!context.currentUser) {
|
||||||
|
throw new AuthenticationError("You don't have permission to do that.")
|
||||||
|
}
|
||||||
|
if(!id && !userName) {
|
||||||
|
throw new ForbiddenError("You don't have access to do that.")
|
||||||
|
}
|
||||||
|
|
||||||
|
const netlifyUserId = context.currentUser?.sub
|
||||||
|
if(id && id !== netlifyUserId) {
|
||||||
|
throw new ForbiddenError("You don't own this resource.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if(context.currentUser.roles?.includes('admin')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await db.user.findOne({
|
||||||
|
where: { userName },
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(user, 'USER')
|
||||||
|
if(!user) {
|
||||||
|
throw new ForbiddenError("You don't own this resource.")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,35 +1,47 @@
|
|||||||
import { db } from 'src/lib/db'
|
import { db } from 'src/lib/db'
|
||||||
|
import { requireAuth } from 'src/lib/auth'
|
||||||
|
import { requireOwnership } from 'src/lib/owner'
|
||||||
|
|
||||||
export const users = () => {
|
export const users = () => {
|
||||||
|
requireAuth({ role: 'admin' })
|
||||||
return db.user.findMany()
|
return db.user.findMany()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const user = ({ id }) => {
|
export const user = ({ id }) => {
|
||||||
|
requireAuth()
|
||||||
return db.user.findOne({
|
return db.user.findOne({
|
||||||
where: { id },
|
where: { id },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const userName = ({ userName }) => {
|
export const userName = ({ userName }) => {
|
||||||
|
requireAuth()
|
||||||
return db.user.findOne({
|
return db.user.findOne({
|
||||||
where: { userName },
|
where: { userName },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createUser = ({ input }) => {
|
export const createUser = ({ input }) => {
|
||||||
|
requireAuth({ role: 'admin' })
|
||||||
|
createUserInsecure({input})
|
||||||
|
}
|
||||||
|
export const createUserInsecure = ({ input }) => {
|
||||||
return db.user.create({
|
return db.user.create({
|
||||||
data: input,
|
data: input,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateUser = ({ id, input }) => {
|
export const updateUser = ({ id, input }) => {
|
||||||
|
requireAuth()
|
||||||
return db.user.update({
|
return db.user.update({
|
||||||
data: input,
|
data: input,
|
||||||
where: { id },
|
where: { id },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateUserByUserName = ({ userName, input }) => {
|
export const updateUserByUserName = async ({ userName, input }) => {
|
||||||
|
requireAuth()
|
||||||
|
await requireOwnership({userName})
|
||||||
return db.user.update({
|
return db.user.update({
|
||||||
data: input,
|
data: input,
|
||||||
where: { userName },
|
where: { userName },
|
||||||
@@ -37,6 +49,7 @@ export const updateUserByUserName = ({ userName, input }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const deleteUser = ({ id }) => {
|
export const deleteUser = ({ id }) => {
|
||||||
|
requireAuth({ role: 'admin' })
|
||||||
return db.user.delete({
|
return db.user.delete({
|
||||||
where: { id },
|
where: { id },
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const UserProfile = ({user, isEditable, loading, onSave, error}) => {
|
|||||||
<h3 className="text-3xl text-gray-500 font-ropa-sans">Bio:</h3>
|
<h3 className="text-3xl text-gray-500 font-ropa-sans">Bio:</h3>
|
||||||
<div name="description" className="markdown-overrides rounded-lg shadow-md bg-white p-12 my-6 min-h-md">
|
<div name="description" className="markdown-overrides rounded-lg shadow-md bg-white p-12 my-6 min-h-md">
|
||||||
<Editor
|
<Editor
|
||||||
defaultValue={user.bio}
|
defaultValue={user.bio || ''}
|
||||||
readOnly={!isEditable}
|
readOnly={!isEditable}
|
||||||
onChange={(bioFn) => setInput({
|
onChange={(bioFn) => setInput({
|
||||||
...input,
|
...input,
|
||||||
|
|||||||
Reference in New Issue
Block a user