---
name: ControllerAuthorization
description: Hướng dẫn cài đặt phân quyền (role-based authorization) chuẩn cho ASP.NET Core Controller trong dự án Ladaer.BackEnd.
---

# Controller Authorization Skill

## Tổng quan

Mỗi action trong Controller phải được gắn attribute `[Authorize(Roles = "...")]` ở cấp **action**, KHÔNG chỉ dùng `[Authorize]` chung ở cấp class. Điều này giúp kiểm soát quyền truy cập chi tiết theo từng nghiệp vụ.

## Quy tắc đặt tên Role

Tên role theo format: **`{ControllerName}.{ActionName}`**

- `ControllerName`: Tên controller **không có hậu tố "Controller"** (ví dụ: `Premise`, `Order`, `PremiseScoring`).
- `ActionName`: Tên method action trong controller (ví dụ: `Search`, `GetById`, `CreateOrUpdate`, `Delete`).

### Ví dụ

| Controller | Action | Role |
|---|---|---|
| `OrderController` | `Search` | `Order.Search` |
| `OrderController` | `CreateOrder` | `Order.CreateOrder` |
| `PremiseController` | `Search` | `Premise.Search` |
| `PremiseController` | `CreateOrUpdate` | `Premise.CreateOrUpdate` |
| `PremiseController` | `Delete` | `Premise.Delete` |
| `PremiseScoringController` | `SearchCriteria` | `PremiseScoring.SearchCriteria` |

## Cấu trúc attribute chuẩn

```csharp
[HttpPost("Search")]
[Authorize(Roles = "ControllerName.ActionName")]
public async Task<IActionResult> Search([FromBody] SmartTableParam param)
{
    var result = await _service.Search(param);
    return Ok(result);
}
```

## Cấu trúc class Controller chuẩn

```csharp
[Route("api/[controller]")]
[ApiController]
[Authorize]          // ← Chỉ đảm bảo user đã đăng nhập ở cấp class
public class ExampleController : ControllerBase
{
    [HttpPost("Search")]
    [Authorize(Roles = "Example.Search")]   // ← Kiểm tra ROLE ở cấp action
    public async Task<IActionResult> Search([FromBody] SmartTableParam param) { ... }

    [HttpGet("GetById/{id}")]
    [Authorize(Roles = "Example.GetById")]
    public async Task<IActionResult> GetById(int id) { ... }

    [HttpPost("CreateOrUpdate")]
    [Authorize(Roles = "Example.CreateOrUpdate")]
    public async Task<IActionResult> CreateOrUpdate([FromBody] ExampleModel model) { ... }

    [HttpDelete("Delete/{id}")]
    [Authorize(Roles = "Example.Delete")]
    public async Task<IActionResult> Delete(int id) { ... }
}
```

## Danh sách action chuẩn và role tương ứng

| Action | Role Pattern |
|---|---|
| Tìm kiếm danh sách | `{Name}.Search` |
| Lấy chi tiết theo ID | `{Name}.GetById` |
| Tạo mới hoặc cập nhật | `{Name}.CreateOrUpdate` |
| Xóa bản ghi | `{Name}.Delete` |
| Đổi trạng thái | `{Name}.ChangeStatus` |
| Xuất Excel | `{Name}.ExportExcel` |
| Lấy tất cả (dropdown/lookup) | `{Name}.GetAll` |

## Lưu ý quan trọng

1. **Luôn giữ `[Authorize]` ở cấp class** để yêu cầu xác thực tối thiểu cho mọi endpoint trong controller.
2. **Luôn thêm `[Authorize(Roles = "...")]` ở cấp action** để kiểm soát quyền chi tiết, ngay cả khi đã có `[Authorize]` ở class.
3. **Thứ tự attribute**: `[HttpMethod]` trước, `[Authorize(Roles)]` sau.
4. **Tên role phân biệt hoa thường**: `Premise.Search` ≠ `premise.search` → Luôn dùng PascalCase.
5. Nếu một endpoint cho phép mọi role (ví dụ: download file), dùng chỉ `[Authorize]` ở cấp action.

## Ví dụ thực tế từ dự án

### OrderController
```csharp
[HttpPost("Search")]
[Authorize(Roles = "Order.Search")]
public async Task<IActionResult> Search(SmartTableParam param) { ... }

[HttpPost("CreateOrder")]
[Authorize(Roles = "Order.CreateOrder")]
public async Task<IActionResult> CreateOrder(OrderModel model) { ... }

[HttpGet("Download/{fileName}")]
[Authorize]   // ← Không cần role cụ thể, chỉ cần đăng nhập
public async Task<IActionResult> DownloadFile(string fileName) { ... }
```

### PremiseController
```csharp
[HttpPost("Search")]
[Authorize(Roles = "Premise.Search")]
public async Task<IActionResult> Search([FromBody] SmartTableParam param) { ... }

[HttpPost("CreateOrUpdate")]
[Authorize(Roles = "Premise.CreateOrUpdate")]
public async Task<IActionResult> CreateOrUpdate([FromForm] PremiseModel model) { ... }

[HttpPost("ChangeStatus")]
[Authorize(Roles = "Premise.ChangeStatus")]
public async Task<IActionResult> ChangeStatus([FromBody] PremiseStatusModel model) { ... }
```
