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