Files
SnapOtter/apps/docs/th/guide/scim.md
T
SnapOtterandGitHub d10d0f544f fix: release QA hardening across processing, media, security, and CI gates (#649)
A release-readiness QA pass over the whole product. The commits split into
defects a user would hit and gates that were reporting green while measuring
nothing.

## Fixes that change behaviour

Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so
request.ip came from a client-set header and a forged X-Forwarded-For got past
the login limiter. The default is now a private-network trust list.

A transient Postgres outage stranded in-flight jobs, leaving finished output on
disk with no row pointing at it. A reconciler now resolves those rows and adopts
the bytes rather than dropping the work.

A Redis connection that moved to a new address wedged every read-blocked
consumer, so completions stopped signalling while health still answered 200.
Socket timeouts plus subscriber pings recover it.

Installing more than one AI bundle left the shared venv multi-versioned and
silently broke three tools. The installer now reconciles distributions to one
version each.

Converting an image to JXL at quality 1 through 4 returned a 500, because
libjxl 0.7 rejects the distance those values compute. The quality is floored at
what the encoder honours. A missing ffmpeg was also reported to the user as a
corrupt upload; it now says the engine is unavailable.

RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at
0.22.2, and the release scan was split so it can fail on an unfixed critical
instead of hiding it behind ignore-unfixed.

## Gates that could not fail

Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs
build; coverage discarded its whole report on any failing test; the lint gate
skipped root tests, scripts, and two workspaces; and several generated matrices
counted a host missing ffmpeg as a passing tool. Each now measures what it
claims.

Full evidence and the outstanding release items are tracked locally and are not
part of this branch.
2026-07-27 15:37:30 +08:00

24 KiB

description, i18n_source_hash, i18n_provenance, i18n_output_hash, i18n_hash_version
description i18n_source_hash i18n_provenance i18n_output_hash i18n_hash_version
ตั้งค่าการจัดสรร SCIM 2.0 เพื่อซิงก์ผู้ใช้และกลุ่มจากผู้ให้บริการข้อมูลประจำตัว (identity provider) ของคุณไปยัง SnapOtter ครอบคลุม Okta, Azure AD / Entra ID และการผสานรวมแบบกำหนดเอง 06ee702b386e human e8ab3b07c519 2

การจัดสรรด้วย SCIM

SnapOtter รองรับ SCIM 2.0 (System for Cross-domain Identity Management) สำหรับการจัดสรรผู้ใช้และกลุ่มแบบอัตโนมัติ ผู้ให้บริการข้อมูลประจำตัวของคุณสามารถสร้าง อัปเดต ปิดใช้งาน และเปิดใช้งานบัญชีผู้ใช้ใหม่ รวมถึงซิงก์การเป็นสมาชิกกลุ่มได้โดยอัตโนมัติ

::: tip คุณสมบัติสำหรับองค์กร (Enterprise) การจัดสรรด้วย SCIM ต้องใช้ไลเซนส์ enterprise ที่มีคุณสมบัติ scim ไม่มีให้ใช้งานในแผน team หากไม่มีคุณสมบัตินี้ ปลายทาง SCIM ทั้งหมด (ยกเว้น discovery) จะคืนค่า 403 :::

ข้อกำหนดเบื้องต้น

  • อินสแตนซ์ SnapOtter ที่ทำงานอยู่และเข้าถึงได้ผ่าน URL สาธารณะ
  • คีย์ไลเซนส์ enterprise ที่มีคุณสมบัติ scim
  • บัญชี SnapOtter admin ในตัวพร้อมชุดสิทธิ์ที่มีผลเต็มรูปแบบ บทบาทที่กำหนดเองที่ได้รับมอบสิทธิ์หรือคีย์ API ของผู้ดูแลระบบที่ขาดสิทธิ์ของผู้ดูแลระบบไม่สามารถสร้างหรือเพิกถอนโทเค็น SCIM ส่วนกลางได้
  • สิทธิ์ผู้ดูแลระบบในการตั้งค่าการจัดสรรของผู้ให้บริการข้อมูลประจำตัวของคุณ

เริ่มต้นใช้งานอย่างรวดเร็ว

  1. สร้างโทเคน bearer สำหรับ SCIM:
curl -X POST https://photos.example.com/api/v1/enterprise/scim/token \
  -H "Cookie: snapotter-session=YOUR_SESSION" \
  -H "Content-Type: application/json"

การตอบกลับจะมีโทเคนอยู่ด้วย บันทึกไว้ทันที เพราะไม่สามารถเรียกดูได้อีก

{
  "token": "so_scim_v2_a1b2c3d4e5f6...",
  "message": "Save this token - it cannot be retrieved again"
}
  1. ในผู้ให้บริการข้อมูลประจำตัวของคุณ กำหนดค่าการจัดสรร SCIM ด้วย:
    • Base URL: https://photos.example.com/api/v1/scim/v2
    • Authentication: โทเคน Bearer (วางโทเคนจากขั้นตอนที่ 1)

การยืนยันตัวตน

ปลายทาง SCIM ใช้โทเคน Bearer เฉพาะ ซึ่งแยกจากเซสชันผู้ใช้และคีย์ API

การสร้างโทเคน

POST /api/v1/enterprise/scim/token สร้างโทเค็น SCIM ใหม่ เนื่องจากโทเค็นสามารถจัดเตรียมและเปลี่ยนผู้ใช้ข้ามอินสแตนซ์ได้ ตำแหน่งข้อมูลนี้จึงต้องมีบทบาท admin ในตัวพร้อมชุดสิทธิ์ผู้ดูแลระบบที่มีผลสมบูรณ์ การถือ users:manage ในบทบาทที่กำหนดเองนั้นไม่เพียงพอ

โทเคนจะถูกคืนค่าเป็นข้อความธรรมดาเพียงครั้งเดียวเท่านั้น SnapOtter จัดเก็บเฉพาะแฮชแบบ scrypt เท่านั้น หากคุณทำโทเคนหาย ให้เพิกถอนและสร้างใหม่

มีโทเคน SCIM ที่ใช้งานได้เพียงหนึ่งโทเคนในแต่ละครั้ง การสร้างโทเคนใหม่จะแทนที่โทเคนก่อนหน้า

::: warning โทเค็นออกใหม่หลังจากอัปเกรด โทเค็น SCIM ที่ไม่มีเวอร์ชันเดิมถูกปฏิเสธ หลังจากอัปเกรดเป็นรุ่นที่ออกโทเค็น so_scim_v2_... แล้ว ให้สร้างโทเค็นใหม่และอัปเดตผู้ให้บริการข้อมูลประจำตัวของคุณก่อนดำเนินการจัดสรรต่อ :::

การเพิกถอนโทเคน

DELETE /api/v1/enterprise/scim/token เพิกถอนโทเค็น SCIM ปัจจุบัน มีข้อกำหนดผู้ดูแลระบบในตัวเต็มรูปแบบเหมือนกับการสร้างโทเค็น

การจำกัดอัตรา

ปลายทาง SCIM ถูกจำกัดอัตราไว้ที่ 1000 คำขอต่อนาทีต่อโทเคน หากเกินขีดจำกัดนี้จะคืนค่า HTTP 429

ทรัพยากรที่รองรับ

ทรัพยากร SCIM แนวคิดใน SnapOtter สร้าง อ่าน อัปเดต ลบ
User บัญชีผู้ใช้ ได้ ได้ ได้ ลบแบบซอฟต์
Group Team ได้ ได้ ได้ ได้

::: warning Groups ใน SCIM แมปกับ teams ของ SnapOtter ไม่ใช่บทบาท (roles) SCIM ไม่สามารถตั้งค่าบทบาทของผู้ใช้ได้ ผู้ใช้ทั้งหมดที่สร้างผ่าน SCIM จะได้รับบทบาท user หากต้องการเปลี่ยนบทบาทของผู้ใช้ ให้ใช้ UI ผู้ดูแลระบบของ SnapOtter :::

การดำเนินการกับผู้ใช้

สร้างผู้ใช้

POST /api/v1/scim/v2/Users

สร้างบัญชีผู้ใช้ใหม่โดยตั้งค่า authProvider เป็น scim และบทบาท user ผู้ใช้จะถูกกำหนดให้อยู่ในทีม Default หาก active เป็น false บทบาทจะถูกตั้งเป็น disabled แทน

แอตทริบิวต์ที่จำเป็น: userName ทางเลือก: externalId, emails, active (ค่าเริ่มต้น true)

แสดงรายการและกรองผู้ใช้

GET /api/v1/scim/v2/Users

คืนค่ารายการผู้ใช้แบบแบ่งหน้า รองรับพารามิเตอร์ query startIndex และ count (สูงสุด 200 ผลลัพธ์ต่อหน้า)

การกรองรองรับเฉพาะ eq (เท่ากับ) เท่านั้น บนแอตทริบิวต์เหล่านี้:

  • userName eq "jane"
  • externalId eq "ext-12345"

ตัวดำเนินการและแอตทริบิวต์การกรองอื่น ๆ จะคืนค่า HTTP 400

รับข้อมูลผู้ใช้

GET /api/v1/scim/v2/Users/:id

คืนค่าผู้ใช้รายเดียวตาม ID ผู้ใช้ SnapOtter ของพวกเขา

แทนที่ผู้ใช้

PUT /api/v1/scim/v2/Users/:id

แทนที่แอตทริบิวต์ของผู้ใช้ รองรับ userName, externalId, emails และ active การเปลี่ยนชื่อผู้ใช้จะถูกตรวจสอบความขัดแย้ง (409 หากชื่อผู้ใช้ใหม่ถูกใช้โดยผู้ใช้อื่นแล้ว)

แพตช์ผู้ใช้

PATCH /api/v1/scim/v2/Users/:id

อัปเดตบางส่วนโดยใช้ SCIM PatchOp การดำเนินการที่รองรับ:

การดำเนินการ Paths
replace active, userName, externalId, emails, emails[type eq "work"].value, name.formatted, displayName
add เหมือนกับ replace
remove externalId, emails

พาธ name.formatted และ displayName ได้รับการยอมรับเพื่อความเข้ากันได้ แต่ไม่มีผลถาวร (SnapOtter ไม่ได้จัดเก็บชื่อที่แสดงแยกต่างหาก)

การดำเนินการ replace แบบไม่มีค่า (ที่ค่าเป็นออบเจกต์โดยไม่มี path) ก็รองรับเช่นกัน โดยมีคีย์ userName, externalId, emails และ active

ปิดใช้งานผู้ใช้ (ลบแบบซอฟต์)

DELETE /api/v1/scim/v2/Users/:id

SnapOtter ไม่ลบผู้ใช้แบบถาวรผ่าน SCIM แต่ DELETE จะทำการปิดใช้งานแบบซอฟต์แทน:

  1. บทบาทของผู้ใช้จะถูกเปลี่ยนจากค่าปัจจุบัน (เช่น editor) เป็น disabled:editor โดยเก็บบทบาทเดิมไว้
  2. รหัสผ่านของผู้ใช้จะถูกล้าง
  3. เซสชันที่ใช้งานอยู่ทั้งหมดจะถูกเพิกถอน
  4. คีย์ API ทั้งหมดจะถูกเพิกถอน

ผู้ใช้จะไม่สามารถเข้าสู่ระบบหรือใช้คีย์ API ใด ๆ ได้อีกต่อไป ข้อมูลของพวกเขา (ไฟล์ ประวัติ) จะยังคงถูกเก็บไว้

เปิดใช้งานผู้ใช้อีกครั้ง

หากต้องการเปิดใช้งานผู้ใช้ที่เคยถูกปิดใช้งานไปแล้วอีกครั้ง ให้ส่งคำขอ PUT หรือ PATCH พร้อม active: true SnapOtter จะกู้คืนบทบาทเดิมจากก่อนการปิดใช้งาน (เช่น disabled:editor จะกลายเป็น editor อีกครั้ง) หากไม่สามารถระบุบทบาทเดิมได้ จะย้อนกลับไปใช้ user

::: details ตัวอย่าง: ปิดใช้งานและเปิดใช้งานอีกครั้งผ่าน PATCH

// Deactivate
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    { "op": "replace", "path": "active", "value": false }
  ]
}

