chore: 初始化 idsync 项目
飞书通讯录 ↔ LDAP 同步工具的首个提交。 - internal/feishu: 飞书通讯录客户端、快照与重试 - internal/ldaplib: LDAP 读写与批量操作 - internal/mapping: 飞书部门/用户到 LDAP 条目的映射 - internal/syncer: 同步计划生成、执行与调度 - internal/store: SQLite 台账 - internal/server + internal/webui: 管理后台与内嵌前端产物 - internal/password: SSHA 口令处理 - config.example.yml / Dockerfile / Makefile / web 前端源码 真实凭据(config.yml)、运行时数据(data/、backup/)与构建产物(bin/)不入库。
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEncodeSSHA(t *testing.T) {
|
||||
const plain = "correct horse battery staple"
|
||||
|
||||
encoded, err := EncodeSSHA(plain)
|
||||
if err != nil {
|
||||
t.Fatalf("编码失败: %v", err)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(encoded, "{SSHA}") {
|
||||
t.Errorf("结果应以 {SSHA} 开头,实际: %q", encoded)
|
||||
}
|
||||
|
||||
// 关键性质:明文绝不能出现在结果里。
|
||||
// slapd 不会自动哈希 Modify 写入的 userPassword,因此这一步是
|
||||
// 密码不落明文存储的唯一保障。
|
||||
if strings.Contains(encoded, plain) {
|
||||
t.Errorf("编码结果中出现了明文密码: %q", encoded)
|
||||
}
|
||||
|
||||
if !VerifySSHA(encoded, plain) {
|
||||
t.Errorf("正确密码校验失败: %q", encoded)
|
||||
}
|
||||
if VerifySSHA(encoded, plain+"x") {
|
||||
t.Errorf("错误密码不应通过校验")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeSSHASaltIsRandom(t *testing.T) {
|
||||
const plain = "same-password"
|
||||
|
||||
a, err := EncodeSSHA(plain)
|
||||
if err != nil {
|
||||
t.Fatalf("编码失败: %v", err)
|
||||
}
|
||||
b, err := EncodeSSHA(plain)
|
||||
if err != nil {
|
||||
t.Fatalf("编码失败: %v", err)
|
||||
}
|
||||
|
||||
// 随机盐应让同一密码产生不同哈希,避免相同密码在目录中可被关联识别。
|
||||
if a == b {
|
||||
t.Errorf("同一密码两次编码结果相同,说明盐未随机化: %q", a)
|
||||
}
|
||||
if !VerifySSHA(a, plain) || !VerifySSHA(b, plain) {
|
||||
t.Errorf("带随机盐的两个哈希都应能校验通过")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifySSHARejectsMalformed(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
encoded string
|
||||
}{
|
||||
{"空字符串", ""},
|
||||
{"缺少前缀", "abcdef"},
|
||||
{"前缀但非法 base64", "{SSHA}!!!not-base64!!!"},
|
||||
{"长度不足以容纳哈希", "{SSHA}YWJj"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
if VerifySSHA(c.encoded, "whatever") {
|
||||
t.Errorf("畸形输入 %q 不应校验通过", c.encoded)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandom(t *testing.T) {
|
||||
a, err := Random()
|
||||
if err != nil {
|
||||
t.Fatalf("生成失败: %v", err)
|
||||
}
|
||||
b, err := Random()
|
||||
if err != nil {
|
||||
t.Fatalf("生成失败: %v", err)
|
||||
}
|
||||
if a == b {
|
||||
t.Errorf("两次生成的随机密码相同: %q", a)
|
||||
}
|
||||
if len(a) < 20 {
|
||||
t.Errorf("随机密码过短(%d 字符),强度不足", len(a))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user