Đăng nhập Đại diện

Mô tả Tổng quan

Tính năng Đăng nhập Đại diện cho phép quản trị viên đăng nhập với tư cách đại diện của một thành viên nhóm cụ thể. Điều này rất hữu ích cho việc hỗ trợ kỹ thuật, kiểm tra vấn đề từ góc nhìn của user, hoặc thực hiện các hành động thay mặt user khi cần thiết. Tính năng này đảm bảo rằng chỉ những quản trị viên có quyền hạn phù hợp mới có thể sử dụng chức năng này và tất cả các hành động đều được ghi lại để kiểm tra.

Biểu đồ Hoạt động

---
config:
  theme: base
  layout: dagre
  flowchart:
    curve: linear
    htmlLabels: true
  themeVariables:
    edgeLabelBackground: "transparent"
---
flowchart TD
    %% Main components
    AdminUser[Admin User]
    RepresentativeController[RepresentativeController]
    AuthService[AuthService]
    UserDB[(Database)]
    SessionDB[(Session Store)]
    
    %% Process steps with numbering
    AdminUser --- Step1[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>1</span>
            <p style='margin-top: 8px'>Yêu cầu Đại diện</p>
        </div>
    ]
    Step1 --> ValidateAdmin[Xác thực Admin]
    
    ValidateAdmin --- Step2[
        <div style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>2</span>
            <p style='margin-top: 8px'>Kiểm tra Quyền</p>
        </div>
    ]
    Step2 --> CheckPermissions[Kiểm tra Quyền Admin]
    
    CheckPermissions --- Step3[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>3</span>
            <p style='margin-top: 8px'>Tìm User</p>
        </div>
    ]
    Step3 --> FindUser[Tìm User trong Database]
    
    FindUser --- Step4[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>4</span>
            <p style='margin-top: 8px'>Kiểm tra Sự tồn tại</p>
        </div>
    ]
    Step4 --> UserCheck{User Tồn tại?}
    
    UserCheck --- Step5A[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #cc6666 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>5A</span>
            <p style='margin-top: 8px'>Trả về Lỗi</p>
        </div>
    ]
    Step5A -->|Không| ReturnError[Trả về Lỗi]
    
    UserCheck --- Step5B[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>5B</span>
            <p style='margin-top: 8px'>Kiểm tra Nhóm</p>
        </div>
    ]
    Step5B -->|Có| CheckGroup[Kiểm tra Thành viên Nhóm]
    
    CheckGroup --- Step6[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>6</span>
            <p style='margin-top: 8px'>Xác minh Quyền</p>
        </div>
    ]
    Step6 --> GroupCheck{Thuộc Nhóm?}
    
    GroupCheck --- Step7A[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #cc6666 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>7A</span>
            <p style='margin-top: 8px'>Trả về Lỗi</p>
        </div>
    ]
    Step7A -->|Không| ReturnError
    
    GroupCheck --- Step7B[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>7B</span>
            <p style='margin-top: 8px'>Tạo Phiên làm việc</p>
        </div>
    ]
    Step7B -->|Có| CreateSession[Tạo Phiên làm việc Đại diện]
    
    CreateSession --- Step8[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>8</span>
            <p style='margin-top: 8px'>Lưu Phiên làm việc</p>
        </div>
    ]
    Step8 --> StoreSession[Lưu vào Session Store]
    
    StoreSession --- Step9[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>9</span>
            <p style='margin-top: 8px'>Tạo Cookie</p>
        </div>
    ]
    Step9 --> GenerateCookie[Tạo Representative Cookie]
    
    GenerateCookie --- Step10[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>10</span>
            <p style='margin-top: 8px'>Trả về Phản hồi</p>
        </div>
    ]
    Step10 --> Success[Trả về Thành công]
    
    %% Styling
    style AdminUser fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style RepresentativeController fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style AuthService fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style UserDB fill:#ffe6cc,stroke:#ff9900,stroke-width:2px
    style SessionDB fill:#ffe6cc,stroke:#ff9900,stroke-width:2px
    style ValidateAdmin fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style CheckPermissions fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style FindUser fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style CheckGroup fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style CreateSession fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style StoreSession fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style GenerateCookie fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style Success fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style ReturnError fill:#fcd9d9,stroke:#cc3333,stroke-width:2px
    style UserCheck fill:#ffe6cc,stroke:#ff9900,stroke-width:2px
    style GroupCheck fill:#ffe6cc,stroke:#ff9900,stroke-width:2px
    style Step1 fill:transparent,stroke:transparent,stroke-width:1px
    style Step2 fill:transparent,stroke:transparent,stroke-width:1px
    style Step3 fill:transparent,stroke:transparent,stroke-width:1px
    style Step4 fill:transparent,stroke:transparent,stroke-width:1px
    style Step5A fill:transparent,stroke:transparent,stroke-width:1px
    style Step5B fill:transparent,stroke:transparent,stroke-width:1px
    style Step6 fill:transparent,stroke:transparent,stroke-width:1px
    style Step7A fill:transparent,stroke:transparent,stroke-width:1px
    style Step7B fill:transparent,stroke:transparent,stroke-width:1px
    style Step8 fill:transparent,stroke:transparent,stroke-width:1px
    style Step9 fill:transparent,stroke:transparent,stroke-width:1px
    style Step10 fill:transparent,stroke:transparent,stroke-width:1px