// Reactivate
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    { "op": "replace", "path": "active", "value": true }
  ]
}

:::

การดำเนินการกับกลุ่ม

Groups ใน SCIM แมปกับทีมของ SnapOtter การสร้างกลุ่มจะสร้างทีม การเป็นสมาชิกกลุ่มจะควบคุมว่าผู้ใช้อยู่ในทีมใด

สร้างกลุ่ม

POST /api/v1/scim/v2/Groups

จำเป็น: displayName ทางเลือก: members (อาร์เรย์ของ { value: userId })

แสดงรายการและกรองกลุ่ม

GET /api/v1/scim/v2/Groups

การกรองรองรับเฉพาะ displayName eq "..." เท่านั้น แบ่งหน้าด้วย startIndex และ count (สูงสุด 200 ผลลัพธ์ต่อหน้า)

รับข้อมูลกลุ่ม

GET /api/v1/scim/v2/Groups/:id

แทนที่กลุ่ม

PUT /api/v1/scim/v2/Groups/:id

แทนที่ชื่อกลุ่มและรายการสมาชิกทั้งหมด สมาชิกที่มีอยู่แต่ไม่อยู่ในรายการใหม่จะถูกย้ายไปยังทีม Default

แพตช์กลุ่ม

PATCH /api/v1/scim/v2/Groups/:id

