Source Code

;; Access Control Contract
;; Centralized access control for badge issuance and management

(use-trait pausable-trait .pausable-trait.pausable)
(impl-trait .pausable-trait.pausable)
;;
;; Error Codes Used:
;; - u100: ERR-OWNER-ONLY - Action restricted to contract owner
;; - u400: ERR-INSUFFICIENT-PERMISSIONS - Lacks required permissions
;; - u401: ERR-INVALID-ROLE - Role is invalid
;; - u403: ERR-ACCOUNT-SUSPENDED - Account is suspended
;; - u405: ERR-NOT-PLATFORM-ADMIN - Not a platform administrator
;; - u110: ERR-PAUSED - Contract is currently paused

;; Import error codes from centralized error-codes contract
(define-constant ERR-OWNER-ONLY (err u100))
(define-constant ERR-INSUFFICIENT-PERMISSIONS (err u400))
(define-constant ERR-INVALID-ROLE (err u401))
(define-constant ERR-ACCOUNT-SUSPENDED (err u403))
(define-constant ERR-NOT-PLATFORM-ADMIN (err u405))
(define-constant ERR-PAUSED (err u110))

;; Contract constants
(define-constant contract-owner tx-sender)

;; Role definitions
(define-constant ROLE-ADMIN "admin")
(define-constant ROLE-ISSUER "issuer")
(define-constant ROLE-COMMUNITY-ISSUER "community-issuer")
(define-constant ROLE-MODERATOR "moderator")
(define-constant ROLE-MEMBER "member")

;; Pausable state
(define-data-var contract-paused bool false)

;; Global permissions
(define-map global-permissions
  { user: principal }
  { 
    can-create-communities: bool,
    can-issue-badges: bool,
    is-platform-admin: bool,
    suspended: bool
  }
)

;; Community-specific permissions
(define-map community-permissions
  { community-id: uint, user: principal }
  {
    can-issue-badges: bool,
    can-manage-members: bool,
    can-create-templates: bool,
    can-revoke-badges: bool,
    role: (string-ascii 32)
  }
)

;; Permission groups for batch management
(define-map permission-groups
  { group-id: uint }
  {
    name: (string-ascii 64),
    permissions: {
      can-issue-badges: bool,
      can-manage-members: bool,
      can-create-templates: bool,
      can-revoke-badges: bool
    }
  }
)

;; Initialize contract owner with full permissions
(map-set global-permissions
  { user: contract-owner }
  {
    can-create-communities: true,
    can-issue-badges: true,
    is-platform-admin: true,
    suspended: false
  }
)

;; Global permission management
(define-public (set-global-permissions (user principal) (permissions {can-create-communities: bool, can-issue-badges: bool, is-platform-admin: bool, suspended: bool}))
  (begin
    (asserts! (is-platform-admin tx-sender) ERR-NOT-PLATFORM-ADMIN)

    ;; Emit global permissions updated event
    (print {
      event: "global-permissions-updated",
      user: user,
      can-create-communities: (get can-create-communities permissions),
      can-issue-badges: (get can-issue-badges permissions),
      is-platform-admin: (get is-platform-admin permissions),
      suspended: (get suspended permissions),
      updated-by: tx-sender,
      block-height: block-height
    })

    (ok (map-set global-permissions { user: user } permissions))
  )
)

;; Community permission management
(define-public (set-community-permissions (community-id uint) (user principal) (permissions {can-issue-badges: bool, can-manage-members: bool, can-create-templates: bool, can-revoke-badges: bool, role: (string-ascii 32)}))
  (begin
    (asserts! (can-manage-community-members community-id tx-sender) ERR-INSUFFICIENT-PERMISSIONS)

    ;; Emit community permissions updated event
    (print {
      event: "community-permissions-updated",
      community-id: community-id,
      user: user,
      can-issue-badges: (get can-issue-badges permissions),
      can-manage-members: (get can-manage-members permissions),
      can-create-templates: (get can-create-templates permissions),
      can-revoke-badges: (get can-revoke-badges permissions),
      role: (get role permissions),
      updated-by: tx-sender,
      block-height: block-height
    })

    (ok (map-set community-permissions { community-id: community-id, user: user } permissions))
  )
)

;; Community issuer role management
(define-public (grant-community-issuer-role (community-id uint) (user principal))
  (begin
    (asserts! (can-manage-community-members community-id tx-sender) ERR-INSUFFICIENT-PERMISSIONS)
    
    (print {
      event: "community-issuer-granted",
      community-id: community-id,
      user: user,
      granted-by: tx-sender,
      block-height: block-height
    })

    (ok (map-set community-permissions
      { community-id: community-id, user: user }
      {
        can-issue-badges: true,
        can-manage-members: false,
        can-create-templates: false,
        can-revoke-badges: false,
        role: ROLE-COMMUNITY-ISSUER
      }
    ))
  )
)

(define-public (revoke-community-issuer-role (community-id uint) (user principal))
  (begin
    (asserts! (can-manage-community-members community-id tx-sender) ERR-INSUFFICIENT-PERMISSIONS)
    
    (print {
      event: "community-issuer-revoked",
      community-id: community-id,
      user: user,
      revoked-by: tx-sender,
      block-height: block-height
    })

    (ok (map-delete community-permissions { community-id: community-id, user: user }))
  )
)

