Skip to content
This repository has been archived by the owner on Feb 10, 2025. It is now read-only.

Commit

Permalink
Working on contributor permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
tomgobich committed Jul 17, 2023
1 parent 6bf3b54 commit b44fe42
Show file tree
Hide file tree
Showing 27 changed files with 582 additions and 70 deletions.
4 changes: 3 additions & 1 deletion .adonisrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
"./start/kernel",
"./start/bouncer",
"./start/events",
"./start/globals"
"./start/globals",
"./start/events/role",
"./start/events/auth"
],
"providers": [
"./providers/AppProvider",
Expand Down
2 changes: 1 addition & 1 deletion app/Controllers/Http/AuthController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default class AuthController {
const loginAttemptsRemaining = await AuthAttemptService.getRemainingAttempts(uid)
if (loginAttemptsRemaining <= 0) {
session.flash('error', 'Your account has been locked due to repeated bad login attempts. Please reset your password.')
return response.redirect('/forgot-password')
return response.redirect('https://adocasts.com/forgot-password')
}

try {
Expand Down
2 changes: 1 addition & 1 deletion app/Controllers/Http/SettingsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export default class SettingsController {
session.flash('success', 'Your email has been successfully reverted. Please resecure your account by changing your password.')
return signedUrl
? response.redirect(signedUrl)
: response.redirect().toRoute('auth.password.forgot')
: response.redirect('https://adocasts.com/forgot-password')
}

public async usernameUpdate({ request, response, auth, session }: HttpContextContract) {
Expand Down
28 changes: 22 additions & 6 deletions app/Controllers/Http/TaxonomiesController.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Taxonomy from "App/Models/Taxonomy";
import TaxonomyValidator from "App/Validators/TaxonomyValidator";
import TaxonomyValidator from "App/Validators/TaxonomyValidator"
import TaxonomyService from 'App/Services/TaxonomyService'
import CacheService from 'App/Services/CacheService'
import AssetService from 'App/Services/AssetService';
import AssetService from 'App/Services/AssetService'
import Database from '@ioc:Adonis/Lucid/Database'
import Logger from '@ioc:Logger/Discord'

export default class TaxonomiesController {
public async index({ view, bouncer }: HttpContextContract) {
Expand All @@ -29,12 +31,12 @@ export default class TaxonomiesController {
})
}

public async store({ request, response, bouncer }: HttpContextContract) {
public async store({ request, response, bouncer, auth }: HttpContextContract) {
await bouncer.with('TaxonomyPolicy').authorize('create')

const { postIds, ...data } = await request.validate(TaxonomyValidator)

const taxonomy = await Taxonomy.create(data)
const taxonomy = await Taxonomy.create({ ...data, ownerId: auth.user!.id })

await TaxonomyService.syncPosts(taxonomy, postIds)

Expand Down Expand Up @@ -79,9 +81,23 @@ export default class TaxonomiesController {

const flatChildren = await TaxonomyService.getFlatChildren(taxonomy.id)
const flatChildrenIds = flatChildren.reverse().map(c => c.id)
const trx = await Database.transaction()

try {

taxonomy.useTransaction(trx)

await trx.from('post_taxonomies').whereIn('taxonomy_id', [...flatChildrenIds, taxonomy.id]).delete()
await Taxonomy.query({ client: trx }).whereIn('id', flatChildrenIds).delete()
await taxonomy.delete()

await trx.commit()
} catch (error) {
await trx.rollback()
Logger.error('TaxonomiesController.destroy', error.message)
throw new Error(error.message)
}

await Taxonomy.query().whereIn('id', flatChildrenIds).delete()
await taxonomy.delete()
await CacheService.clearForTaxonomy(taxonomy.slug)

return response.redirect().toRoute('studio.taxonomies.index')
Expand Down
7 changes: 7 additions & 0 deletions app/Controllers/Http/UsersController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import User from 'App/Models/User'
import Route from '@ioc:Adonis/Core/Route'
import Role from 'App/Models/Role'
import RoleEnum from 'App/Enums/Roles'
import UserRoleValidator from 'App/Validators/UserRoleValidator'
import Event from '@ioc:Adonis/Core/Event'
import UserService from 'App/Services/UserService'

export default class UsersController {
public async index({ view, request, bouncer }: HttpContextContract) {
Expand Down Expand Up @@ -42,11 +45,15 @@ export default class UsersController {

const data = await request.validate(UserRoleValidator)
const user = await User.findOrFail(params.id)
const oldRole = await user.related('role').query().firstOrFail()
const newRole = await Role.findOrFail(data.roleId)

await user.merge(data).save()

session.flash('success', 'Role updated successfully')

await UserService.sendRoleUpdateNotification(user, newRole, oldRole)

return response.redirect().toRoute('studio.users.show', { id: params.id })
}

Expand Down
5 changes: 4 additions & 1 deletion app/Enums/Roles.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
enum Role {
USER = 1,
ADMIN = 2,
CONTRIBUTOR = 3
CONTRIBUTOR_LVL_1 = 3, // can only create, edit, delete posts they create
CONTRIBUTOR_LVL_2 = 4, // can also create series & taxonomies & sync taxonomy lessons
}

export const RoleWeights = [Role.USER, Role.CONTRIBUTOR_LVL_1, Role.CONTRIBUTOR_LVL_2, Role.ADMIN]

export default Role;
8 changes: 6 additions & 2 deletions app/Policies/BasePolicy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ export default class BasePolicy extends BouncerBasePolicy {
return user?.roleId === Role.ADMIN
}

protected canContribute(user: User | null) {
return this.isAdmin(user) || user?.roleId === Role.CONTRIBUTOR
protected isLvl1Contributor(user: User | null) {
return this.isAdmin(user) || user?.roleId === Role.CONTRIBUTOR_LVL_1 || user?.roleId === Role.CONTRIBUTOR_LVL_2
}

protected isLvl2Contributor(user: User | null) {
return this.isAdmin(user) || user?.roleId === Role.CONTRIBUTOR_LVL_2
}

protected isAuthenticated(user: User | null) {
Expand Down
10 changes: 5 additions & 5 deletions app/Policies/CollectionPolicy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,31 @@ export default class CollectionPolicy extends BasePolicy {
}

public async viewList(user: User) {
return this.canContribute(user)
return this.isLvl2Contributor(user)
}

public async view(user: User, _collection: Collection) {
return this.canContribute(user)
return this.isLvl2Contributor(user)
}

public async feature(_user: User) {
return false
}

public async create(user: User) {
return this.canContribute(user)
return this.isLvl2Contributor(user)
}

public async update(user: User, _collection: Collection) {
return this.canContribute(user)
return this.isLvl2Contributor(user)
}

public async delete(user: User, collection: Collection) {
return this.isOwner(user, collection)
}

public async isOwner(user: User, collection: Collection) {
if (!collection) return this.canContribute(user)
if (!collection) return this.isLvl2Contributor(user)
return collection.ownerId === user.id
}
}
4 changes: 2 additions & 2 deletions app/Policies/PostPolicy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default class PostPolicy extends BasePolicy {
}

public async viewList(user: User) {
return this.canContribute(user)
return this.isLvl1Contributor(user)
}

public async view(user: User, post: Post) {
Expand All @@ -19,7 +19,7 @@ export default class PostPolicy extends BasePolicy {
}

public async store(user: User) {
return this.canContribute(user)
return this.isLvl1Contributor(user)
}

public async update(user: User, post: Post) {
Expand Down
2 changes: 1 addition & 1 deletion app/Policies/StudioPolicy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default class StudioPolicy extends BasePolicy {
}

public async viewDashboard(user: User) {
return this.canContribute(user)
return this.isLvl1Contributor(user)
}

public async viewPosts(_: User) {
Expand Down
10 changes: 5 additions & 5 deletions app/Policies/TaxonomyPolicy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,31 @@ export default class TaxonomyPolicy extends BasePolicy {
}

public async viewList(user: User) {
return this.canContribute(user)
return this.isLvl2Contributor(user)
}

public async view(user: User, _taxonomy: Taxonomy) {
return this.canContribute(user)
return this.isLvl2Contributor(user)
}

public async feature(_user: User) {
return false
}

public async create(user: User) {
return this.canContribute(user)
return this.isLvl2Contributor(user)
}

public async update(user: User, _taxonomy: Taxonomy) {
return this.canContribute(user)
return this.isLvl2Contributor(user)
}

public async delete(user: User, taxonomy: Taxonomy) {
return this.isOwner(user, taxonomy)
}

public async isOwner(user: User, taxonomy: Taxonomy) {
if (!taxonomy) return this.canContribute(user)
if (!taxonomy) return this.isLvl2Contributor(user)
return taxonomy.ownerId === user.id
}
}
2 changes: 1 addition & 1 deletion app/Services/SettingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default class SettingService {
return {
success: false,
message: "You've exceeded the maximum number of password attempts allowed. Please reset your password.",
redirect: Route.makeUrl('auth.password.forgot')
redirect: 'https://adocasts.com/forgot-password'
}
}

Expand Down
21 changes: 21 additions & 0 deletions app/Services/UserService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import RoleEnum, { RoleWeights} from 'App/Enums/Roles'
import Role from 'App/Models/Role'
import Event from '@ioc:Adonis/Core/Event'

export default class UserService {
public static async sendRoleUpdateNotification(user: User, newRole: Role, oldRole: Role) {
const oldRoleWeight = RoleWeights.findIndex(id => id === oldRole.id)
const newRoleWeight = RoleWeights.findIndex(id => id === newRole.id)
console.log({ oldRoleWeight, newRoleWeight })
if (oldRoleWeight >= newRoleWeight) return

switch (newRole.id) {
case RoleEnum.CONTRIBUTOR_LVL_1:
return Event.emit('role:upgrade:contributor_lvl_1', { user, newRole, oldRole })
case RoleEnum.CONTRIBUTOR_LVL_2:
return Event.emit('role:upgrade:contributor_lvl_2', { user, newRole, oldRole })
case RoleEnum.ADMIN:
return Event.emit('role:upgrade:admin', { user, newRole, oldRole })
}
}
}
16 changes: 8 additions & 8 deletions public/assets/entrypoints.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,40 @@
"entrypoints": {
"app": {
"css": [
"http://localhost:58020/assets/app.css"
"http://localhost:63705/assets/app.css"
],
"js": [
"http://localhost:58020/assets/app.js"
"http://localhost:63705/assets/app.js"
]
},
"post": {
"js": [
"http://localhost:58020/assets/post.js"
"http://localhost:63705/assets/post.js"
]
},
"stream": {
"js": [
"http://localhost:58020/assets/stream.js"
"http://localhost:63705/assets/stream.js"
]
},
"file_manager": {
"js": [
"http://localhost:58020/assets/file_manager.js"
"http://localhost:63705/assets/file_manager.js"
]
},
"tiptap_basic": {
"js": [
"http://localhost:58020/assets/tiptap_basic.js"
"http://localhost:63705/assets/tiptap_basic.js"
]
},
"studio.posts.editor": {
"js": [
"http://localhost:58020/assets/studio.posts.editor.js"
"http://localhost:63705/assets/studio.posts.editor.js"
]
},
"studio.collections": {
"js": [
"http://localhost:58020/assets/studio.collections.js"
"http://localhost:63705/assets/studio.collections.js"
]
}
}
Expand Down
16 changes: 8 additions & 8 deletions public/assets/manifest.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"assets/app.css": "http://localhost:58020/assets/app.css",
"assets/app.js": "http://localhost:58020/assets/app.js",
"assets/post.js": "http://localhost:58020/assets/post.js",
"assets/stream.js": "http://localhost:58020/assets/stream.js",
"assets/file_manager.js": "http://localhost:58020/assets/file_manager.js",
"assets/tiptap_basic.js": "http://localhost:58020/assets/tiptap_basic.js",
"assets/studio.posts.editor.js": "http://localhost:58020/assets/studio.posts.editor.js",
"assets/studio.collections.js": "http://localhost:58020/assets/studio.collections.js"
"assets/app.css": "http://localhost:63705/assets/app.css",
"assets/app.js": "http://localhost:63705/assets/app.js",
"assets/post.js": "http://localhost:63705/assets/post.js",
"assets/stream.js": "http://localhost:63705/assets/stream.js",
"assets/file_manager.js": "http://localhost:63705/assets/file_manager.js",
"assets/tiptap_basic.js": "http://localhost:63705/assets/tiptap_basic.js",
"assets/studio.posts.editor.js": "http://localhost:63705/assets/studio.posts.editor.js",
"assets/studio.collections.js": "http://localhost:63705/assets/studio.collections.js"
}
2 changes: 1 addition & 1 deletion resources/views/auth/signin.edge
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
</div>

<div class="text-sm">
<a href="/forgot-password" class="font-medium text-brand-800 hover:text-brand-900 dark:text-brand-600 dark:hover:text-brand-700">
<a href="https://adocasts.com/forgot-password" class="font-medium text-brand-800 hover:text-brand-900 dark:text-brand-600 dark:hover:text-brand-700">
Forgot your password?
</a>
</div>
Expand Down
25 changes: 25 additions & 0 deletions resources/views/components/emails/button.edge
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<p
style="
color: #000000;
font-size: 16px;
mso-line-height-rule: exactly;
line-height: 24px;
font-family: Arial, sans-serif;
"
>
<a
href="{{ href }}"
style="
font-size: 16px;
mso-line-height-rule: exactly;
line-height: 24px;
font-family: Arial, sans-serif;
text-decoration: underline;
font-weight: bold;
color: #0000ff;
"
>
{{{ await $slots.main() }}}
</a
>
</p>
12 changes: 12 additions & 0 deletions resources/views/components/emails/paragraph.edge
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<p
style="
color: #000000;
font-size: 16px;
mso-line-height-rule: exactly;
line-height: 24px;
font-family: Arial, sans-serif;
{{ style }}
"
>
{{{ await $slots.main() }}}
</p>
Loading

0 comments on commit b44fe42

Please sign in to comment.