From d39bff7208328265e4ba37b16b5eb13039697a9f Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Wed, 18 Dec 2024 12:16:52 +0100 Subject: [PATCH 1/5] chore: use outer refs in WithPermission() callbacks This makes code simpler and less error prone. --- examples/gno.land/r/demo/boards2/public.gno | 30 ++++----------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/examples/gno.land/r/demo/boards2/public.gno b/examples/gno.land/r/demo/boards2/public.gno index 9820c45499f..dd1c37cf412 100644 --- a/examples/gno.land/r/demo/boards2/public.gno +++ b/examples/gno.land/r/demo/boards2/public.gno @@ -24,14 +24,11 @@ func CreateBoard(name string) BoardID { caller := std.GetOrigCaller() id := incGetBoardID() args := Args{name, id} - gPerm.WithPermission(caller, PermissionBoardCreate, args, func(a Args) { - // TODO: Do the callback really need the args or we could have the same result directly referencing? - name := a[0].(string) + gPerm.WithPermission(caller, PermissionBoardCreate, args, func(Args) { if gBoardsByName.Has(name) { panic("board already exists") } - id := a[1].(BoardID) board := newBoard(id, name, caller) gBoardsByID.Set(id.Key(), board) gBoardsByName.Set(name, board) @@ -106,11 +103,8 @@ func DeleteThread(bid BoardID, threadID PostID) { caller := std.GetOrigCaller() args := Args{bid, threadID} - gPerm.WithPermission(caller, PermissionThreadDelete, args, func(a Args) { - bid := a[0].(BoardID) + gPerm.WithPermission(caller, PermissionThreadDelete, args, func(Args) { board := mustGetBoard(bid) - - threadID := a[1].(PostID) board.DeleteThread(threadID) }) } @@ -128,13 +122,8 @@ func DeleteReply(bid BoardID, threadID, replyID PostID) { caller := std.GetOrigCaller() args := Args{bid, threadID, replyID} gPerm.WithPermission(caller, PermissionReplyDelete, args, func(a Args) { - bid := a[0].(BoardID) board := mustGetBoard(bid) - - threadID := a[1].(PostID) thread := mustGetThread(board, threadID) - - replyID := a[2].(PostID) thread.DeleteReply(replyID) }) } @@ -147,15 +136,9 @@ func EditThread(bid BoardID, threadID PostID, title, body string) { caller := std.GetOrigCaller() args := Args{bid, threadID, title, body} - gPerm.WithPermission(caller, PermissionThreadEdit, args, func(a Args) { - bid := a[0].(BoardID) + gPerm.WithPermission(caller, PermissionThreadEdit, args, func(Args) { board := mustGetBoard(bid) - - threadID := a[1].(PostID) thread := mustGetThread(board, threadID) - - title := a[2].(string) - body := a[3].(string) thread.Update(title, body) }) } @@ -180,9 +163,7 @@ func InviteMember(user std.Address, role Role) { caller := std.GetOrigCaller() args := Args{user, role} - gPerm.WithPermission(caller, PermissionMemberInvite, args, func(a Args) { - user := a[0].(std.Address) - role := a[1].(Role) + gPerm.WithPermission(caller, PermissionMemberInvite, args, func(Args) { if err := gPerm.AddUser(user, role); err != nil { panic(err) } @@ -193,8 +174,7 @@ func RemoveMember(user std.Address) { assertIsUserCall() caller := std.GetOrigCaller() - gPerm.WithPermission(caller, PermissionMemberRemove, Args{user}, func(a Args) { - user := a[0].(std.Address) + gPerm.WithPermission(caller, PermissionMemberRemove, Args{user}, func(Args) { if !gPerm.RemoveUser(user) { panic("member not found") } From 51d4a8c17d8d2749f9822be9733ac1c46d50936d Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Fri, 20 Dec 2024 11:23:08 +0100 Subject: [PATCH 2/5] chore: rename Permissioner interface to Permissions --- examples/gno.land/r/demo/boards2/boards.gno | 2 +- examples/gno.land/r/demo/boards2/permission.gno | 4 ++-- examples/gno.land/r/demo/boards2/permission_default.gno | 4 ++-- examples/gno.land/r/demo/boards2/permission_default_test.gno | 2 +- examples/gno.land/r/demo/boards2/permission_handlers.gno | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/gno.land/r/demo/boards2/boards.gno b/examples/gno.land/r/demo/boards2/boards.gno index 53072e37542..9d5d0d4b0b4 100644 --- a/examples/gno.land/r/demo/boards2/boards.gno +++ b/examples/gno.land/r/demo/boards2/boards.gno @@ -3,7 +3,7 @@ package boards2 import "gno.land/p/demo/avl" var ( - gPerm Permissioner // TODO: Support changing the permissioner + gPerm Permissions // TODO: Support assigning a different implementation gLastBoardID BoardID gBoardsByID avl.Tree // string(id) -> *Board gBoardsByName avl.Tree // string(name) -> *Board diff --git a/examples/gno.land/r/demo/boards2/permission.gno b/examples/gno.land/r/demo/boards2/permission.gno index 0fc5dc9515d..d287b4f8e8b 100644 --- a/examples/gno.land/r/demo/boards2/permission.gno +++ b/examples/gno.land/r/demo/boards2/permission.gno @@ -29,8 +29,8 @@ type ( // Args is a list of generic arguments. Args []interface{} - // Permissioner define an interface to for permissioned execution. - Permissioner interface { + // Permissions define an interface to for permissioned execution. + Permissions interface { // HasRole checks if a user has a specific role assigned. HasRole(std.Address, Role) bool diff --git a/examples/gno.land/r/demo/boards2/permission_default.gno b/examples/gno.land/r/demo/boards2/permission_default.gno index fc8efee14d2..87776e253bd 100644 --- a/examples/gno.land/r/demo/boards2/permission_default.gno +++ b/examples/gno.land/r/demo/boards2/permission_default.gno @@ -12,7 +12,7 @@ type ( // PermissionsHandlerFunc defines a function to handle permission callbacks. // Handlers are called by the `WithPermission()` method to execute callbacks // when users have the permission assigned. - PermissionsHandlerFunc func(Permissioner, Args, func(Args)) + PermissionsHandlerFunc func(Permissions, Args, func(Args)) // DefaultPermissions manages users, roles and permissions. DefaultPermissions struct { @@ -151,7 +151,7 @@ func createDefaultPermissions() *DefaultPermissions { // TODO: Define and change the default realm owner (or owners) owner := std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") // @test1 // TODO: DAO should be a different realm or proposal and voting functions should be part of boards realm? - // Permissions and DAO mechanics should be discussed and improved. Add `GetDAO()` to `Permissioner`?? + // Permissions and DAO mechanics should be discussed and improved. Add `GetDAO()` to `Permissions`?? dao := admindao.New(admindao.WithMember(owner)) perms := NewDefaultPermissions( dao, diff --git a/examples/gno.land/r/demo/boards2/permission_default_test.gno b/examples/gno.land/r/demo/boards2/permission_default_test.gno index bf64018c11a..a3c51fb6abe 100644 --- a/examples/gno.land/r/demo/boards2/permission_default_test.gno +++ b/examples/gno.land/r/demo/boards2/permission_default_test.gno @@ -9,7 +9,7 @@ import ( "gno.land/p/demo/urequire" ) -var _ Permissioner = (*DefaultPermissions)(nil) +var _ Permissions = (*DefaultPermissions)(nil) func TestNewDefaultPermissions(t *testing.T) { roles := []Role{"a", "b"} diff --git a/examples/gno.land/r/demo/boards2/permission_handlers.gno b/examples/gno.land/r/demo/boards2/permission_handlers.gno index 9c6a24b90f0..63b351f1d53 100644 --- a/examples/gno.land/r/demo/boards2/permission_handlers.gno +++ b/examples/gno.land/r/demo/boards2/permission_handlers.gno @@ -6,7 +6,7 @@ import ( "gno.land/r/demo/users" ) -func handleBoardCreate(_ Permissioner, args Args, cb func(Args)) { +func handleBoardCreate(_ Permissions, args Args, cb func(Args)) { // TODO: This way of dealing with arguments is delicate, ideally types should be used name := args[0].(string) if std.Address(name).IsValid() { @@ -24,7 +24,7 @@ func handleBoardCreate(_ Permissioner, args Args, cb func(Args)) { cb(args) } -func handleMemberInvite(p Permissioner, args Args, cb func(Args)) { +func handleMemberInvite(p Permissions, args Args, cb func(Args)) { // Make sure that only owners invite other owners role := args[1].(Role) if role == RoleOwner { From fbd238970b570ba6828d3db5604e7431b3dcca26 Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Fri, 20 Dec 2024 11:44:39 +0100 Subject: [PATCH 3/5] feat: add permissions support to Board type This is a temporary implementation to unblock other features, it will provably change once discussed. It needs to be better defined. --- examples/gno.land/r/demo/boards2/board.gno | 20 +++++++++++++++++++ examples/gno.land/r/demo/boards2/boards.gno | 5 ++++- .../r/demo/boards2/permission_default.gno | 9 ++------- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/examples/gno.land/r/demo/boards2/board.gno b/examples/gno.land/r/demo/boards2/board.gno index ee1b548aa0f..6c9f817026d 100644 --- a/examples/gno.land/r/demo/boards2/board.gno +++ b/examples/gno.land/r/demo/boards2/board.gno @@ -28,6 +28,7 @@ type Board struct { postsCtr uint64 // increments Post.id createdAt time.Time deleted avl.Tree // TODO reserved for fast-delete. + perms Permissions } func newBoard(id BoardID, name string, creator std.Address) *Board { @@ -38,6 +39,7 @@ func newBoard(id BoardID, name string, creator std.Address) *Board { threads: avl.Tree{}, createdAt: time.Now(), deleted: avl.Tree{}, + perms: createDefaultBoardPermissions(creator), } } @@ -64,6 +66,10 @@ func (board *Board) GetURL() string { return strings.TrimPrefix(std.CurrentRealm().PkgPath(), "gno.land") + ":" + board.name } +func (board *Board) GetPermissions() Permissions { + return board.perms +} + func (board *Board) GetThread(threadID PostID) (_ *Post, found bool) { v, found := board.threads.Get(threadID.Key()) if !found { @@ -116,3 +122,17 @@ func (board *Board) GetURLFromReplyID(threadID, replyID PostID) string { func (board *Board) GetPostFormURL() string { return txlink.URL("CreateThread", "bid", board.id.String()) } + +// TODO: This is a temporary implementation until the permissions and DAO mecahnics are defined +func createDefaultBoardPermissions(owner std.Address) *DefaultPermissions { + perms := NewDefaultPermissions( + admindao.New(admindao.WithMember(owner)), + WithSuperRole(RoleOwner), + WithRole(RoleAdmin, PermissionMemberInvite), + // TODO: Finish assigning all roles and permissions + // WithRole(RoleModerator, permissions...), + WithUser(owner, RoleOwner), + ) + perms.HandleFunc(PermissionMemberInvite, handleMemberInvite) + return perms +} diff --git a/examples/gno.land/r/demo/boards2/boards.gno b/examples/gno.land/r/demo/boards2/boards.gno index 9d5d0d4b0b4..56ec7b6d146 100644 --- a/examples/gno.land/r/demo/boards2/boards.gno +++ b/examples/gno.land/r/demo/boards2/boards.gno @@ -10,8 +10,11 @@ var ( ) func init() { + // TODO: Define and change the default realm owner (or owners) + owner := std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") // @test1 + // Initialize the default realm permissions - gPerm = createDefaultPermissions() + gPerm = createDefaultPermissions(owner) } // incGetBoardID returns a new board ID. diff --git a/examples/gno.land/r/demo/boards2/permission_default.gno b/examples/gno.land/r/demo/boards2/permission_default.gno index 87776e253bd..b4558f82bcc 100644 --- a/examples/gno.land/r/demo/boards2/permission_default.gno +++ b/examples/gno.land/r/demo/boards2/permission_default.gno @@ -147,23 +147,18 @@ func (dp *DefaultPermissions) WithPermission(user std.Address, perm Permission, fn(dp, args, cb) } -func createDefaultPermissions() *DefaultPermissions { - // TODO: Define and change the default realm owner (or owners) - owner := std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") // @test1 +func createDefaultPermissions(owner std.Address) *DefaultPermissions { // TODO: DAO should be a different realm or proposal and voting functions should be part of boards realm? // Permissions and DAO mechanics should be discussed and improved. Add `GetDAO()` to `Permissions`?? - dao := admindao.New(admindao.WithMember(owner)) perms := NewDefaultPermissions( - dao, + admindao.New(admindao.WithMember(owner)), WithSuperRole(RoleOwner), WithRole(RoleAdmin, PermissionBoardCreate, PermissionMemberInvite), // TODO: Finish assigning all roles and permissions // WithRole(RoleModerator, permissions...), WithUser(owner, RoleOwner), ) - perms.HandleFunc(PermissionBoardCreate, handleBoardCreate) perms.HandleFunc(PermissionMemberInvite, handleMemberInvite) - return perms } From 69cd7d640f3c83e8799eddfe4aee4f57469d5b78 Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Fri, 20 Dec 2024 11:48:44 +0100 Subject: [PATCH 4/5] chore: fix imports --- examples/gno.land/r/demo/boards2/board.gno | 2 ++ examples/gno.land/r/demo/boards2/boards.gno | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/r/demo/boards2/board.gno b/examples/gno.land/r/demo/boards2/board.gno index 980b2157039..1920cd41225 100644 --- a/examples/gno.land/r/demo/boards2/board.gno +++ b/examples/gno.land/r/demo/boards2/board.gno @@ -8,6 +8,8 @@ import ( "gno.land/p/demo/avl" "gno.land/p/moul/txlink" + + "gno.land/p/demo/boards2/admindao" ) type BoardID uint64 diff --git a/examples/gno.land/r/demo/boards2/boards.gno b/examples/gno.land/r/demo/boards2/boards.gno index 56ec7b6d146..70e02d5dffe 100644 --- a/examples/gno.land/r/demo/boards2/boards.gno +++ b/examples/gno.land/r/demo/boards2/boards.gno @@ -1,6 +1,10 @@ package boards2 -import "gno.land/p/demo/avl" +import ( + "std" + + "gno.land/p/demo/avl" +) var ( gPerm Permissions // TODO: Support assigning a different implementation From 956a8240fa1296b579abae0ac214c112929d740c Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Fri, 20 Dec 2024 11:51:59 +0100 Subject: [PATCH 5/5] chore: add `assertHasBoardPermission()` assert --- examples/gno.land/r/demo/boards2/public.gno | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/gno.land/r/demo/boards2/public.gno b/examples/gno.land/r/demo/boards2/public.gno index dd1c37cf412..083f8daff65 100644 --- a/examples/gno.land/r/demo/boards2/public.gno +++ b/examples/gno.land/r/demo/boards2/public.gno @@ -193,6 +193,12 @@ func assertHasPermission(user std.Address, p Permission) { } } +func assertHasBoardPermission(b *Board, user std.Address, p Permission) { + if !b.perms.HasPermission(user, p) { + panic("unauthorized") + } +} + func assertBoardExists(id BoardID) { if _, found := getBoard(id); !found { panic("board not found: " + id.String())