Skip to content

Commit 364410a

Browse files
committed
Authz
1 parent 158ccdb commit 364410a

File tree

6 files changed

+122
-39
lines changed

6 files changed

+122
-39
lines changed

app/access/access.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ func IsValidAccessToken(token string) bool {
5656
type Role string
5757

5858
const (
59-
Role_Admin = "admin"
60-
Role_Edit = "edit"
61-
Role_View = "view"
59+
Role_Admin Role = "admin"
60+
Role_Edit Role = "edit"
61+
Role_View Role = "view"
6262
)
6363

6464
func GetTokenRole(space *db.Space, token string) (Role, bool) {

app/access/authz.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package access
2+
3+
type Action string
4+
5+
const (
6+
Action_UpdateSpace Action = "update_space"
7+
Action_DeleteSpace Action = "delete_space"
8+
Action_ViewTokens Action = "view_tokens"
9+
Action_UpdateTokens Action = "updated_tokens"
10+
Action_CreateMember Action = "create_member"
11+
Action_UpdateMember Action = "update_member"
12+
Action_DeleteMember Action = "delete_member"
13+
Action_CreateNote Action = "create_note"
14+
Action_UpdateNote Action = "update_note"
15+
Action_DeleteNote Action = "delete_note"
16+
)
17+
18+
var rolePermissions = map[Role]map[Action]bool{
19+
Role_Admin: {
20+
Action_UpdateSpace: true,
21+
Action_DeleteSpace: true,
22+
Action_ViewTokens: true,
23+
Action_UpdateTokens: true,
24+
Action_CreateMember: true,
25+
Action_UpdateMember: true,
26+
Action_DeleteMember: true,
27+
Action_CreateNote: true,
28+
Action_UpdateNote: true,
29+
Action_DeleteNote: true,
30+
},
31+
Role_Edit: {
32+
Action_CreateNote: true,
33+
Action_UpdateNote: true,
34+
Action_DeleteNote: true,
35+
Action_UpdateMember: true,
36+
},
37+
Role_View: {
38+
// This role can only view resources
39+
},
40+
}
41+
42+
func (a *Access) Can(action Action) bool {
43+
permissions, exists := rolePermissions[a.Role]
44+
if !exists {
45+
return false
46+
}
47+
48+
return permissions[action]
49+
}

app/handlers/middlewares.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package handlers
2+
3+
import (
4+
"github.com/nicolashery/simply-shared-notes/app/access"
5+
"github.com/nicolashery/simply-shared-notes/app/rctx"
6+
"net/http"
7+
)
8+
9+
func Authorize(action access.Action) func(http.Handler) http.Handler {
10+
return func(next http.Handler) http.Handler {
11+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
12+
access := rctx.GetAccess(r.Context())
13+
14+
if !access.Can(action) {
15+
http.Error(w, "insufficient permissions", http.StatusForbidden)
16+
return
17+
}
18+
19+
next.ServeHTTP(w, r)
20+
})
21+
}
22+
}

app/handlers/routes.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ package handlers
22

33
import (
44
"database/sql"
5-
"log/slog"
6-
75
"github.com/go-chi/chi/v5"
86
"github.com/gorilla/sessions"
7+
"github.com/nicolashery/simply-shared-notes/app/access"
98
"github.com/nicolashery/simply-shared-notes/app/config"
109
"github.com/nicolashery/simply-shared-notes/app/db"
1110
"github.com/nicolashery/simply-shared-notes/app/rctx"
11+
"log/slog"
1212
)
1313

