user_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. package state
  2. import (
  3. "testing"
  4. "time"
  5. "github.com/stretchr/testify/assert"
  6. "github.com/stretchr/testify/require"
  7. "github.com/mk6i/open-oscar-server/wire"
  8. )
  9. func TestUser_HashPassword(t *testing.T) {
  10. tests := []struct {
  11. name string
  12. user User
  13. password string
  14. expectedWeakMD5 []byte
  15. expectedStrongMD5 []byte
  16. wantError bool
  17. }{
  18. {
  19. name: "Valid AIM password",
  20. user: User{AuthKey: "someAuthKey", IsICQ: false},
  21. password: "validPassword",
  22. expectedWeakMD5: wire.WeakMD5PasswordHash("validPassword", "someAuthKey"),
  23. expectedStrongMD5: wire.StrongMD5PasswordHash("validPassword", "someAuthKey"),
  24. wantError: false,
  25. },
  26. {
  27. name: "Empty AIM password",
  28. user: User{AuthKey: "someAuthKey", IsICQ: false},
  29. password: "",
  30. expectedWeakMD5: nil,
  31. expectedStrongMD5: nil,
  32. wantError: true,
  33. },
  34. {
  35. name: "AIM password too short",
  36. user: User{AuthKey: "someAuthKey", IsICQ: false},
  37. password: "abc",
  38. expectedWeakMD5: nil,
  39. expectedStrongMD5: nil,
  40. wantError: true,
  41. },
  42. {
  43. name: "AIM password too long",
  44. user: User{AuthKey: "someAuthKey", IsICQ: false},
  45. password: "thispasswordistoolong",
  46. expectedWeakMD5: nil,
  47. expectedStrongMD5: nil,
  48. wantError: true,
  49. },
  50. {
  51. name: "Valid ICQ password",
  52. user: User{AuthKey: "someAuthKey", IsICQ: true},
  53. password: "validICQ",
  54. expectedWeakMD5: wire.WeakMD5PasswordHash("validICQ", "someAuthKey"),
  55. expectedStrongMD5: wire.StrongMD5PasswordHash("validICQ", "someAuthKey"),
  56. wantError: false,
  57. },
  58. {
  59. name: "Empty ICQ password",
  60. user: User{AuthKey: "someAuthKey", IsICQ: true},
  61. password: "",
  62. expectedWeakMD5: nil,
  63. expectedStrongMD5: nil,
  64. wantError: true,
  65. },
  66. {
  67. name: "ICQ password too long",
  68. user: User{AuthKey: "someAuthKey", IsICQ: true},
  69. password: "icqpass89",
  70. expectedWeakMD5: nil,
  71. expectedStrongMD5: nil,
  72. wantError: true,
  73. },
  74. }
  75. for _, tt := range tests {
  76. t.Run(tt.name, func(t *testing.T) {
  77. err := tt.user.HashPassword(tt.password)
  78. if tt.wantError {
  79. require.Error(t, err)
  80. } else {
  81. require.NoError(t, err)
  82. assert.Equal(t, tt.expectedWeakMD5, tt.user.WeakMD5Pass)
  83. assert.Equal(t, tt.expectedStrongMD5, tt.user.StrongMD5Pass)
  84. }
  85. })
  86. }
  87. }
  88. func TestAge(t *testing.T) {
  89. tests := []struct {
  90. name string
  91. user User
  92. timeNow func() time.Time
  93. expectedAge uint16
  94. }{
  95. {
  96. name: "Valid birthday, only year is set",
  97. user: User{
  98. ICQInfo: ICQInfo{
  99. More: ICQMoreInfo{
  100. BirthYear: 1990,
  101. },
  102. },
  103. },
  104. timeNow: func() time.Time {
  105. return time.Date(2024, 8, 1, 0, 0, 0, 0, time.UTC)
  106. },
  107. expectedAge: 34,
  108. },
  109. {
  110. name: "Valid birthday, birthday passed this year",
  111. user: User{
  112. ICQInfo: ICQInfo{
  113. More: ICQMoreInfo{
  114. BirthYear: 1990,
  115. BirthMonth: 5,
  116. BirthDay: 10,
  117. },
  118. },
  119. },
  120. timeNow: func() time.Time {
  121. return time.Date(2024, 8, 1, 0, 0, 0, 0, time.UTC)
  122. },
  123. expectedAge: 34,
  124. },
  125. {
  126. name: "Valid birthday, birthday not yet passed this year",
  127. user: User{
  128. ICQInfo: ICQInfo{
  129. More: ICQMoreInfo{
  130. BirthYear: 1990,
  131. BirthMonth: 12,
  132. BirthDay: 10,
  133. },
  134. },
  135. },
  136. timeNow: func() time.Time {
  137. return time.Date(2024, 8, 1, 0, 0, 0, 0, time.UTC)
  138. },
  139. expectedAge: 33,
  140. },
  141. {
  142. name: "Birthday is today",
  143. user: User{
  144. ICQInfo: ICQInfo{
  145. More: ICQMoreInfo{
  146. BirthYear: 1990,
  147. BirthMonth: 8,
  148. BirthDay: 1,
  149. },
  150. },
  151. },
  152. timeNow: func() time.Time {
  153. return time.Date(2024, 8, 1, 0, 0, 0, 0, time.UTC)
  154. },
  155. expectedAge: 34,
  156. },
  157. {
  158. name: "Invalid birthday, year is zero",
  159. user: User{
  160. ICQInfo: ICQInfo{
  161. More: ICQMoreInfo{
  162. BirthYear: 0,
  163. BirthMonth: 8,
  164. BirthDay: 1,
  165. },
  166. },
  167. },
  168. timeNow: func() time.Time {
  169. return time.Date(2024, 8, 1, 0, 0, 0, 0, time.UTC)
  170. },
  171. expectedAge: 0,
  172. },
  173. {
  174. name: "Invalid birthday, day is zero",
  175. user: User{
  176. ICQInfo: ICQInfo{
  177. More: ICQMoreInfo{
  178. BirthYear: 1990,
  179. BirthMonth: 8,
  180. BirthDay: 0,
  181. },
  182. },
  183. },
  184. timeNow: func() time.Time {
  185. return time.Date(2024, 8, 1, 0, 0, 0, 0, time.UTC)
  186. },
  187. expectedAge: 0,
  188. },
  189. {
  190. name: "Invalid birthday, month is zero",
  191. user: User{
  192. ICQInfo: ICQInfo{
  193. More: ICQMoreInfo{
  194. BirthYear: 1990,
  195. BirthMonth: 0,
  196. BirthDay: 1,
  197. },
  198. },
  199. },
  200. timeNow: func() time.Time {
  201. return time.Date(2024, 8, 1, 0, 0, 0, 0, time.UTC)
  202. },
  203. expectedAge: 0,
  204. },
  205. }
  206. for _, tt := range tests {
  207. t.Run(tt.name, func(t *testing.T) {
  208. age := tt.user.Age(tt.timeNow)
  209. if age != tt.expectedAge {
  210. t.Errorf("expected age %d, got %d", tt.expectedAge, age)
  211. }
  212. })
  213. }
  214. }
  215. func TestDisplayScreenName_ValidateAIMHandle(t *testing.T) {
  216. tests := []struct {
  217. name string
  218. input DisplayScreenName
  219. wantErr error
  220. }{
  221. {"Valid handle no spaces", "User123", nil},
  222. {"Valid handle with min character count and space", "U SR", nil},
  223. {"Valid handle with min character including letters and numbers", "dj3520", nil},
  224. {"Valid handle with max character count", "JustTheRightSize", nil},
  225. {"Valid handle with max character count and spaces", "Just RightSize", nil},
  226. {"Too short", "Us", ErrAIMHandleLength},
  227. {"Too short due to spaces", "U S", ErrAIMHandleLength},
  228. {"Too long", "ThisIsAReallyLongScreenName", ErrAIMHandleLength},
  229. {"Too many spaces", "User 123 ", ErrAIMHandleLength},
  230. {"Starts with number", "1User", ErrAIMHandleInvalidFormat},
  231. {"Ends with space", "User123 ", ErrAIMHandleInvalidFormat},
  232. {"Contains invalid character", "User@123", ErrAIMHandleInvalidFormat},
  233. }
  234. for _, tt := range tests {
  235. t.Run(tt.name, func(t *testing.T) {
  236. err := tt.input.ValidateAIMHandle()
  237. if tt.wantErr != nil {
  238. assert.ErrorIs(t, err, tt.wantErr, "ValidateAIMHandle() error = %v, wantErr %v", err, tt.wantErr)
  239. } else {
  240. assert.NoError(t, err, "ValidateAIMHandle() error = %v, wantErr %v", err, tt.wantErr)
  241. }
  242. })
  243. }
  244. }
  245. func TestDisplayScreenName_ValidateICQHandle(t *testing.T) {
  246. tests := []struct {
  247. name string
  248. input DisplayScreenName
  249. wantErr error
  250. }{
  251. {"Valid UIN", "123456", nil},
  252. {"Too low", "9999", ErrICQUINInvalidFormat},
  253. {"Too high", "2147483647", ErrICQUINInvalidFormat},
  254. {"Non-numeric", "abcd", ErrICQUINInvalidFormat},
  255. }
  256. for _, tt := range tests {
  257. t.Run(tt.name, func(t *testing.T) {
  258. err := tt.input.ValidateUIN()
  259. if tt.wantErr != nil {
  260. assert.ErrorIs(t, err, tt.wantErr, "ValidateUIN() error = %v, wantErr %v", err, tt.wantErr)
  261. } else {
  262. assert.NoError(t, err, "ValidateUIN() error = %v, wantErr %v", err, tt.wantErr)
  263. }
  264. })
  265. }
  266. }
  267. func TestUser_ValidateRoastedPass(t *testing.T) {
  268. tests := []struct {
  269. name string
  270. user User
  271. roastedPass []byte
  272. expected bool
  273. }{
  274. {
  275. name: "Valid roasted password",
  276. user: User{
  277. AuthKey: "testAuthKey",
  278. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  279. },
  280. roastedPass: wire.RoastOSCARPassword([]byte("testPassword")),
  281. expected: true,
  282. },
  283. {
  284. name: "Invalid roasted password",
  285. user: User{
  286. AuthKey: "testAuthKey",
  287. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  288. },
  289. roastedPass: wire.RoastOSCARPassword([]byte("wrongPassword")),
  290. expected: false,
  291. },
  292. {
  293. name: "Empty roasted password",
  294. user: User{
  295. AuthKey: "testAuthKey",
  296. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  297. },
  298. roastedPass: wire.RoastOSCARPassword([]byte("")),
  299. expected: false,
  300. },
  301. {
  302. name: "Empty stored password",
  303. user: User{
  304. AuthKey: "testAuthKey",
  305. WeakMD5Pass: []byte{},
  306. },
  307. roastedPass: wire.RoastOSCARPassword([]byte("testPassword")),
  308. expected: false,
  309. },
  310. }
  311. for _, tt := range tests {
  312. t.Run(tt.name, func(t *testing.T) {
  313. result := tt.user.ValidateRoastedPass(tt.roastedPass)
  314. assert.Equal(t, tt.expected, result)
  315. })
  316. }
  317. }
  318. func TestUser_ValidateRoastedJavaPass(t *testing.T) {
  319. tests := []struct {
  320. name string
  321. user User
  322. roastedPass []byte
  323. expected bool
  324. }{
  325. {
  326. name: "Valid roasted Java password",
  327. user: User{
  328. AuthKey: "testAuthKey",
  329. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  330. },
  331. roastedPass: wire.RoastOSCARJavaPassword([]byte("testPassword")),
  332. expected: true,
  333. },
  334. {
  335. name: "Invalid roasted Java password",
  336. user: User{
  337. AuthKey: "testAuthKey",
  338. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  339. },
  340. roastedPass: wire.RoastOSCARJavaPassword([]byte("wrongPassword")),
  341. expected: false,
  342. },
  343. {
  344. name: "Empty roasted Java password",
  345. user: User{
  346. AuthKey: "testAuthKey",
  347. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  348. },
  349. roastedPass: wire.RoastOSCARJavaPassword([]byte("")),
  350. expected: false,
  351. },
  352. {
  353. name: "Empty stored password",
  354. user: User{
  355. AuthKey: "testAuthKey",
  356. WeakMD5Pass: []byte{},
  357. },
  358. roastedPass: wire.RoastOSCARJavaPassword([]byte("testPassword")),
  359. expected: false,
  360. },
  361. }
  362. for _, tt := range tests {
  363. t.Run(tt.name, func(t *testing.T) {
  364. result := tt.user.ValidateRoastedJavaPass(tt.roastedPass)
  365. assert.Equal(t, tt.expected, result)
  366. })
  367. }
  368. }
  369. func TestUser_ValidateRoastedTOCPass(t *testing.T) {
  370. tests := []struct {
  371. name string
  372. user User
  373. roastedPass []byte
  374. expected bool
  375. }{
  376. {
  377. name: "Valid roasted TOC password",
  378. user: User{
  379. AuthKey: "testAuthKey",
  380. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  381. },
  382. roastedPass: wire.RoastTOCPassword([]byte("testPassword")),
  383. expected: true,
  384. },
  385. {
  386. name: "Invalid roasted TOC password",
  387. user: User{
  388. AuthKey: "testAuthKey",
  389. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  390. },
  391. roastedPass: wire.RoastTOCPassword([]byte("wrongPassword")),
  392. expected: false,
  393. },
  394. {
  395. name: "Empty roasted TOC password",
  396. user: User{
  397. AuthKey: "testAuthKey",
  398. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  399. },
  400. roastedPass: wire.RoastTOCPassword([]byte("")),
  401. expected: false,
  402. },
  403. {
  404. name: "Empty stored password",
  405. user: User{
  406. AuthKey: "testAuthKey",
  407. WeakMD5Pass: []byte{},
  408. },
  409. roastedPass: wire.RoastTOCPassword([]byte("testPassword")),
  410. expected: false,
  411. },
  412. }
  413. for _, tt := range tests {
  414. t.Run(tt.name, func(t *testing.T) {
  415. result := tt.user.ValidateRoastedTOCPass(tt.roastedPass)
  416. assert.Equal(t, tt.expected, result)
  417. })
  418. }
  419. }
  420. func TestUser_ValidatePlaintextPass(t *testing.T) {
  421. tests := []struct {
  422. name string
  423. user User
  424. plaintextPass []byte
  425. expected bool
  426. }{
  427. {
  428. name: "Valid plaintext password",
  429. user: User{
  430. AuthKey: "testAuthKey",
  431. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  432. },
  433. plaintextPass: []byte("testPassword"),
  434. expected: true,
  435. },
  436. {
  437. name: "Invalid plaintext password",
  438. user: User{
  439. AuthKey: "testAuthKey",
  440. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  441. },
  442. plaintextPass: []byte("wrongPassword"),
  443. expected: false,
  444. },
  445. {
  446. name: "Empty plaintext password",
  447. user: User{
  448. AuthKey: "testAuthKey",
  449. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  450. },
  451. plaintextPass: []byte(""),
  452. expected: false,
  453. },
  454. {
  455. name: "Empty stored password",
  456. user: User{
  457. AuthKey: "testAuthKey",
  458. WeakMD5Pass: []byte{},
  459. },
  460. plaintextPass: []byte("testPassword"),
  461. expected: false,
  462. },
  463. {
  464. name: "Password with special characters",
  465. user: User{
  466. AuthKey: "testAuthKey",
  467. WeakMD5Pass: wire.WeakMD5PasswordHash("test@123!", "testAuthKey"),
  468. },
  469. plaintextPass: []byte("test@123!"),
  470. expected: true,
  471. },
  472. }
  473. for _, tt := range tests {
  474. t.Run(tt.name, func(t *testing.T) {
  475. result := tt.user.ValidatePlaintextPass(tt.plaintextPass)
  476. assert.Equal(t, tt.expected, result)
  477. })
  478. }
  479. }
  480. func TestUser_ValidateRoastedKerberosPass(t *testing.T) {
  481. tests := []struct {
  482. name string
  483. user User
  484. roastedPass []byte
  485. expected bool
  486. }{
  487. {
  488. name: "Valid roasted Kerberos password",
  489. user: User{
  490. AuthKey: "testAuthKey",
  491. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  492. },
  493. roastedPass: wire.RoastKerberosPassword([]byte("testPassword")),
  494. expected: true,
  495. },
  496. {
  497. name: "Invalid roasted Kerberos password",
  498. user: User{
  499. AuthKey: "testAuthKey",
  500. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  501. },
  502. roastedPass: wire.RoastKerberosPassword([]byte("wrongPassword")),
  503. expected: false,
  504. },
  505. {
  506. name: "Empty roasted Kerberos password",
  507. user: User{
  508. AuthKey: "testAuthKey",
  509. WeakMD5Pass: wire.WeakMD5PasswordHash("testPassword", "testAuthKey"),
  510. },
  511. roastedPass: wire.RoastKerberosPassword([]byte("")),
  512. expected: false,
  513. },
  514. {
  515. name: "Empty stored password",
  516. user: User{
  517. AuthKey: "testAuthKey",
  518. WeakMD5Pass: []byte{},
  519. },
  520. roastedPass: wire.RoastKerberosPassword([]byte("testPassword")),
  521. expected: false,
  522. },
  523. }
  524. for _, tt := range tests {
  525. t.Run(tt.name, func(t *testing.T) {
  526. result := tt.user.ValidateRoastedKerberosPass(tt.roastedPass)
  527. assert.Equal(t, tt.expected, result)
  528. })
  529. }
  530. }