รองรับการดำเนินการเหล่านี้:

การดำเนินการ Path ผลลัพธ์
add members เพิ่มผู้ใช้เข้าทีม
remove members[value eq "userId"] ย้ายผู้ใช้ไปยังทีม Default
replace displayName เปลี่ยนชื่อทีม
replace members แทนที่สมาชิกทั้งหมด (สมาชิกที่ถูกลบออกจะย้ายไปยังทีม Default)

ลบกลุ่ม

DELETE /api/v1/scim/v2/Groups/:id

ลบทีม สมาชิกทั้งหมดของทีมที่ถูกลบจะถูกย้ายไปยังทีม Default ผู้ใช้จะไม่ถูกปิดใช้งานหรือลบ

การตั้งค่า IdP

Okta

  1. ในคอนโซลผู้ดูแลระบบ Okta ให้เปิดแอปพลิเคชัน SnapOtter ของคุณ (หรือสร้างใหม่)
  2. ไปที่แท็บ Provisioning แล้วคลิก Configure API Integration
  3. เลือก Enable API Integration และป้อน:
    • Base URL: https://photos.example.com/api/v1/scim/v2
    • API Token: โทเคน bearer สำหรับ SCIM ที่สร้างไว้ข้างต้น
  4. คลิก Test API Credentials แล้วคลิก Save
  5. ภายใต้ Provisioning > To App ให้เปิดใช้งาน:
    • Create Users
    • Update User Attributes
    • Deactivate Users
  6. ภายใต้ Push Groups ให้กำหนดค่าว่ากลุ่ม Okta ใดที่จะซิงก์เป็นทีมของ SnapOtter

