UI はローカライズ済みです。スキルの紹介と説明文書は選択した言語へ AI が自動翻訳し、コード・コマンド・パス・専門用語は原文のままです。原作者の文書が優先されます。
スキルを探す
生産性・共同作業 · SKILL.md

extension-user-approval

extension-user-approval

Approval-based user management.

Independent GitHub snapshot · Automated classification · Original documentation governs

Ccaffeinelabs / skillsApache-2.0

スキルの説明

GitHub

name: extension-user-approval description: Approval-based user management. version: 1.1.0 compatibility: mops: caffeineai-user-approval: "~1.1.0" caffeineai-authorization: "~1.0.0" caffeineai-subscription: [none]

User Approval

User approval extension for Caffeine AI.

Overview

This skill adds approval-based user management. Users request access; admins approve or reject. Approved users gain access to protected features.

Prerequisite: You must follow extension-authorization first, as this integration depends on it.

Backend

Module API

The prefabricated module mo:caffeineai-user-approval/approval provides low-level approval state management. Do not modify it.

import AccessControl "mo:caffeineai-authorization/access-control";

module {
    public type ApprovalStatus = {
        #approved;
        #rejected;
        #pending;
    };

    public type UserApprovalState = { /* internal state */ };

    public func initState(accessControlState: AccessControl.AccessControlState) : UserApprovalState;

    public func isApproved(state : UserApprovalState, caller : Principal) : Bool;
    public func requestApproval(state : UserApprovalState, caller : Principal);
    public func setApproval(state : UserApprovalState, user : Principal, approval : ApprovalStatus);
    public func removeApproval(state : UserApprovalState, user : Principal);

    public type UserApprovalInfo = {
        principal : Principal;
        status : ApprovalStatus;
    };

    public func listApprovals(state : UserApprovalState) : [UserApprovalInfo];
}

Setup in main.mo

include MixinUserApproval(accessControlState, approvalState) MUST be placed in main.mo, not in a custom mixin file. Declare approvalState at actor top level and pass it into the mixin. The mixin provides these public endpoints automatically:

  • isCallerApproved()
  • requestApproval()
  • setApproval(user, status)
  • removeApproval(user)
  • listApprovals()

Keep approvalState in scope for custom approval guards in app-specific endpoints.

Do NOT redeclare any of the mixin-provided functions.

import AccessControl "mo:caffeineai-authorization/access-control";
import MixinAuthorization "mo:caffeineai-authorization/MixinAuthorization";
import MixinUserApproval "mo:caffeineai-user-approval/MixinUserApproval";
import UserApproval "mo:caffeineai-user-approval/approval";
import Runtime "mo:core/Runtime";

actor {
  let accessControlState : AccessControl.AccessControlState;
  include MixinAuthorization(accessControlState, null);
  let approvalState : UserApproval.UserApprovalState;
  include MixinUserApproval(accessControlState, approvalState);

  // Example custom endpoint with an approval guard:
  // public shared ({ caller }) func protectedFeature() : async () {
  //     if (not (UserApproval.isApproved(approvalState, caller) or AccessControl.hasPermission(accessControlState, caller, #admin))) {
  //         Runtime.trap("Unauthorized: Only approved users can perform this action");
  //     };
  // };
};

The migration chain head — UserApproval.initState depends on the access-control state, so compute it in order inside the migration body:

import AccessControl "mo:caffeineai-authorization/access-control";
import UserApproval "mo:caffeineai-user-approval/approval";

module {
  type NewActor = {
    accessControlState : AccessControl.AccessControlState;
    approvalState : UserApproval.UserApprovalState;
  };

  public func migration(_old : {}) : NewActor {
    let accessControlState = AccessControl.initState();
    {
      accessControlState;
      approvalState = UserApproval.initState(accessControlState);
    };
  };
};

On initState, existing admins are automatically approved. All other users are pending.

The anonymous principal is never approved: requestApproval and setApproval trap for it, and isCallerApproved returns false. isCallerApproved also returns false, rather than trapping, for a signed-in caller that has not registered yet.

IMPORTANT: Apply the right authorization and/or approval check to each custom public function.

Frontend

Approval-based user management:

User Approval Flow

  • Check approval status (isCallerApproved)
  • If not approved, show option to request approval (requestApproval). Show it only to signed-in users; it traps for the anonymous principal
  • Block access to main features for non-approved users
  • Admins have access to all features of the application
  • Display approval status clearly in the UI

Admin Dashboard

For admin users, provide a dashboard to:

  • List all users with their approval status (listApprovals)
  • Approve or reject users (setApproval). A rejected user cannot request approval again
  • Remove a user's entry (removeApproval), so they can request approval again
  • View and assign user roles (using getCallerUserRole and assignCallerUserRole)

Backend Integration

The backend already implements the following functionality. The full interface can be found in

// Check if current user is approved, admins are always approved isCallerApproved(): Promise;

// Submit approval request requestApproval(): Promise;

// Get all users and their approval status (admin only) listApprovals(): Promise<Array>;

// Approve or reject a user (admin only) setApproval(user: Principal, status: ApprovalStatus): Promise;

// Delete a user's approval entry, so they can request approval again (admin only) removeApproval(user: Principal): Promise;

// Assign a role to a user (admin only) assignCallerUserRole(user: Principal, role: UserRole): Promise;

// Get current role for a specific user getCallerUserRole(): Promise;

保存したスキル