Tài liệu Trường hợp

Trường hợp 1: Đăng nhập Đại diện Thành công

Biểu đồ Tuần tự

sequenceDiagram
    participant Admin
    participant API as RepresentativeController
    participant Service as AuthService
    participant UserDB as Database
    participant SessionDB as Database

    Note over Admin,API: Bước 1: Gửi Yêu cầu Đại diện
    Admin->>API: POST /api/v1/admin/auth/representative (với user_id)
    
    Note over API,Service: Bước 2: Xử lý Yêu cầu
    API->>Service: loginAsRepresentative(user_id)
    
    Note over Service,UserDB: Bước 3: Tìm User
    Service->>UserDB: findUserWithGroup(user_id)
    UserDB-->>Service: Trả về dữ liệu user
    
    Note over Service,Service: Bước 4: Kiểm tra Quyền
    Service->>Service: validateRepresentativeAccess(user_id)
    
    Note over Service,SessionDB: Bước 5: Tạo Phiên làm việc
    Service->>SessionDB: createRepresentativeSession(admin_id, user_id)
    SessionDB-->>Service: Trả về dữ liệu phiên làm việc
    
    Note over Service,Service: Bước 6: Tạo Cookie
    Service->>Service: generateRepresentativeCookie
    
    Note over API,Admin: Bước 7: Trả về Phản hồi
    API-->>Admin: 200 OK với cookies

Trường hợp 2: Đăng nhập Đại diện Thất bại

Biểu đồ Tuần tự

sequenceDiagram
    participant Admin
    participant API as RepresentativeController
    participant Service as AuthService
    participant UserDB as Database

    Note over Admin,API: Bước 1: Gửi Yêu cầu Đại diện
    Admin->>API: POST /api/v1/admin/auth/representative (với user_id)
    
    Note over API,Service: Bước 2: Xử lý Yêu cầu
    API->>Service: loginAsRepresentative(user_id)
    
    Note over Service,UserDB: Bước 3: Tìm User
    Service->>UserDB: findUserWithGroup(user_id)
    UserDB-->>Service: Trả về lỗi - User không tồn tại
    
    Note over Service,API: Bước 4: Xử lý Lỗi
    Service->>API: Trả về lỗi user không tồn tại
    
    Note over API,Admin: Bước 5: Trả về Phản hồi Lỗi
    API-->>Admin: 404 Not Found với thông báo lỗi

Các Bước

Bước 1: Gửi Yêu cầu Đại diện

  • Mô tả: Admin thực hiện thao tác đăng nhập với vai trò đại diện cho user khác. Nếu muốn quay lại quyền admin, truyền giá trị đặc biệt 0.
  • Yêu cầu: POST /api/admin/auth/representative/{id}
  • Tham số URL:
    • id: ID của user cần đại diện, hoặc 0 để trở lại quyền admin
  • Xác thực:
    • Kiểm tra xác thực admin
    • Xác minh quyền sử dụng tính năng đại diện
    • Kiểm tra giới hạn tốc độ

Bước 2: Xử lý Yêu cầu

  • Mô tả: Controller xác thực yêu cầu và chuyển cho service
  • Hành động:
    • Xác minh quyền admin
    • Trích xuất user_id từ body
    • Gọi service xác thực cho quá trình đại diện

Bước 3: Tìm User

  • Mô tả: Tìm bản ghi user trong cơ sở dữ liệu
  • Hành động:
    • Truy vấn cơ sở dữ liệu cho user với ID khớp
    • Xác minh user tồn tại và đang hoạt động
    • Tải thông tin nhóm của user

Bước 4: Kiểm tra Quyền

  • Mô tả: Xác minh quyền truy cập đại diện
  • Hành động:
    • Kiểm tra user có thuộc nhóm hợp lệ
    • Xác minh quyền admin có thể đại diện cho user này
    • Kiểm tra các ràng buộc bảo mật

Bước 5: Tạo Phiên làm việc

  • Mô tả: Tạo phiên làm việc đại diện
  • Hành động:
    • Tạo định danh phiên làm việc duy nhất
    • Liên kết admin và user đại diện
    • Đặt thời gian hết hạn phiên làm việc
    • Ghi log quyền truy cập đại diện

Bước 6: Tạo Cookie

  • Mô tả: Tạo cookie phiên làm việc đại diện
  • Hành động:
    • Tạo token đại diện an toàn
    • Tạo cookie với cờ secure và httpOnly
    • Đặt domain và path phù hợp
    • Đặt thời gian hết hạn

