Apexにおけるデータアクセス時のセキュリティ機能

SalesforceのApexでデータアクセスのセキュリティを考慮するとき、以下の4つの機能を理解することが重要です。  

1. **アクセスレベルパラメータ（Access Level Parameters）**  
2. **WITH SECURITY_ENFORCED**  
3. **Security.stripInaccessible()**  
4. **Apex Describe Information**  

それぞれの役割や違い、実装例を整理しました。  

---

# **1. アクセスレベルパラメータ（Access Level Parameters）**
### **概要**
- **SOQLクエリやDML操作でオブジェクトやフィールドレベルのアクセス制御を行うためのパラメータ**。  
- Apexコードが「**ユーザーの権限を考慮するか**」を決めるのに使う。  
- **主にDML操作（insert, update, delete）で使われる**。

### **使えるアクセスレベル**
| レベル | 説明 |
|--------|--------|
| **USER_MODE** | **ユーザーの権限を考慮する（プロファイルや権限セットを適用）** |
| **SYSTEM_MODE** | **管理者権限で実行（アクセス制限を無視）** |

### **実装例**
```apex
// USER_MODEを使用（ユーザーのアクセス権を考慮）
Database.SaveResult result = Database.insert(myRecord, AccessLevel.USER_MODE);

// SYSTEM_MODEを使用（管理者権限で実行）
Database.SaveResult result2 = Database.insert(myRecord, AccessLevel.SYSTEM_MODE);
```

### **どんなときに使う？**
✅ **管理者のように実行する場合（SYSTEM_MODE）**  
✅ **標準のアクセス制御を適用したい場合（USER_MODE）**  

**⚠️ 注意点**  
- `DML` 操作で使うものなので、SOQLクエリには影響しない。  
- `USER_MODE` にすると、ユーザーが**アクセスできないオブジェクトやフィールドには書き込めない**。

---

# **2. WITH SECURITY_ENFORCED**
### **概要**
- **SOQLクエリ実行時に、ユーザーがアクセスできないフィールドを自動的に除外する**。  
- **SOQLのクエリに直接組み込めるので簡単に使える**。  
- **フィールドレベルセキュリティ（FLS）を考慮**する。  

### **実装例**
```apex
List<Account> accounts = [SELECT Id, Name, Industry FROM Account WITH SECURITY_ENFORCED];
```
→ **ユーザーが「Industry」フィールドにアクセス権を持っていない場合、クエリは失敗（例外発生）する。**

### **どんなときに使う？**
✅ **SOQLクエリ時に、アクセス権を考慮したいとき**  
✅ **シンプルなコードでセキュリティを担保したいとき**  

**⚠️ 注意点**  
- アクセスできないフィールドが含まれていると **例外（Exception）が発生する**。  
- **一部のフィールドだけ取得したいときは Security.stripInaccessible() の方が便利**。  

---

# **3. Security.stripInaccessible()**
### **概要**
- **アクセスできないフィールドを自動的に除外してくれる（クエリもDML操作も対象）**。  
- **WITH SECURITY_ENFORCED との違い → クエリが失敗せず、アクセス可能なデータのみ取得できる**。  
- **データの作成・更新時にも使えるので、汎用性が高い**。  

### **実装例**
```apex
// クエリ実行（ユーザーがアクセスできないフィールドを自動的に除外）
SObjectAccessDecision decision = Security.stripInaccessible(AccessType.READABLE, [SELECT Id, Name, Industry FROM Account]);
List<Account> safeAccounts = (List<Account>)decision.getRecords();
```
→ **アクセス権のない「Industry」フィールドが自動的に除外されるため、例外は発生しない。**

#### **データ更新時の使用例**
```apex
Account acc = new Account(Id = '001xxxxxxx', Industry = 'Technology'); // Industryが制限されている可能性あり
SObject sanitizedAccount = Security.stripInaccessible(AccessType.UPDATABLE, acc);
update sanitizedAccount;
```
→ **Industry フィールドが更新不可なら、自動的に除外されて安全にupdateできる**。

### **どんなときに使う？**
✅ **クエリ時にアクセスできないフィールドを自動的に取り除きたいとき**  
✅ **DML操作時に、アクセスできないフィールドを除外してエラーを防ぎたいとき**  

**⚠️ 注意点**  
- `WITH SECURITY_ENFORCED` とは違い、クエリの失敗はしないが、**アクセスできないフィールドは結果から消える**。  
- クエリだけでなく、DML操作でも使えるのがメリット。

---

# **4. Apex Describe Information**
### **概要**
- **オブジェクトやフィールドのメタデータを動的に取得できる**。  
- **フィールドレベルセキュリティ（FLS）やオブジェクトレベルセキュリティ（CRUD）のチェックをカスタム処理で行うときに使う**。  
- **「ユーザーがこのフィールドを読めるか・書けるか？」を事前にチェックできる**。  

### **実装例**
```apex
// ユーザーが Account オブジェクトに対する read 権限を持っているか確認
if (Schema.sObjectType.Account.isAccessible()) {
    List<Account> accounts = [SELECT Id, Name FROM Account];
}

// ユーザーが Account の Industry フィールドを編集できるか確認
if (Schema.sObjectType.Account.fields.Industry.isUpdateable()) {
    Account acc = new Account(Id = '001xxxxxxx', Industry = 'Technology');
    update acc;
}
```

### **どんなときに使う？**
✅ **カスタムのロジックでアクセス制御をしたいとき**  
✅ **CRUDやFLSチェックをコードで実装したいとき**  

**⚠️ 注意点**  
- **`WITH SECURITY_ENFORCED` や `Security.stripInaccessible()` の方が簡単なので、通常はそれを使う**。  
- **動的な権限チェックが必要な場合にのみ使う（例：カスタムUIでボタンを表示/非表示）**。

---

# **まとめ：どれを使えばいい？**
| 機能 | 役割 | 使う場面 | クエリ | DML |
|------|------|------|------|------|
| **Access Level Parameters** | DML時の権限制御 | **DML（insert, update, delete）でユーザー権限を適用** | ❌ | ✅ |
| **WITH SECURITY_ENFORCED** | クエリのセキュリティ適用 | **SOQLでFLSを適用し、アクセスできない場合はエラーにする** | ✅ | ❌ |
| **Security.stripInaccessible()** | クエリとDMLのフィールド制御 | **クエリ時にアクセスできないフィールドを除外 / DML時に変更不可のフィールドを削除** | ✅ | ✅ |
| **Apex Describe Information** | 権限チェック（CRUD/FLS） | **コード内で動的に権限チェックをする** | ✅ | ✅ |

---

## **どれを使うべき？**
- **SOQLクエリ** → `WITH SECURITY_ENFORCED` or `Security.stripInaccessible()`  
- **DML操作** → `Security.stripInaccessible()` or `Access Level Parameters (USER_MODE)`  
- **動的に権限をチェックしたい** → `Apex Describe Information`  

適切に使い分けることで、安全でエラーの少ないApexコードを実装できます！ 🚀