44 "encoding/json"
55 "net/http/httptest"
66 "path"
7- "slices"
87 "strings"
98 "testing"
109 "time"
@@ -36,6 +35,23 @@ func TestUserController(t *testing.T) {
3635 Password : "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa" , // password
3736 TotpSecret : "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK" ,
3837 },
38+ {
39+ Username : "attruser" ,
40+ Password : "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa" , // password
41+ Attributes : config.UserAttributes {
42+ Name : "Alice Smith" ,
43+ Email : "alice@example.com" ,
44+ },
45+ },
46+ {
47+ Username : "attrtotpuser" ,
48+ Password : "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa" , // password
49+ TotpSecret : "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK" ,
50+ Attributes : config.UserAttributes {
51+ Name : "Bob Jones" ,
52+ Email : "bob@example.com" ,
53+ },
54+ },
3955 },
4056 SessionExpiry : 10 , // 10 seconds, useful for testing
4157 CookieDomain : "example.com" ,
@@ -273,6 +289,64 @@ func TestUserController(t *testing.T) {
273289 assert .Contains (t , recorder .Body .String (), "Too many failed TOTP attempts." )
274290 },
275291 },
292+ {
293+ description : "Login uses name and email from user attributes" ,
294+ middlewares : []gin.HandlerFunc {},
295+ run : func (t * testing.T , router * gin.Engine , recorder * httptest.ResponseRecorder ) {
296+ loginReq := controller.LoginRequest {Username : "attruser" , Password : "password" }
297+ body , err := json .Marshal (loginReq )
298+ require .NoError (t , err )
299+
300+ req := httptest .NewRequest ("POST" , "/api/user/login" , strings .NewReader (string (body )))
301+ req .Header .Set ("Content-Type" , "application/json" )
302+ router .ServeHTTP (recorder , req )
303+
304+ require .Equal (t , 200 , recorder .Code )
305+ cookies := recorder .Result ().Cookies ()
306+ require .Len (t , cookies , 1 )
307+ assert .Equal (t , "tinyauth-session" , cookies [0 ].Name )
308+ },
309+ },
310+ {
311+ description : "Login with TOTP uses name and email from user attributes in pending session" ,
312+ middlewares : []gin.HandlerFunc {},
313+ run : func (t * testing.T , router * gin.Engine , recorder * httptest.ResponseRecorder ) {
314+ loginReq := controller.LoginRequest {Username : "attrtotpuser" , Password : "password" }
315+ body , err := json .Marshal (loginReq )
316+ require .NoError (t , err )
317+
318+ req := httptest .NewRequest ("POST" , "/api/user/login" , strings .NewReader (string (body )))
319+ req .Header .Set ("Content-Type" , "application/json" )
320+ router .ServeHTTP (recorder , req )
321+
322+ require .Equal (t , 200 , recorder .Code )
323+ var res map [string ]any
324+ require .NoError (t , json .Unmarshal (recorder .Body .Bytes (), & res ))
325+ assert .Equal (t , true , res ["totpPending" ])
326+ require .Len (t , recorder .Result ().Cookies (), 1 )
327+ },
328+ },
329+ {
330+ description : "TOTP completion uses name and email from user attributes" ,
331+ middlewares : []gin.HandlerFunc {},
332+ run : func (t * testing.T , router * gin.Engine , recorder * httptest.ResponseRecorder ) {
333+ code , err := totp .GenerateCode ("JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK" , time .Now ())
334+ require .NoError (t , err )
335+
336+ totpReq := controller.TotpRequest {Code : code }
337+ body , err := json .Marshal (totpReq )
338+ require .NoError (t , err )
339+
340+ req := httptest .NewRequest ("POST" , "/api/user/totp" , strings .NewReader (string (body )))
341+ req .Header .Set ("Content-Type" , "application/json" )
342+ router .ServeHTTP (recorder , req )
343+
344+ require .Equal (t , 200 , recorder .Code )
345+ cookies := recorder .Result ().Cookies ()
346+ require .Len (t , cookies , 1 )
347+ assert .Equal (t , "tinyauth-session" , cookies [0 ].Name )
348+ },
349+ },
276350 }
277351
278352 oauthBrokerCfgs := make (map [string ]config.OAuthServiceConfig )
@@ -305,9 +379,31 @@ func TestUserController(t *testing.T) {
305379 authService .ClearRateLimitsTestingOnly ()
306380 }
307381
308- setTotpMiddlewareOverrides := []string {
309- "Should be able to login with totp" ,
310- "Totp should rate limit on multiple invalid attempts" ,
382+ setTotpMiddlewareOverrides := map [string ]config.UserContext {
383+ "Should be able to login with totp" : {
384+ Username : "totpuser" ,
385+ Name : "Totpuser" ,
386+ Email : "totpuser@example.com" ,
387+ Provider : "local" ,
388+ TotpPending : true ,
389+ TotpEnabled : true ,
390+ },
391+ "Totp should rate limit on multiple invalid attempts" : {
392+ Username : "totpuser" ,
393+ Name : "Totpuser" ,
394+ Email : "totpuser@example.com" ,
395+ Provider : "local" ,
396+ TotpPending : true ,
397+ TotpEnabled : true ,
398+ },
399+ "TOTP completion uses name and email from user attributes" : {
400+ Username : "attrtotpuser" ,
401+ Name : "Bob Jones" ,
402+ Email : "bob@example.com" ,
403+ Provider : "local" ,
404+ TotpPending : true ,
405+ TotpEnabled : true ,
406+ },
311407 }
312408
313409 for _ , test := range tests {
@@ -321,18 +417,10 @@ func TestUserController(t *testing.T) {
321417
322418 // Gin is stupid and doesn't allow setting a middleware after the groups
323419 // so we need to do some stupid overrides here
324- if slices .Contains (setTotpMiddlewareOverrides , test .description ) {
325- // Assuming the cookie is set, it should be picked up by the
326- // context middleware
420+ if ctx , ok := setTotpMiddlewareOverrides [test .description ]; ok {
421+ ctx := ctx
327422 router .Use (func (c * gin.Context ) {
328- c .Set ("context" , & config.UserContext {
329- Username : "totpuser" ,
330- Name : "Totpuser" ,
331- Email : "totpuser@example.com" ,
332- Provider : "local" ,
333- TotpPending : true ,
334- TotpEnabled : true ,
335- })
423+ c .Set ("context" , & ctx )
336424 })
337425 }
338426
@@ -353,150 +441,3 @@ func TestUserController(t *testing.T) {
353441 require .NoError (t , err )
354442 })
355443}
356-
357- func TestUserControllerAttributes (t * testing.T ) {
358- tlog .NewTestLogger ().Init ()
359- tempDir := t .TempDir ()
360-
361- authServiceCfg := service.AuthServiceConfig {
362- Users : []config.User {
363- {
364- Username : "attruser" ,
365- Password : "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa" , // password
366- Attributes : config.UserAttributes {
367- Name : "Alice Smith" ,
368- Email : "alice@example.com" ,
369- },
370- },
371- {
372- Username : "attrtotpuser" ,
373- Password : "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa" , // password
374- TotpSecret : "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK" ,
375- Attributes : config.UserAttributes {
376- Name : "Bob Jones" ,
377- Email : "bob@example.com" ,
378- },
379- },
380- },
381- SessionExpiry : 10 ,
382- CookieDomain : "example.com" ,
383- LoginTimeout : 10 ,
384- LoginMaxRetries : 3 ,
385- SessionCookieName : "tinyauth-session" ,
386- }
387-
388- userControllerCfg := controller.UserControllerConfig {
389- CookieDomain : "example.com" ,
390- }
391-
392- app := bootstrap .NewBootstrapApp (config.Config {})
393- db , err := app .SetupDatabase (path .Join (tempDir , "tinyauth_attrs.db" ))
394- require .NoError (t , err )
395-
396- queries := repository .New (db )
397-
398- docker := service .NewDockerService ()
399- err = docker .Init ()
400- require .NoError (t , err )
401-
402- ldap := service .NewLdapService (service.LdapServiceConfig {})
403- err = ldap .Init ()
404- require .NoError (t , err )
405-
406- broker := service .NewOAuthBrokerService (make (map [string ]config.OAuthServiceConfig ))
407- err = broker .Init ()
408- require .NoError (t , err )
409-
410- authService := service .NewAuthService (authServiceCfg , docker , ldap , queries , broker )
411- err = authService .Init ()
412- require .NoError (t , err )
413-
414- makeRouter := func (extraMiddlewares ... gin.HandlerFunc ) * gin.Engine {
415- router := gin .Default ()
416- for _ , m := range extraMiddlewares {
417- router .Use (m )
418- }
419- gin .SetMode (gin .TestMode )
420- group := router .Group ("/api" )
421- ctrl := controller .NewUserController (userControllerCfg , group , authService )
422- ctrl .SetupRoutes ()
423- return router
424- }
425-
426- t .Run ("Login uses name and email from user attributes" , func (t * testing.T ) {
427- authService .ClearRateLimitsTestingOnly ()
428- router := makeRouter ()
429-
430- loginReq := controller.LoginRequest {Username : "attruser" , Password : "password" }
431- body , err := json .Marshal (loginReq )
432- require .NoError (t , err )
433-
434- req := httptest .NewRequest ("POST" , "/api/user/login" , strings .NewReader (string (body )))
435- req .Header .Set ("Content-Type" , "application/json" )
436- rec := httptest .NewRecorder ()
437- router .ServeHTTP (rec , req )
438-
439- require .Equal (t , 200 , rec .Code )
440- cookies := rec .Result ().Cookies ()
441- require .Len (t , cookies , 1 )
442- assert .Equal (t , "tinyauth-session" , cookies [0 ].Name )
443- })
444-
445- t .Run ("Login with TOTP uses name and email from user attributes in pending session" , func (t * testing.T ) {
446- authService .ClearRateLimitsTestingOnly ()
447- router := makeRouter ()
448-
449- loginReq := controller.LoginRequest {Username : "attrtotpuser" , Password : "password" }
450- body , err := json .Marshal (loginReq )
451- require .NoError (t , err )
452-
453- req := httptest .NewRequest ("POST" , "/api/user/login" , strings .NewReader (string (body )))
454- req .Header .Set ("Content-Type" , "application/json" )
455- rec := httptest .NewRecorder ()
456- router .ServeHTTP (rec , req )
457-
458- require .Equal (t , 200 , rec .Code )
459- var res map [string ]any
460- require .NoError (t , json .Unmarshal (rec .Body .Bytes (), & res ))
461- assert .Equal (t , true , res ["totpPending" ])
462- require .Len (t , rec .Result ().Cookies (), 1 )
463- })
464-
465- t .Run ("TOTP completion uses name and email from user attributes" , func (t * testing.T ) {
466- authService .ClearRateLimitsTestingOnly ()
467-
468- // First: login to get TOTP-pending session
469- router := makeRouter (func (c * gin.Context ) {
470- c .Set ("context" , & config.UserContext {
471- Username : "attrtotpuser" ,
472- Name : "Bob Jones" ,
473- Email : "bob@example.com" ,
474- Provider : "local" ,
475- TotpPending : true ,
476- TotpEnabled : true ,
477- })
478- })
479-
480- code , err := totp .GenerateCode ("JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK" , time .Now ())
481- require .NoError (t , err )
482-
483- totpReq := controller.TotpRequest {Code : code }
484- body , err := json .Marshal (totpReq )
485- require .NoError (t , err )
486-
487- req := httptest .NewRequest ("POST" , "/api/user/totp" , strings .NewReader (string (body )))
488- req .Header .Set ("Content-Type" , "application/json" )
489- rec := httptest .NewRecorder ()
490- router .ServeHTTP (rec , req )
491-
492- require .Equal (t , 200 , rec .Code )
493- cookies := rec .Result ().Cookies ()
494- require .Len (t , cookies , 1 )
495- assert .Equal (t , "tinyauth-session" , cookies [0 ].Name )
496- })
497-
498- t .Cleanup (func () {
499- err = db .Close ()
500- require .NoError (t , err )
501- })
502- }
0 commit comments