1414
func RegisterRoutes(r chi.Router, cfg *config.Config, logger *slog.Logger, sqlDB *sql.DB, queries *db.Queries, sessionStore *sessions.CookieStore) {
@@ -32,15 +32,19 @@ func RegisterRoutes(r chi.Router, cfg *config.Config, logger *slog.Logger, sqlDB
3232
r.Use(rctx.FlashCtxMiddleware(logger, sessionStore))
3333

3434
r.Get("/", handleSpacesShow(logger))
35-
r.Get("/settings", handleSpacesEdit(logger))
36-
r.Post("/settings", handleSpacesUpdate(logger, queries, sessionStore))
35+
r.With(Authorize(access.Action_UpdateSpace)).Group(func(r chi.Router) {
36+
r.Get("/settings", handleSpacesEdit(logger))
37+
r.Post("/settings", handleSpacesUpdate(logger, queries, sessionStore))
38+
})
3739

38-
r.Get("/share", handleTokensShow(logger))
40+
r.With(Authorize(access.Action_ViewTokens)).
41+
Get("/share", handleTokensShow(logger))
3942

4043
r.Get("/notes", handleNotesList(logger))
4144

4245
r.Get("/members", handleMembersList(logger))
43-
r.Get("/members/{memberId}/edit", handleMembersEdit(logger))
46+
r.With(Authorize(access.Action_UpdateMember)).
47+
Get("/members/{memberId}/edit", handleMembersEdit(logger))
4448

4549
r.Get("/activity", handleActivityList(logger))
4650
})

app/views/layouts/space.layout.templ

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package layouts
33
import (
44
"fmt"
55
"github.com/go-chi/chi/v5"
6-
"github.com/nicolashery/simply-shared-notes/app/access"
6+
acc "github.com/nicolashery/simply-shared-notes/app/access"
77
"github.com/nicolashery/simply-shared-notes/app/db"
88
"github.com/nicolashery/simply-shared-notes/app/identity"
99
"github.com/nicolashery/simply-shared-notes/app/rctx"
@@ -30,7 +30,7 @@ templ Space() {
3030
}
3131
}
3232

33-
templ navBar(route string, space *db.Space, access *access.Access, identity *identity.Identity) {
33+
templ navBar(route string, space *db.Space, access *acc.Access, identity *identity.Identity) {
3434
<div class="navbar bg-base-100 px-4 border-b-2 border-base-300 mb-6">
3535
<div class="navbar-start">
3636
<div>
@@ -48,7 +48,7 @@ templ navBar(route string, space *db.Space, access *access.Access, identity *ide
4848
</div>
4949
}
5050

51-
templ navMenu(route string, space *db.Space, access *access.Access) {
51+
templ navMenu(route string, space *db.Space, access *acc.Access) {
5252
<div class="drawer">
5353
<input id="nav-drawer" type="checkbox" class="drawer-toggle"/>
5454
<div class="drawer-content">
@@ -159,7 +159,7 @@ templ navMenu(route string, space *db.Space, access *access.Access) {
159159
Activity
160160
</a>
161161
</li>
162-
if access.IsAdmin() {
162+
if access.Can(acc.Action_ViewTokens) {
163163
<li>
164164
<a
165165
href={ templ.URL(fmt.Sprintf("/s/%s/share", access.Token)) }
@@ -182,6 +182,8 @@ templ navMenu(route string, space *db.Space, access *access.Access) {
182182
Share
183183
</a>
184184
</li>
185+
}
186+
if access.Can(acc.Action_UpdateSpace) {
185187
<li>
186188
<a
187189
href={ templ.URL(fmt.Sprintf("/s/%s/settings", access.Token)) }
@@ -211,7 +213,7 @@ templ navMenu(route string, space *db.Space, access *access.Access) {
211213
</div>
212214
}
213215

214-
templ userMenu(access *access.Access, identity *identity.Identity) {
216+
templ userMenu(access *acc.Access, identity *identity.Identity) {
215217
<div class="drawer drawer-end">
216218
<input id="user-drawer" type="checkbox" class="drawer-toggle"/>
217219
<div class="drawer-content">
@@ -240,7 +242,7 @@ templ userMenu(access *access.Access, identity *identity.Identity) {
240242
<div>{ helpers.IdentityName(identity) }</div>
241243
<div class="text-xs font-normal">{ helpers.RoleLabel(access) }</div>
242244
</li>
243-
if !access.IsView() {
245+
if access.Can(acc.Action_UpdateMember) {
244246
<li>
245247
<a
246248
href={ templ.URL(fmt.Sprintf("/s/%s/members/%s/edit", access.Token, identity.Member.PublicID)) }

0 commit comments

Comments
 (0)