Azure AD / Entra ID

  1. ในพอร์ทัล Azure ให้ไปที่แอปพลิเคชันสำหรับองค์กร SnapOtter ของคุณ
  2. ไปที่ Provisioning และตั้ง Provisioning Mode เป็น Automatic
  3. ภายใต้ Admin Credentials ให้ป้อน:
    • Tenant URL: https://photos.example.com/api/v1/scim/v2
    • Secret Token: โทเคน bearer สำหรับ SCIM ที่สร้างไว้ข้างต้น
  4. คลิก Test Connection แล้วคลิก Save
  5. ภายใต้ Mappings ให้กำหนดค่าการแมปแอตทริบิวต์ของผู้ใช้และกลุ่ม โดยปกติค่าเริ่มต้นจะใช้งานได้ แต่ให้ตรวจสอบว่า userName แมปกับ userPrincipalName หรือ mail ตามที่ต้องการ
  6. ตั้ง Provisioning Status เป็น On แล้วบันทึก

Azure จัดสรรผู้ใช้และกลุ่มตามรอบการซิงก์ที่กำหนดไว้ (โดยทั่วไปทุก ๆ 40 นาที)

ปลายทางสำหรับ Discovery

ปลายทางทั้งสามนี้ใช้งานได้โดยไม่ต้องยืนยันตัวตน และอธิบายความสามารถของเซิร์ฟเวอร์ SCIM:

ปลายทาง คำอธิบาย
GET /api/v1/scim/v2/ServiceProviderConfig ความสามารถของเซิร์ฟเวอร์และคุณสมบัติที่รองรับ
GET /api/v1/scim/v2/Schemas คำนิยาม schema ของ User และ Group
GET /api/v1/scim/v2/ResourceTypes ประเภททรัพยากรที่ใช้ได้ (User, Group)

ServiceProviderConfig ประกาศความสามารถเหล่านี้:

คุณสมบัติ รองรับ
Patch ได้
Bulk ไม่ได้
Filter ได้ (สูงสุด 200 ผลลัพธ์ ตัวดำเนินการ eq เท่านั้น)
Change password ไม่ได้
Sort ไม่ได้
ETag ไม่ได้

