[FEAT] B000 登入回應新增 identity 身分欄位

1. MachineAuthController loginB000 於 RBAC 判斷時記錄登入者身分,成功回應加入 identity 欄位 (system/company/staff)
2. 未授權與帳密錯誤回應維持不含 identity,避免洩漏身分
3. 同步更新 api-technical-specs SKILL.md 與 config/api-docs.php 回應參數文件
4. 新增 MachineAuthB000Test 涵蓋三身分成功與 401/403 失敗共 5 案例

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
sky121113 2026-06-11 10:09:44 +08:00
parent 2b25c5bebf
commit 339b33d09b
4 changed files with 156 additions and 0 deletions

View File

@ -51,6 +51,7 @@ description: 本技能規範定義了 Star Cloud 系統中所有機台 (IoT) 與
| success | Boolean | 請求是否成功 | true |
| code | Integer | 業務狀態碼 | 200 |
| message | String | 驗證結果 (Success 或 Failed) | Success |
| identity | String | **登入者身分**`system` 系統登入者 / `company` 公司帳號 / `staff` 一般人員。僅驗證成功時回傳 | system |
| token | String | **臨時身份認證 Token** (可選,供未來擴充使用) | 1\|abcdefg... |
---

View File

@ -59,16 +59,22 @@ class MachineAuthController extends Controller
}
// 5. RBAC 權限驗證 (遵循多租戶與機台授權規範)
// $identity: 登入者身分 (system:系統登入者 / company:公司帳號 / staff:一般人員)
// 僅在授權通過時賦值,確保未授權者不洩漏身分
$isAuthorized = false;
$identity = null;
if ($user->isSystemAdmin()) {
$isAuthorized = true;
$identity = 'system';
} elseif ($user->is_admin) {
if ($machine->company_id === $user->company_id) {
$isAuthorized = true;
$identity = 'company';
}
} else {
if ($user->machines()->where('machine_id', $machine->id)->exists()) {
$isAuthorized = true;
$identity = 'staff';
}
}
@ -120,6 +126,7 @@ class MachineAuthController extends Controller
'success' => true,
'code' => 200,
'message' => 'Success',
'identity' => $identity,
'token' => $user->createToken('technician-setup', ['*'], now()->addHours(8))->plainTextToken
]);
}

View File

@ -55,6 +55,11 @@ return [
'description' => '回應訊息',
'example' => 'Success'
],
'identity' => [
'type' => 'string',
'description' => '登入者身分system 系統登入者 / company 公司帳號 / staff 一般人員 (僅驗證成功時回傳)',
'example' => 'system'
],
'token' => [
'type' => 'string',
'description' => '臨時身份認證 Token',
@ -70,6 +75,7 @@ return [
'success' => true,
'code' => 200,
'message' => 'Success',
'identity' => 'system',
'token' => '1|abcdefg...'
],
],

View File

@ -0,0 +1,142 @@
<?php
namespace Tests\Feature\Api\V1;
use App\Models\Machine\Machine;
use App\Models\System\Company;
use App\Models\System\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Tests\TestCase;
class MachineAuthB000Test extends TestCase
{
use RefreshDatabase;
private Company $company;
private Machine $machine;
private string $apiToken;
protected function setUp(): void
{
parent::setUp();
$this->company = Company::create([
'name' => 'Test Company',
]);
$this->apiToken = 'test-machine-api-token-123456';
$this->machine = Machine::factory()->create([
'company_id' => $this->company->id,
'api_token' => $this->apiToken,
]);
}
private function loginAs(string $account, string $password = 'password')
{
return $this->withHeader('Authorization', "Bearer {$this->apiToken}")
->postJson('/api/v1/app/admin/login/B000', [
'Su_Account' => $account,
'Su_Password' => $password,
]);
}
/**
* 系統登入者 (company_id = null) identity: system
*/
public function test_system_admin_login_returns_identity_system(): void
{
User::factory()->create([
'company_id' => null,
'username' => 'sysadmin',
'password' => Hash::make('password'),
]);
$this->loginAs('sysadmin')
->assertStatus(200)
->assertJson([
'success' => true,
'code' => 200,
'identity' => 'system',
]);
}
/**
* 公司帳號 (租戶管理員is_admin = true,同 company_id 機台) identity: company
*/
public function test_company_admin_login_returns_identity_company(): void
{
User::factory()->create([
'company_id' => $this->company->id,
'username' => 'companyadmin',
'is_admin' => true,
'password' => Hash::make('password'),
]);
$this->loginAs('companyadmin')
->assertStatus(200)
->assertJson([
'success' => true,
'identity' => 'company',
]);
}
/**
* 一般人員 (is_admin = false,已加入 machine_user 授權) identity: staff
*/
public function test_staff_login_returns_identity_staff(): void
{
$staff = User::factory()->create([
'company_id' => $this->company->id,
'username' => 'staff',
'is_admin' => false,
'password' => Hash::make('password'),
]);
$staff->machines()->attach($this->machine->id);
$this->loginAs('staff')
->assertStatus(200)
->assertJson([
'success' => true,
'identity' => 'staff',
]);
}
/**
* 帳密錯誤 401,回應不含 identity
*/
public function test_invalid_credentials_returns_401_without_identity(): void
{
User::factory()->create([
'company_id' => null,
'username' => 'sysadmin',
'password' => Hash::make('password'),
]);
$response = $this->loginAs('sysadmin', 'wrong-password');
$response->assertStatus(401)
->assertJson(['success' => false, 'code' => 401]);
$this->assertArrayNotHasKey('identity', $response->json());
}
/**
* 未授權 (一般人員未加入 machine_user) 403,回應不含 identity
*/
public function test_unauthorized_staff_returns_403_without_identity(): void
{
User::factory()->create([
'company_id' => $this->company->id,
'username' => 'staff',
'is_admin' => false,
'password' => Hash::make('password'),
]);
$response = $this->loginAs('staff');
$response->assertStatus(403)
->assertJson(['success' => false, 'code' => 403]);
$this->assertArrayNotHasKey('identity', $response->json());
}
}