1
0
forked from baron/baron-sso

관리자 비밀번호 변경을 Kratos 해시 업데이트 방식으로 수정

This commit is contained in:
2026-03-31 10:28:27 +09:00
parent 4d8b9d9f87
commit 2364ff59d2
6 changed files with 335 additions and 42 deletions

View File

@@ -14,6 +14,8 @@ import (
"os"
"strings"
"time"
"golang.org/x/crypto/bcrypt"
)
// OryProvider는 Kratos/Hydra를 기반으로 하는 IDP 어댑터의 최소 스켈레톤입니다.
@@ -711,20 +713,53 @@ func (o *OryProvider) UpdateUserPassword(loginID, newPassword string, r *http.Re
return fmt.Errorf("ory provider: identity not found for loginID=%s", loginID)
}
patchOps := []map[string]interface{}{
{
"op": "add",
"path": "/credentials/password/config/password",
"value": newPassword,
},
identity, err := o.getIdentity(identityID)
if err != nil {
return fmt.Errorf("ory provider: load identity failed: %w", err)
}
if identity == nil {
return fmt.Errorf("ory provider: identity payload missing for loginID=%s", loginID)
}
body, _ := json.Marshal(patchOps)
req, err := http.NewRequestWithContext(context.Background(), http.MethodPatch, fmt.Sprintf("%s/admin/identities/%s", o.KratosAdminURL, identityID), bytes.NewReader(body))
hashedPassword, err := hashPasswordForKratos(newPassword)
if err != nil {
return fmt.Errorf("ory provider: hash password failed: %w", err)
}
payload := map[string]interface{}{
"schema_id": identity.SchemaID,
"traits": identity.Traits,
"state": identity.State,
"credentials": map[string]interface{}{
"password": map[string]interface{}{
"config": map[string]string{
"hashed_password": hashedPassword,
},
},
},
}
if payload["schema_id"] == "" {
payload["schema_id"] = "default"
}
if payload["state"] == "" {
payload["state"] = "active"
}
if identity.MetadataAdmin != nil {
payload["metadata_admin"] = identity.MetadataAdmin
}
if identity.MetadataPublic != nil {
payload["metadata_public"] = identity.MetadataPublic
}
if identity.ExternalID != "" {
payload["external_id"] = identity.ExternalID
}
body, _ := json.Marshal(payload)
req, err := http.NewRequestWithContext(context.Background(), http.MethodPut, fmt.Sprintf("%s/admin/identities/%s", o.KratosAdminURL, identityID), bytes.NewReader(body))
if err != nil {
return fmt.Errorf("ory provider: build request failed: %w", err)
}
req.Header.Set("Content-Type", "application/json-patch+json")
req.Header.Set("Content-Type", "application/json")
resp, err := o.httpClient().Do(req)
if err != nil {
@@ -789,6 +824,41 @@ func (o *OryProvider) findIdentityID(loginID string) (string, error) {
return identities[0].ID, nil
}
func (o *OryProvider) getIdentity(identityID string) (*KratosIdentity, error) {
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, fmt.Sprintf("%s/admin/identities/%s", o.KratosAdminURL, identityID), nil)
if err != nil {
return nil, err
}
resp, err := o.httpClient().Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return nil, nil
}
if resp.StatusCode >= 300 {
respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
return nil, fmt.Errorf("ory provider: get identity failed status=%d body=%s", resp.StatusCode, string(respBody))
}
var identity KratosIdentity
if err := json.NewDecoder(resp.Body).Decode(&identity); err != nil {
return nil, err
}
return &identity, nil
}
func hashPasswordForKratos(password string) (string, error) {
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hashed), nil
}
func (o *OryProvider) httpClient() *http.Client {
if o.HTTPClient != nil {
return o.HTTPClient