ข้อจำกัด

  • การกรอง: รองรับเฉพาะตัวดำเนินการ eq เท่านั้น ตัวกรองที่ซับซ้อน ตัวดำเนินการ and/or, co (contains) และ sw (starts with) ไม่ได้ถูกนำมาใช้งาน
  • การดำเนินการแบบ Bulk: ไม่รองรับ
  • Sort และ ETag: ไม่รองรับ
  • บทบาท: SCIM ไม่สามารถกำหนดบทบาทของ SnapOtter ได้ ผู้ใช้ที่จัดสรรทั้งหมดจะได้รับบทบาท user
  • MAX_USERS: ขีดจำกัดของตัวแปรสภาพแวดล้อม MAX_USERS ไม่ได้ถูกบังคับใช้เมื่อสร้างผู้ใช้ผ่าน SCIM หากคุณต้องการจำกัดจำนวนผู้ใช้ ให้จัดการการมอบหมายใน IdP ของคุณ
  • หนึ่งโทเคน: มีโทเคน SCIM ที่ใช้งานได้เพียงหนึ่งโทเคนในแต่ละครั้ง หาก IdP หลายรายต้องเข้าถึง SCIM พวกเขาต้องใช้โทเคนร่วมกัน
  • กลุ่มคือทีม: Groups ใน SCIM สอดคล้องกับทีม ไม่ใช่บทบาทหรือกลุ่มสิทธิ์

การแก้ไขปัญหา

403 "SCIM provisioning requires an enterprise license with the scim feature"

ไลเซนส์ของคุณไม่มีคุณสมบัติ scim หรือไม่มีการกำหนดค่าไลเซนส์ SCIM ต้องใช้ไลเซนส์แผน enterprise ตรวจสอบว่าตั้งค่า SNAPOTTER_LICENSE_KEY ไว้แล้วและไลเซนส์รวมคุณสมบัติ scim ด้วย

401 "Bearer token required"

คำขอ SCIM ไม่ได้รวมส่วนหัว Authorization: Bearer <token> ตรวจสอบการกำหนดค่าการจัดสรรของ IdP ของคุณ

401 "Invalid token"

โทเค็นมีรูปแบบไม่ถูกต้อง ใช้รูปแบบที่เลิกใช้แล้ว หรือไม่ตรงกับแฮชที่เก็บไว้ สร้างโทเค็น so_scim_v2_... ปัจจุบันและอัปเดตโทเค็นในการตั้งค่าการจัดสรรของ IdP

401 "SCIM not configured"

ยังไม่มีการสร้างโทเคน SCIM ใช้ปลายทาง POST /api/v1/enterprise/scim/token เพื่อสร้างโทเคน

409 "User already exists" / "userName already taken"

มีผู้ใช้ที่มีชื่อผู้ใช้เดียวกันอยู่แล้ว กรณีนี้สามารถเกิดขึ้นได้เมื่อ IdP ลองสร้างใหม่หลังจากที่ล้มเหลว ตรวจสอบชื่อผู้ใช้ที่ซ้ำกันในแผงผู้ดูแลระบบของ SnapOtter

429 "SCIM rate limit exceeded"

IdP กำลังส่งคำขอมากกว่า 1000 คำขอต่อนาที กรณีนี้มักเกิดขึ้นระหว่างการซิงก์ครั้งแรกขนาดใหญ่ IdP ส่วนใหญ่จะลองใหม่โดยอัตโนมัติหลังจากหน้าต่างการจำกัดอัตรารีเซ็ต หากปัญหายังคงอยู่ ให้ตรวจสอบช่วงเวลาการซิงก์การจัดสรรของ IdP ของคุณ

ผู้ใช้ถูกยกเลิกการจัดสรรแต่ไม่ถูกลบออกจาก UI

DELETE ของ SCIM คือการปิดใช้งานแบบซอฟต์ ผู้ใช้ที่ถูกปิดใช้งานจะยังคงปรากฏในรายการผู้ใช้ของผู้ดูแลระบบพร้อมสถานะปิดใช้งาน นี่เป็นการออกแบบเพื่อให้ข้อมูลของพวกเขาถูกเก็บรักษาไว้ บทบาทของพวกเขาจะแสดงเป็น disabled:<original-role>