Bước 7: Trả về Phản hồi

  • Mô tả: Gửi phản hồi thành công
  • Phản hồi:
    • Thành công: 200 OK với dữ liệu user
    • Đặt cookie đại diện
    • Bao gồm thông tin phiên làm việc

API Endpoint

Endpoint: POST /api/admin/auth/representative/{id}

Headers:

  • firebase-token: Token xác thực từ Firebase (bắt buộc)
  • Content-Type: application/json

Body Parameters:

{
    "user_id": 123
}

Response:

{
    "success": true,
    "message": "Đăng nhập đại diện thành công",
    "data": {
        "representative_user": {
            "id": 123,
            "name": "User Name",
            "email": "user@example.com",
            "uid": "firebase_uid_here",
            "status": 1,
            "groups": [
                {
                    "id": 1,
                    "name": "Default Group",
                    "role": "member"
                }
            ]
        },
        "session_info": {
            "representative_session_id": "session_uuid_here",
            "expires_at": "2024-01-01T08:00:00.000000Z",
            "admin_user_id": 1
        }
    }
}

Bảng Cơ sở Dữ liệu Liên quan & Trường

erDiagram
    users {
        bigint id PK
        string name "Tên đầy đủ của user"
        string email "Địa chỉ email của user (duy nhất)"
        string uid "Firebase user ID (duy nhất)"
        int status "Trạng thái tài khoản: 1: hoạt động, 0: không hoạt động"
        boolean is_first_login "Cờ cho biết user đã hoàn thành đăng nhập lần đầu"
        timestamp created_at
        timestamp updated_at
    }
    groups {
        bigint id PK
        string name "Tên nhóm"
        string description "Mô tả nhóm"
        int status "Trạng thái nhóm: 1: hoạt động, 0: không hoạt động"
        timestamp created_at
        timestamp updated_at
    }
    group_members {
        bigint id PK
        bigint group_id FK "Tham chiếu đến bảng groups"
        bigint user_id FK "Tham chiếu đến bảng users"
        int status "Trạng thái thành viên: 1: hoạt động, 0: không hoạt động"
        timestamp created_at
        timestamp updated_at
    }
    group_roles {
        bigint id PK
        bigint group_id FK "Tham chiếu đến bảng groups"
        bigint user_id FK "Tham chiếu đến bảng users"
        string role "Vai trò trong nhóm"
        timestamp created_at
        timestamp updated_at
    }
    representative_sessions {
        bigint id PK
        bigint admin_user_id FK "Tham chiếu đến bảng users (admin)"
        bigint representative_user_id FK "Tham chiếu đến bảng users (đại diện)"
        string session_id "Định danh phiên làm việc duy nhất"
        timestamp expires_at "Thời gian hết hạn phiên làm việc"
        timestamp created_at
        timestamp updated_at
    }

    users ||--o{ group_members : belongs_to
    groups ||--o{ group_members : contains
    users ||--o{ group_roles : has_role_in
    groups ||--o{ group_roles : assigns_role
    users ||--o{ representative_sessions : admin_creates
    users ||--o{ representative_sessions : is_represented

Xử lý Lỗi

  • Ghi log

    • Tất cả các yêu cầu đại diện được ghi vào log ứng dụng
    • Lỗi xác minh quyền được ghi lại
    • (Tùy chọn) Gửi tin nhắn slack cho các sự kiện bảo mật
  • Chi tiết Lỗi:

    Mã Trạng thái Thông báo Lỗi Mô tả
    401 "Không được phép truy cập." Khi không có quyền admin
    404 "User không tồn tại." Khi user_id không hợp lệ
    403 "Không thể đại diện cho user này." Khi user không thuộc nhóm hợp lệ
    429 "Quá nhiều yêu cầu." Khi vượt quá giới hạn tốc độ
    500 Lỗi chung với thông báo exception Khi xảy ra lỗi không mong đợi

Ghi chú Bổ sung

  • Giới hạn tốc độ: Các yêu cầu đại diện bị giới hạn ở 10 lần/phút cho mỗi admin
  • Thời gian hết hạn phiên làm việc: Phiên làm việc đại diện hết hạn sau 1 giờ
  • Bảo mật: Tất cả các hành động đại diện đều được ghi lại để kiểm tra
  • Kiểm tra quyền: Chỉ những admin có quyền hạn phù hợp mới có thể sử dụng
  • Ghi log: Tất cả các lần đăng nhập đại diện đều được ghi lại để kiểm tra bảo mật
  • Cân nhắc triển khai:
    • Giới hạn số lượng phiên làm việc đại diện đồng thời
    • Cảnh báo khi admin sử dụng quyền đại diện
    • Theo dõi hoạt động đại diện
    • Tự động kết thúc phiên làm việc không hoạt động