;; Permission check functions
(define-read-only (is-platform-admin (user principal))
  (default-to false (get is-platform-admin (map-get? global-permissions { user: user })))
)

(define-read-only (is-community-issuer (community-id uint) (user principal))
  (let
    (
      (perms (map-get? community-permissions { community-id: community-id, user: user }))
    )
    (and
      (is-some perms)
      (get can-issue-badges (unwrap-panic perms))
      (is-eq (get role (unwrap-panic perms)) ROLE-COMMUNITY-ISSUER)
    )
  )
)

(define-read-only (can-create-communities (user principal))
  (let
    (
      (perms (map-get? global-permissions { user: user }))
    )
    (and 
      (is-some perms)
      (get can-create-communities (unwrap-panic perms))
      (not (get suspended (unwrap-panic perms)))
    )
  )
)

(define-read-only (can-issue-badges-globally (user principal))
  (let
    (
      (perms (map-get? global-permissions { user: user }))
    )
    (and 
      (is-some perms)
      (get can-issue-badges (unwrap-panic perms))
      (not (get suspended (unwrap-panic perms)))
    )
  )
)

(define-read-only (can-issue-badges-in-community (community-id uint) (user principal))
  (let
    (
      (community-perms (map-get? community-permissions { community-id: community-id, user: user }))
      (global-perms (map-get? global-permissions { user: user }))
    )
    (or
      ;; Has community-specific permission
      (and 
        (is-some community-perms)
        (get can-issue-badges (unwrap-panic community-perms))
      )
      ;; Has global permission and not suspended
      (and
        (is-some global-perms)
        (get can-issue-badges (unwrap-panic global-perms))
        (not (get suspended (unwrap-panic global-perms)))
      )
    )
  )
)

(define-read-only (can-manage-community-members (community-id uint) (user principal))
  (let
    (
      (community-perms (map-get? community-permissions { community-id: community-id, user: user }))
    )
    (or
      (is-platform-admin user)
      (and 
        (is-some community-perms)
        (get can-manage-members (unwrap-panic community-perms))
      )
    )
  )
)

(define-read-only (can-revoke-badges-in-community (community-id uint) (user principal))
  (let
    (
      (community-perms (map-get? community-permissions { community-id: community-id, user: user }))
    )
    (or
      (is-platform-admin user)
      (and 
        (is-some community-perms)
        (get can-revoke-badges (unwrap-panic community-perms))
      )
    )
  )
)

;; Suspend/unsuspend users
(define-public (suspend-user (user principal))
  (let
    (
      (current-perms (default-to
        { can-create-communities: false, can-issue-badges: false, is-platform-admin: false, suspended: false }
        (map-get? global-permissions { user: user })
      ))
    )
    (asserts! (is-platform-admin tx-sender) ERR-NOT-PLATFORM-ADMIN)

    ;; Emit user suspended event
    (print {
      event: "user-suspended",
      user: user,
      suspended-by: tx-sender,
      block-height: block-height
    })

    (ok (map-set global-permissions
      { user: user }
      (merge current-perms { suspended: true })
    ))
  )
)

(define-public (unsuspend-user (user principal))
  (let
    (
      (current-perms (default-to
        { can-create-communities: false, can-issue-badges: false, is-platform-admin: false, suspended: false }
        (map-get? global-permissions { user: user })
      ))
    )
    (asserts! (is-platform-admin tx-sender) ERR-NOT-PLATFORM-ADMIN)

    ;; Emit user unsuspended event
    (print {
      event: "user-unsuspended",
      user: user,
      unsuspended-by: tx-sender,
      block-height: block-height
    })

    (ok (map-set global-permissions
      { user: user }
      (merge current-perms { suspended: false })
    ))
  )
)

;; Pausable management
(define-public (set-paused (paused bool))
  (begin
    (asserts! (is-platform-admin tx-sender) ERR-NOT-PLATFORM-ADMIN)
    
    (print {
      event: "contract-pause-status-changed",
      paused: paused,
      updated-by: tx-sender,
      block-height: block-height
    })

    (ok (var-set contract-paused paused))
  )
)

(define-read-only (is-paused)
  (ok (var-get contract-paused))
)

Functions (14)

FunctionAccessArgs
set-global-permissionspublicuser: principal, permissions: {can-create-communities: bool, can-issue-badges: bool, is-platform-admin: bool, suspended: bool}
grant-community-issuer-rolepubliccommunity-id: uint, user: principal
revoke-community-issuer-rolepubliccommunity-id: uint, user: principal
is-platform-adminread-onlyuser: principal
is-community-issuerread-onlycommunity-id: uint, user: principal
can-create-communitiesread-onlyuser: principal
can-issue-badges-globallyread-onlyuser: principal
can-issue-badges-in-communityread-onlycommunity-id: uint, user: principal
can-manage-community-membersread-onlycommunity-id: uint, user: principal
can-revoke-badges-in-communityread-onlycommunity-id: uint, user: principal
suspend-userpublicuser: principal
unsuspend-userpublicuser: principal
set-pausedpublicpaused: bool
is-pausedread-only