Implement database access for user information
This commit is contained in:
parent
161ea7fe0c
commit
82918fb782
12 changed files with 298 additions and 118 deletions
|
@ -70,3 +70,7 @@ other = "Deine Nutzerprofilinformationen inklusive Name, Geburtsdatum und Sprach
|
||||||
[TitleRequestConsent]
|
[TitleRequestConsent]
|
||||||
hash = "sha1-14984a9e08cda5390677458a98408baa955f9f8b"
|
hash = "sha1-14984a9e08cda5390677458a98408baa955f9f8b"
|
||||||
other = "Anwendung erbittet deine Zustimmung"
|
other = "Anwendung erbittet deine Zustimmung"
|
||||||
|
|
||||||
|
[WrongOrLockedUserOrInvalidPassword]
|
||||||
|
hash = "sha1-87e0a0ac67c6c3a06bed184e10b22aae4d075b64"
|
||||||
|
other = "Du hast einen ungültigen Nutzernamen oder ein ungültiges Passwort eingegeben oder dein Benutzerkonto wurde gesperrt."
|
||||||
|
|
|
@ -15,6 +15,7 @@ Scope-offline_access-Description = "Keep access to your information until you re
|
||||||
Scope-openid-Description = "Request information about your identity."
|
Scope-openid-Description = "Request information about your identity."
|
||||||
Scope-profile-Description = "Access your user profile information including your name, birth date and locale."
|
Scope-profile-Description = "Access your user profile information including your name, birth date and locale."
|
||||||
TitleRequestConsent = "Application requests your consent"
|
TitleRequestConsent = "Application requests your consent"
|
||||||
|
WrongOrLockedUserOrInvalidPassword = "You entered an invalid username or password or your account has been locked."
|
||||||
|
|
||||||
[LogoutLabel]
|
[LogoutLabel]
|
||||||
description = "A label on a logout button or link"
|
description = "A label on a logout button or link"
|
||||||
|
|
|
@ -85,6 +85,11 @@ func main() {
|
||||||
clientTransport := client.New(adminURL.Host, adminURL.Path, []string{adminURL.Scheme})
|
clientTransport := client.New(adminURL.Host, adminURL.Path, []string{adminURL.Scheme})
|
||||||
adminClient := hydra.New(clientTransport, nil)
|
adminClient := hydra.New(clientTransport, nil)
|
||||||
|
|
||||||
|
ctx, err = services.InitDatabase(ctx, services.NewDatabaseParams(config.MustString("db.dsn")))
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("error initializing the database connection: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
handlerContext := context.WithValue(ctx, handlers.CtxAdminClient, adminClient.Admin)
|
handlerContext := context.WithValue(ctx, handlers.CtxAdminClient, adminClient.Admin)
|
||||||
loginHandler, err := handlers.NewLoginHandler(handlerContext, logger)
|
loginHandler, err := handlers.NewLoginHandler(handlerContext, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -94,7 +99,7 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatalf("error initializing consent handler: %v", err)
|
logger.Fatalf("error initializing consent handler: %v", err)
|
||||||
}
|
}
|
||||||
logoutHandler := handlers.NewLogoutHandler(logger, handlerContext)
|
logoutHandler := handlers.NewLogoutHandler(handlerContext, logger)
|
||||||
logoutSuccessHandler := handlers.NewLogoutSuccessHandler()
|
logoutSuccessHandler := handlers.NewLogoutSuccessHandler()
|
||||||
errorHandler := handlers.NewErrorHandler()
|
errorHandler := handlers.NewErrorHandler()
|
||||||
|
|
||||||
|
|
5
go.mod
5
go.mod
|
@ -7,9 +7,11 @@ require (
|
||||||
github.com/go-openapi/runtime v0.19.22
|
github.com/go-openapi/runtime v0.19.22
|
||||||
github.com/go-playground/form/v4 v4.1.1
|
github.com/go-playground/form/v4 v4.1.1
|
||||||
github.com/go-playground/validator/v10 v10.4.1
|
github.com/go-playground/validator/v10 v10.4.1
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0
|
||||||
github.com/golang/protobuf v1.4.0 // indirect
|
github.com/golang/protobuf v1.4.0 // indirect
|
||||||
github.com/gorilla/csrf v1.7.0
|
github.com/gorilla/csrf v1.7.0
|
||||||
github.com/gorilla/sessions v1.2.1
|
github.com/gorilla/sessions v1.2.1
|
||||||
|
github.com/jmoiron/sqlx v1.2.0
|
||||||
github.com/knadh/koanf v0.14.0
|
github.com/knadh/koanf v0.14.0
|
||||||
github.com/lestrrat-go/jwx v1.0.6
|
github.com/lestrrat-go/jwx v1.0.6
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.1.1
|
github.com/nicksnyder/go-i18n/v2 v2.1.1
|
||||||
|
@ -20,6 +22,7 @@ require (
|
||||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 // indirect
|
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 // indirect
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||||
golang.org/x/text v0.3.3
|
golang.org/x/text v0.3.4
|
||||||
google.golang.org/appengine v1.6.5 // indirect
|
google.golang.org/appengine v1.6.5 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
14
go.sum
14
go.sum
|
@ -102,6 +102,9 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||||
|
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
|
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
|
||||||
|
@ -155,6 +158,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
|
||||||
|
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
|
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
|
||||||
|
@ -180,6 +185,8 @@ github.com/lestrrat-go/jwx v1.0.6 h1:0absmJ/XlsxNkXr9syeIHjCJnu3rZa+DKzdCI6QfYgU
|
||||||
github.com/lestrrat-go/jwx v1.0.6/go.mod h1:NNxs6i86gQDGEqgIszN/pkJihMqzYrXMIJt2Yhxhkvs=
|
github.com/lestrrat-go/jwx v1.0.6/go.mod h1:NNxs6i86gQDGEqgIszN/pkJihMqzYrXMIJt2Yhxhkvs=
|
||||||
github.com/lestrrat-go/pdebug v0.0.0-20200204225717-4d6bd78da58d h1:aEZT3f1GGg5RIlHMAy4/4fe4ciOi3SCwYoaURphcB4k=
|
github.com/lestrrat-go/pdebug v0.0.0-20200204225717-4d6bd78da58d h1:aEZT3f1GGg5RIlHMAy4/4fe4ciOi3SCwYoaURphcB4k=
|
||||||
github.com/lestrrat-go/pdebug v0.0.0-20200204225717-4d6bd78da58d/go.mod h1:B06CSso/AWxiPejj+fheUINGeBKeeEZNt8w+EoU7+L8=
|
github.com/lestrrat-go/pdebug v0.0.0-20200204225717-4d6bd78da58d/go.mod h1:B06CSso/AWxiPejj+fheUINGeBKeeEZNt8w+EoU7+L8=
|
||||||
|
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||||
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
@ -188,11 +195,14 @@ github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8
|
||||||
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||||
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
||||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||||
|
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
|
||||||
|
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
|
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
|
||||||
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
|
github.com/nicksnyder/go-i18n v1.10.1 h1:isfg77E/aCD7+0lD/D00ebR2MV5vgeQ276WYyDaCRQc=
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.1.1 h1:ATCOanRDlrfKVB4WHAdJnLEqZtDmKYsweqsOUYflnBU=
|
github.com/nicksnyder/go-i18n/v2 v2.1.1 h1:ATCOanRDlrfKVB4WHAdJnLEqZtDmKYsweqsOUYflnBU=
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.1.1/go.mod h1:d++QJC9ZVf7pa48qrsRWhMJ5pSHIPmS3OLqK1niyLxs=
|
github.com/nicksnyder/go-i18n/v2 v2.1.1/go.mod h1:d++QJC9ZVf7pa48qrsRWhMJ5pSHIPmS3OLqK1niyLxs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
|
@ -297,6 +307,8 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
|
||||||
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
@ -332,6 +344,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||||
|
|
|
@ -2,12 +2,15 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-playground/form/v4"
|
"github.com/go-playground/form/v4"
|
||||||
|
"github.com/go-sql-driver/mysql"
|
||||||
"github.com/gorilla/csrf"
|
"github.com/gorilla/csrf"
|
||||||
"github.com/lestrrat-go/jwx/jwt/openid"
|
"github.com/lestrrat-go/jwx/jwt/openid"
|
||||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||||
|
@ -16,12 +19,14 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
commonServices "git.cacert.org/oidc_login/common/services"
|
commonServices "git.cacert.org/oidc_login/common/services"
|
||||||
|
"git.cacert.org/oidc_login/idp/services"
|
||||||
)
|
)
|
||||||
|
|
||||||
type consentHandler struct {
|
type consentHandler struct {
|
||||||
adminClient *admin.Client
|
adminClient *admin.Client
|
||||||
bundle *i18n.Bundle
|
bundle *i18n.Bundle
|
||||||
consentTemplate *template.Template
|
consentTemplate *template.Template
|
||||||
|
context context.Context
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
messageCatalog *commonServices.MessageCatalog
|
messageCatalog *commonServices.MessageCatalog
|
||||||
}
|
}
|
||||||
|
@ -30,6 +35,31 @@ type ConsentInformation struct {
|
||||||
ConsentChecked bool `form:"consent"`
|
ConsentChecked bool `form:"consent"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserInfo struct {
|
||||||
|
Email string `db:"email"`
|
||||||
|
EmailVerified bool `db:"verified"`
|
||||||
|
GivenName string `db:"fname"`
|
||||||
|
MiddleName string `db:"mname"`
|
||||||
|
FamilyName string `db:"lname"`
|
||||||
|
BirthDate mysql.NullTime `db:"dob"`
|
||||||
|
Language string `db:"language"`
|
||||||
|
Modified mysql.NullTime `db:"modified"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *UserInfo) GetFullName() string {
|
||||||
|
nameParts := make([]string, 0)
|
||||||
|
if len(i.GivenName) > 0 {
|
||||||
|
nameParts = append(nameParts, i.GivenName)
|
||||||
|
}
|
||||||
|
if len(i.MiddleName) > 0 {
|
||||||
|
nameParts = append(nameParts, i.MiddleName)
|
||||||
|
}
|
||||||
|
if len(i.FamilyName) > 0 {
|
||||||
|
nameParts = append(nameParts, i.FamilyName)
|
||||||
|
}
|
||||||
|
return strings.Join(nameParts, " ")
|
||||||
|
}
|
||||||
|
|
||||||
func (h *consentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *consentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
challenge := r.URL.Query().Get("consent_challenge")
|
challenge := r.URL.Query().Get("consent_challenge")
|
||||||
h.logger.Debugf("received consent challenge %s", challenge)
|
h.logger.Debugf("received consent challenge %s", challenge)
|
||||||
|
@ -47,26 +77,7 @@ func (h *consentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
trans := h.messageCatalog.LookupMessage
|
h.renderConsentForm(w, r, consentData, err, localizer)
|
||||||
|
|
||||||
// render consent form
|
|
||||||
client := consentData.GetPayload().Client
|
|
||||||
err = h.consentTemplate.Lookup("base").Execute(w, map[string]interface{}{
|
|
||||||
"Title": trans("TitleRequestConsent", nil, localizer),
|
|
||||||
csrf.TemplateTag: csrf.TemplateField(r),
|
|
||||||
"errors": map[string]string{},
|
|
||||||
"client": client,
|
|
||||||
"requestedScope": h.mapRequestedScope(consentData.GetPayload().RequestedScope, localizer),
|
|
||||||
"LabelSubmit": trans("LabelSubmit", nil, localizer),
|
|
||||||
"LabelConsent": trans("LabelConsent", nil, localizer),
|
|
||||||
"IntroMoreInformation": template.HTML(trans("IntroConsentMoreInformation", map[string]interface{}{
|
|
||||||
"client": client.ClientName,
|
|
||||||
"clientLink": client.ClientURI,
|
|
||||||
}, localizer)),
|
|
||||||
"IntroConsentRequested": template.HTML(trans("IntroConsentRequested", map[string]interface{}{
|
|
||||||
"client": client.ClientName,
|
|
||||||
}, localizer)),
|
|
||||||
})
|
|
||||||
break
|
break
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
var consentInfo ConsentInformation
|
var consentInfo ConsentInformation
|
||||||
|
@ -82,46 +93,79 @@ func (h *consentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
if consentInfo.ConsentChecked {
|
if consentInfo.ConsentChecked {
|
||||||
idTokenData := make(map[string]interface{}, 0)
|
idTokenData := make(map[string]interface{}, 0)
|
||||||
|
|
||||||
for _, scope := range consentData.GetPayload().RequestedScope {
|
db := services.GetDb(h.context)
|
||||||
switch scope {
|
|
||||||
case "email":
|
|
||||||
idTokenData[openid.EmailKey] = "john@theripper.mil"
|
|
||||||
idTokenData[openid.EmailVerifiedKey] = true
|
|
||||||
break
|
|
||||||
case "profile":
|
|
||||||
idTokenData[openid.GivenNameKey] = "John"
|
|
||||||
idTokenData[openid.FamilyNameKey] = "The ripper"
|
|
||||||
idTokenData[openid.MiddleNameKey] = ""
|
|
||||||
idTokenData[openid.NameKey] = "John the Ripper"
|
|
||||||
idTokenData[openid.BirthdateKey] = "1970-01-01"
|
|
||||||
idTokenData[openid.ZoneinfoKey] = "Europe/London"
|
|
||||||
idTokenData[openid.LocaleKey] = "en_UK"
|
|
||||||
idTokenData["https://cacert.localhost/groups"] = []string{"admin", "user"}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionData := &models.ConsentRequestSession{
|
stmt, err := db.PreparexContext(
|
||||||
AccessToken: nil,
|
r.Context(),
|
||||||
IDToken: idTokenData,
|
`SELECT email, verified, fname, mname, lname, dob, language, modified
|
||||||
}
|
FROM users
|
||||||
consentRequest, err := h.adminClient.AcceptConsentRequest(
|
WHERE id = ?
|
||||||
admin.NewAcceptConsentRequestParams().WithConsentChallenge(challenge).WithBody(
|
AND LOCKED = 0`,
|
||||||
&models.AcceptConsentRequest{
|
)
|
||||||
GrantAccessTokenAudience: nil,
|
|
||||||
GrantScope: consentData.GetPayload().RequestedScope,
|
|
||||||
HandledAt: models.NullTime(time.Now()),
|
|
||||||
Remember: true,
|
|
||||||
RememberFor: 86400,
|
|
||||||
Session: sessionData,
|
|
||||||
}).WithTimeout(time.Second * 10))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logger.Error(err)
|
h.logger.Errorf("error preparing user information SQL: %v", err)
|
||||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Add("Location", *consentRequest.GetPayload().RedirectTo)
|
defer func() { _ = stmt.Close() }()
|
||||||
w.WriteHeader(http.StatusFound)
|
|
||||||
|
userInfo := &UserInfo{}
|
||||||
|
|
||||||
|
err = stmt.QueryRowxContext(r.Context(), consentData.GetPayload().Subject).StructScan(userInfo)
|
||||||
|
switch {
|
||||||
|
case err == sql.ErrNoRows:
|
||||||
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
case err != nil:
|
||||||
|
h.logger.Errorf("error performing user information SQL: %v", err)
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
for _, scope := range consentData.GetPayload().RequestedScope {
|
||||||
|
switch scope {
|
||||||
|
case "email":
|
||||||
|
idTokenData[openid.EmailKey] = userInfo.Email
|
||||||
|
idTokenData[openid.EmailVerifiedKey] = userInfo.EmailVerified
|
||||||
|
break
|
||||||
|
case "profile":
|
||||||
|
idTokenData[openid.GivenNameKey] = userInfo.GivenName
|
||||||
|
idTokenData[openid.FamilyNameKey] = userInfo.FamilyName
|
||||||
|
idTokenData[openid.MiddleNameKey] = userInfo.MiddleName
|
||||||
|
idTokenData[openid.NameKey] = userInfo.GetFullName()
|
||||||
|
if userInfo.BirthDate.Valid {
|
||||||
|
idTokenData[openid.BirthdateKey] = userInfo.BirthDate.Time.Format("2006-01-02")
|
||||||
|
}
|
||||||
|
idTokenData[openid.LocaleKey] = userInfo.Language
|
||||||
|
idTokenData["https://cacert.localhost/groups"] = []string{"admin", "user"}
|
||||||
|
if userInfo.Modified.Valid {
|
||||||
|
idTokenData[openid.UpdatedAtKey] = userInfo.Modified.Time.Unix()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sessionData := &models.ConsentRequestSession{
|
||||||
|
AccessToken: nil,
|
||||||
|
IDToken: idTokenData,
|
||||||
|
}
|
||||||
|
consentRequest, err := h.adminClient.AcceptConsentRequest(
|
||||||
|
admin.NewAcceptConsentRequestParams().WithConsentChallenge(challenge).WithBody(
|
||||||
|
&models.AcceptConsentRequest{
|
||||||
|
GrantAccessTokenAudience: nil,
|
||||||
|
GrantScope: consentData.GetPayload().RequestedScope,
|
||||||
|
HandledAt: models.NullTime(time.Now()),
|
||||||
|
Remember: true,
|
||||||
|
RememberFor: 86400,
|
||||||
|
Session: sessionData,
|
||||||
|
}).WithTimeout(time.Second * 10))
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error(err)
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Add("Location", *consentRequest.GetPayload().RedirectTo)
|
||||||
|
w.WriteHeader(http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
consentRequest, err := h.adminClient.RejectConsentRequest(
|
consentRequest, err := h.adminClient.RejectConsentRequest(
|
||||||
admin.NewRejectConsentRequestParams().WithConsentChallenge(challenge).WithBody(
|
admin.NewRejectConsentRequestParams().WithConsentChallenge(challenge).WithBody(
|
||||||
|
@ -137,6 +181,34 @@ func (h *consentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *consentHandler) renderConsentForm(w http.ResponseWriter, r *http.Request, consentData *admin.GetConsentRequestOK, err error, localizer *i18n.Localizer) {
|
||||||
|
trans := func(id string, values ...map[string]interface{}) string {
|
||||||
|
if len(values) > 0 {
|
||||||
|
return h.messageCatalog.LookupMessage(id, values[0], localizer)
|
||||||
|
}
|
||||||
|
return h.messageCatalog.LookupMessage(id, nil, localizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// render consent form
|
||||||
|
client := consentData.GetPayload().Client
|
||||||
|
err = h.consentTemplate.Lookup("base").Execute(w, map[string]interface{}{
|
||||||
|
"Title": trans("TitleRequestConsent"),
|
||||||
|
csrf.TemplateTag: csrf.TemplateField(r),
|
||||||
|
"errors": map[string]string{},
|
||||||
|
"client": client,
|
||||||
|
"requestedScope": h.mapRequestedScope(consentData.GetPayload().RequestedScope, localizer),
|
||||||
|
"LabelSubmit": trans("LabelSubmit"),
|
||||||
|
"LabelConsent": trans("LabelConsent"),
|
||||||
|
"IntroMoreInformation": template.HTML(trans("IntroConsentMoreInformation", map[string]interface{}{
|
||||||
|
"client": client.ClientName,
|
||||||
|
"clientLink": client.ClientURI,
|
||||||
|
})),
|
||||||
|
"IntroConsentRequested": template.HTML(trans("IntroConsentRequested", map[string]interface{}{
|
||||||
|
"client": client.ClientName,
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type scopeWithLabel struct {
|
type scopeWithLabel struct {
|
||||||
Name string
|
Name string
|
||||||
Label string
|
Label string
|
||||||
|
@ -162,6 +234,7 @@ func NewConsentHandler(ctx context.Context, logger *log.Logger) (*consentHandler
|
||||||
adminClient: ctx.Value(CtxAdminClient).(*admin.Client),
|
adminClient: ctx.Value(CtxAdminClient).(*admin.Client),
|
||||||
bundle: commonServices.GetI18nBundle(ctx),
|
bundle: commonServices.GetI18nBundle(ctx),
|
||||||
consentTemplate: consentTemplate,
|
consentTemplate: consentTemplate,
|
||||||
|
context: ctx,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
messageCatalog: commonServices.GetMessageCatalog(ctx),
|
messageCatalog: commonServices.GetMessageCatalog(ctx),
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -2,6 +2,9 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/sha1"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/hex"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
@ -16,11 +19,13 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
commonServices "git.cacert.org/oidc_login/common/services"
|
commonServices "git.cacert.org/oidc_login/common/services"
|
||||||
|
"git.cacert.org/oidc_login/idp/services"
|
||||||
)
|
)
|
||||||
|
|
||||||
type loginHandler struct {
|
type loginHandler struct {
|
||||||
adminClient *admin.Client
|
adminClient *admin.Client
|
||||||
bundle *i18n.Bundle
|
bundle *i18n.Bundle
|
||||||
|
context context.Context
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
loginTemplate *template.Template
|
loginTemplate *template.Template
|
||||||
messageCatalog *commonServices.MessageCatalog
|
messageCatalog *commonServices.MessageCatalog
|
||||||
|
@ -47,24 +52,15 @@ func (h *loginHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
var err error
|
var err error
|
||||||
challenge := r.URL.Query().Get("login_challenge")
|
challenge := r.URL.Query().Get("login_challenge")
|
||||||
h.logger.Debugf("received login challenge %s\n", challenge)
|
h.logger.Debugf("received login challenge %s\n", challenge)
|
||||||
|
accept := r.Header.Get("Accept-Language")
|
||||||
|
localizer := i18n.NewLocalizer(h.bundle, accept)
|
||||||
|
|
||||||
validate := validator.New()
|
validate := validator.New()
|
||||||
|
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
// render login form
|
// render login form
|
||||||
err = h.loginTemplate.Lookup("base").Execute(w, map[string]interface{}{
|
h.renderLoginForm(w, r, map[string]string{}, &LoginInformation{}, localizer)
|
||||||
"Title": "Title",
|
|
||||||
csrf.TemplateTag: csrf.TemplateField(r),
|
|
||||||
"LabelEmail": "Email",
|
|
||||||
"LabelPassword": "Password",
|
|
||||||
"LabelLogin": "Login",
|
|
||||||
"errors": map[string]string{},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
h.logger.Error(err)
|
|
||||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
var loginInfo LoginInformation
|
var loginInfo LoginInformation
|
||||||
|
@ -84,42 +80,66 @@ func (h *loginHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
accept := r.Header.Get("Accept-Language")
|
accept := r.Header.Get("Accept-Language")
|
||||||
errors[err.Field()] = h.messageCatalog.LookupErrorMessage(err.Tag(), err.Field(), err.Value(), i18n.NewLocalizer(h.bundle, accept))
|
errors[err.Field()] = h.messageCatalog.LookupErrorMessage(err.Tag(), err.Field(), err.Value(), i18n.NewLocalizer(h.bundle, accept))
|
||||||
}
|
}
|
||||||
|
h.renderLoginForm(w, r, errors, &loginInfo, localizer)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err = h.loginTemplate.Lookup("base").Execute(w, map[string]interface{}{
|
db := services.GetDb(h.context)
|
||||||
"Title": "Title",
|
|
||||||
csrf.TemplateTag: csrf.TemplateField(r),
|
stmt, err := db.PrepareContext(
|
||||||
"LabelEmail": "Email",
|
r.Context(),
|
||||||
"LabelPassword": "Password",
|
`SELECT id
|
||||||
"LabelLogin": "Login",
|
FROM users
|
||||||
"Email": loginInfo.Email,
|
WHERE email = ?
|
||||||
"errors": errors,
|
AND password = ?
|
||||||
})
|
AND locked = 0`,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Errorf("error preparing login SQL: %v", err)
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() { _ = stmt.Close() }()
|
||||||
|
|
||||||
|
// FIXME: replace with a real password hash algorithm
|
||||||
|
passwordHash := sha1.Sum([]byte(loginInfo.Password))
|
||||||
|
password := hex.EncodeToString(passwordHash[:])
|
||||||
|
// FIXME: introduce a real opaque identifier (i.e. a UUID)
|
||||||
|
var userId string
|
||||||
|
// GET user data
|
||||||
|
err = stmt.QueryRowContext(r.Context(), loginInfo.Email, password).Scan(&userId)
|
||||||
|
switch {
|
||||||
|
case err == sql.ErrNoRows:
|
||||||
|
errors := map[string]string{
|
||||||
|
"Form": h.messageCatalog.LookupMessage(
|
||||||
|
"WrongOrLockedUserOrInvalidPassword",
|
||||||
|
nil,
|
||||||
|
localizer,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
h.renderLoginForm(w, r, errors, &loginInfo, localizer)
|
||||||
|
return
|
||||||
|
case err != nil:
|
||||||
|
h.logger.Errorf("error performing login SQL: %v", err)
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
// finish login and redirect to target
|
||||||
|
loginRequest, err := h.adminClient.AcceptLoginRequest(
|
||||||
|
admin.NewAcceptLoginRequestParams().WithLoginChallenge(challenge).WithBody(&models.AcceptLoginRequest{
|
||||||
|
Acr: string(Password),
|
||||||
|
Remember: true,
|
||||||
|
RememberFor: 0,
|
||||||
|
Subject: &userId,
|
||||||
|
}).WithTimeout(time.Second * 10))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logger.Error(err)
|
h.logger.Errorf("error getting login request: %#v", err)
|
||||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
http.Error(w, err.Error(), err.(*runtime.APIError).Code)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
w.Header().Add("Location", *loginRequest.GetPayload().RedirectTo)
|
||||||
|
w.WriteHeader(http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET user data
|
|
||||||
// finish login and redirect to target
|
|
||||||
// TODO: get or generate a user id
|
|
||||||
subject := "a-user-with-an-id"
|
|
||||||
loginRequest, err := h.adminClient.AcceptLoginRequest(
|
|
||||||
admin.NewAcceptLoginRequestParams().WithLoginChallenge(challenge).WithBody(&models.AcceptLoginRequest{
|
|
||||||
Acr: string(NoCredentials),
|
|
||||||
Remember: true,
|
|
||||||
RememberFor: 0,
|
|
||||||
Subject: &subject,
|
|
||||||
}).WithTimeout(time.Second * 10))
|
|
||||||
if err != nil {
|
|
||||||
h.logger.Errorf("error getting login request: %#v", err)
|
|
||||||
http.Error(w, err.Error(), err.(*runtime.APIError).Code)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Header().Add("Location", *loginRequest.GetPayload().RedirectTo)
|
|
||||||
w.WriteHeader(http.StatusFound)
|
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
|
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
|
||||||
|
@ -127,6 +147,27 @@ func (h *loginHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *loginHandler) renderLoginForm(w http.ResponseWriter, r *http.Request, errors map[string]string, info *LoginInformation, localizer *i18n.Localizer) {
|
||||||
|
trans := func(label string) string {
|
||||||
|
return h.messageCatalog.LookupMessage(label, nil, localizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := h.loginTemplate.Lookup("base").Execute(w, map[string]interface{}{
|
||||||
|
"Title": trans("LoginTitle"),
|
||||||
|
csrf.TemplateTag: csrf.TemplateField(r),
|
||||||
|
"LabelEmail": trans("LabelEmail"),
|
||||||
|
"LabelPassword": trans("LabelPassword"),
|
||||||
|
"LabelLogin": trans("LabelLogin"),
|
||||||
|
"Email": info.Email,
|
||||||
|
"errors": errors,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error(err)
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewLoginHandler(ctx context.Context, logger *log.Logger) (*loginHandler, error) {
|
func NewLoginHandler(ctx context.Context, logger *log.Logger) (*loginHandler, error) {
|
||||||
loginTemplate, err := template.ParseFiles(
|
loginTemplate, err := template.ParseFiles(
|
||||||
"templates/idp/base.gohtml", "templates/idp/login.gohtml")
|
"templates/idp/base.gohtml", "templates/idp/login.gohtml")
|
||||||
|
@ -136,6 +177,7 @@ func NewLoginHandler(ctx context.Context, logger *log.Logger) (*loginHandler, er
|
||||||
return &loginHandler{
|
return &loginHandler{
|
||||||
adminClient: ctx.Value(CtxAdminClient).(*admin.Client),
|
adminClient: ctx.Value(CtxAdminClient).(*admin.Client),
|
||||||
bundle: commonServices.GetI18nBundle(ctx),
|
bundle: commonServices.GetI18nBundle(ctx),
|
||||||
|
context: ctx,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
loginTemplate: loginTemplate,
|
loginTemplate: loginTemplate,
|
||||||
messageCatalog: commonServices.GetMessageCatalog(ctx),
|
messageCatalog: commonServices.GetMessageCatalog(ctx),
|
||||||
|
|
|
@ -39,7 +39,7 @@ func (h *logoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusFound)
|
w.WriteHeader(http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLogoutHandler(logger *log.Logger, ctx context.Context) *logoutHandler {
|
func NewLogoutHandler(ctx context.Context, logger *log.Logger) *logoutHandler {
|
||||||
return &logoutHandler{
|
return &logoutHandler{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
adminClient: ctx.Value(CtxAdminClient).(*admin.Client),
|
adminClient: ctx.Value(CtxAdminClient).(*admin.Client),
|
||||||
|
|
46
idp/services/database.go
Normal file
46
idp/services/database.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dbContextKey int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ctxDbConnection dbContextKey = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
type DatabaseParams struct {
|
||||||
|
ConnMaxLifeTime time.Duration
|
||||||
|
DSN string
|
||||||
|
MaxOpenConnections int
|
||||||
|
MaxIdleConnections int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDatabaseParams(dsn string) *DatabaseParams {
|
||||||
|
return &DatabaseParams{
|
||||||
|
DSN: dsn,
|
||||||
|
ConnMaxLifeTime: time.Minute * 3,
|
||||||
|
MaxOpenConnections: 10,
|
||||||
|
MaxIdleConnections: 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitDatabase(ctx context.Context, params *DatabaseParams) (context.Context, error) {
|
||||||
|
db, err := sqlx.Connect("mysql", params.DSN)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
db.SetConnMaxLifetime(params.ConnMaxLifeTime)
|
||||||
|
db.SetMaxOpenConns(params.MaxOpenConnections)
|
||||||
|
db.SetMaxIdleConns(params.MaxIdleConnections)
|
||||||
|
return context.WithValue(ctx, ctxDbConnection, db), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDb(ctx context.Context) *sqlx.DB {
|
||||||
|
return ctx.Value(ctxDbConnection).(*sqlx.DB)
|
||||||
|
}
|
|
@ -66,5 +66,9 @@ func AddMessages(ctx context.Context) {
|
||||||
ID: "Scope-email-Description",
|
ID: "Scope-email-Description",
|
||||||
Other: "Access your primary email address.",
|
Other: "Access your primary email address.",
|
||||||
}
|
}
|
||||||
|
messages["WrongOrLockedUserOrInvalidPassword"] = &i18n.Message{
|
||||||
|
ID: "WrongOrLockedUserOrInvalidPassword",
|
||||||
|
Other: "You entered an invalid username or password or your account has been locked.",
|
||||||
|
}
|
||||||
services.GetMessageCatalog(ctx).AddMessages(messages)
|
services.GetMessageCatalog(ctx).AddMessages(messages)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{{ .csrfField }}
|
{{ .csrfField }}
|
||||||
|
{{ if .errors.Form}}<p>{{ .errors.Form }}</p>{{ end }}
|
||||||
{{ if .errors.Email }}<p>{{ .errors.Email }}</p>{{ end }}
|
{{ if .errors.Email }}<p>{{ .errors.Email }}</p>{{ end }}
|
||||||
<label for="email">{{ .LabelEmail }}</label>
|
<label for="email">{{ .LabelEmail }}</label>
|
||||||
<input type="text" id="email" name="email" value="{{ .Email }}"/><br/>
|
<input type="text" id="email" name="email" value="{{ .Email }}"/><br/>
|
||||||
|
|
|
@ -1,16 +1,3 @@
|
||||||
[IndexGreeting]
|
[WrongOrLockedUserOrInvalidPassword]
|
||||||
hash = "sha1-d4a13058e497fa24143ea96d50d82b818455ef61"
|
hash = "sha1-87e0a0ac67c6c3a06bed184e10b22aae4d075b64"
|
||||||
other = "Hallo {{ .User }}"
|
other = "Du hast einen ungültigen Nutzernamen oder ein ungültiges Passwort eingegeben oder dein Benutzerkonto wurde gesperrt."
|
||||||
|
|
||||||
[IndexIntroductionText]
|
|
||||||
hash = "sha1-c2c530e263fc9c38482338ed290aafb496794179"
|
|
||||||
other = "Das ist eine zugriffsgeschützte Resource"
|
|
||||||
|
|
||||||
[IndexTitle]
|
|
||||||
hash = "sha1-eccb2b889c068d3f25496c1dad3fb0f88d021bd9"
|
|
||||||
other = "Willkommen in der Demo-Anwendung"
|
|
||||||
|
|
||||||
[LogoutLabel]
|
|
||||||
description = "A label on a logout button or link"
|
|
||||||
hash = "sha1-8acfdeb9a8286f00c8e5dd48471cfdc994807579"
|
|
||||||
other = "Ausloggen"
|
|
||||||
|
|
Reference in a new issue