permissions_validators_basic.gno

package boards2

import (
	"errors"

	"gno.land/p/gnoland/boards"

	"gno.land/r/sys/users"
)

// validateBasicBoardCreate validates PermissionBoardCreate.
//
// Expected `args` values:
// 1. Caller address
// 2. Board name
// 3. Board ID
// 4. Is board listed
// 5. Is board open
func validateBasicBoardCreate(perms boards.Permissions, args boards.Args) error {
	caller, ok := args[0].(address)
	if !ok {
		return errors.New("expected a valid caller address")
	}

	name, ok := args[1].(string)
	if !ok {
		return errors.New("expected board name to be a string")
	}

	open, ok := args[4].(bool)
	if !ok {
		return errors.New("expected board open flag to be a boolean")
	}

	if open && !perms.HasRole(caller, RoleOwner) {
		return errors.New("only owners can create open boards")
	}

	if err := checkBoardNameIsNotAddress(name); err != nil {
		return err
	}

	if err := checkBoardNameBelongsToAddress(caller, name); err != nil {
		return err
	}
	return nil
}

// validateBasicBoardRename validates PermissionBoardRename.
//
// Expected `args` values:
// 1. Caller address
// 2. Board ID
// 3. Current board name
// 4. New board name
func validateBasicBoardRename(_ boards.Permissions, args boards.Args) error {
	caller, ok := args[0].(address)
	if !ok {
		return errors.New("expected a valid caller address")
	}

	newName, ok := args[3].(string)
	if !ok {
		return errors.New("expected new board name to be a string")
	}

	if err := checkBoardNameIsNotAddress(newName); err != nil {
		return err
	}

	if err := checkBoardNameBelongsToAddress(caller, newName); err != nil {
		return err
	}
	return nil
}

// validateBasicMemberInvite validates PermissionMemberInvite.
//
// Expected `args` values:
// 1. Caller address
// 2. Board ID
// 3. Invites
func validateBasicMemberInvite(perms boards.Permissions, args boards.Args) error {
	caller, ok := args[0].(address)
	if !ok {
		return errors.New("expected a valid caller address")
	}

	invites, ok := args[2].([]Invite)
	if !ok {
		return errors.New("expected valid user invites")
	}

	// Make sure that only owners invite other owners
	callerIsOwner := perms.HasRole(caller, RoleOwner)
	for _, v := range invites {
		if v.Role == RoleOwner && !callerIsOwner {
			return errors.New("only owners are allowed to invite other owners")
		}
	}
	return nil
}

// validateBasicRoleChange validates PermissionRoleChange.
//
// Expected `args` values:
// 1. Caller address
// 2. Board ID
// 3. Member address
// 4. Role
func validateBasicRoleChange(perms boards.Permissions, args boards.Args) error {
	caller, ok := args[0].(address)
	if !ok {
		return errors.New("expected a valid caller address")
	}

	// Owners and Admins can change roles.
	// Admins should not be able to assign or remove the Owner role from members.
	if perms.HasRole(caller, RoleAdmin) {
		role, ok := args[3].(boards.Role)
		if !ok {
			return errors.New("expected a valid member role")
		}

		if role == RoleOwner {
			return errors.New("admins are not allowed to promote members to Owner")
		} else {
			member, ok := args[2].(address)
			if !ok {
				return errors.New("expected a valid member address")
			}

			if perms.HasRole(member, RoleOwner) {
				return errors.New("admins are not allowed to remove the Owner role")
			}
		}
	}
	return nil
}

func checkBoardNameIsNotAddress(s string) error {
	if address(s).IsValid() {
		return errors.New("addresses are not allowed as board name")
	}
	return nil
}

func checkBoardNameBelongsToAddress(owner address, name string) error {
	// When the board name is the name of a registered user
	// check that caller is the owner of the name.
	user, _ := users.ResolveName(name)
	if user != nil && user.Addr() != owner {
		return errors.New("board name is a user name registered to a different user")
	}
	return nil
}