feature: Local user login!
This commit is contained in:
parent
71ad5d2e3a
commit
1af2e92132
|
|
@ -215,7 +215,14 @@ message LocalUserLoginRequest {
|
|||
}
|
||||
|
||||
message LocalUserLoginResponse {
|
||||
string authenticated_user = 1;
|
||||
bool success = 1;
|
||||
}
|
||||
|
||||
message PasswordHashRequest {
|
||||
string password = 1;
|
||||
}
|
||||
|
||||
message PasswordHashResponse {
|
||||
}
|
||||
|
||||
service OliveTinApiService {
|
||||
|
|
@ -308,9 +315,16 @@ service OliveTinApiService {
|
|||
};
|
||||
}
|
||||
|
||||
rpc Authenticate(LocalUserLoginRequest) returns (LocalUserLoginResponse) {
|
||||
rpc LocalUserLogin(LocalUserLoginRequest) returns (LocalUserLoginResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/authenticate"
|
||||
post: "/api/LocalUserLogin"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
rpc PasswordHash(PasswordHashRequest) returns (google.api.HttpBody) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/PasswordHash"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -36,6 +36,7 @@ require (
|
|||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/MicahParks/jwkset v0.5.17 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/alexedwards/argon2id v1.0.0 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bufbuild/protocompile v0.9.0 // indirect
|
||||
|
|
|
|||
27
go.sum
27
go.sum
|
|
@ -52,6 +52,8 @@ github.com/MicahParks/keyfunc/v3 v3.3.2 h1:YTtwc4dxalBZKFqHhqctBWN6VhbLdGhywmne9
|
|||
github.com/MicahParks/keyfunc/v3 v3.3.2/go.mod h1:GJBeEjnv25OnD9y2OYQa7ELU6gYahEMBNXINZb+qm34=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/alexedwards/argon2id v1.0.0 h1:wJzDx66hqWX7siL/SRUmgz3F8YMrd/nfX/xHHcQQP0w=
|
||||
github.com/alexedwards/argon2id v1.0.0/go.mod h1:tYKkqIjzXvZdzPvADMWOEZ+l6+BD6CtBXMj5fnJppiw=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
|
|
@ -371,6 +373,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
|
|
@ -409,7 +412,9 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
|
@ -451,6 +456,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
|
@ -484,6 +491,9 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
|
@ -507,6 +517,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -546,13 +558,22 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
@ -561,6 +582,10 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
|||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
|
@ -617,6 +642,8 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
|
|||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
|
|||
|
|
@ -134,6 +134,10 @@ func getMetadataKeyOrEmpty(md metadata.MD, key string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func SetUserFromMetadata(md metadata.MD) string {
|
||||
return getMetadataKeyOrEmpty(md, "set-user")
|
||||
}
|
||||
|
||||
// UserFromContext tries to find a user from a grpc context
|
||||
func UserFromContext(ctx context.Context, cfg *config.Config) *AuthenticatedUser {
|
||||
var ret *AuthenticatedUser
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ type Config struct {
|
|||
AuthJwtPubKeyPath string // will read pub key from file on disk
|
||||
AuthHttpHeaderUsername string
|
||||
AuthHttpHeaderUserGroup string
|
||||
AuthUsernamePassword AuthUsernamePasswordConfig
|
||||
AuthLocalUsers AuthLocalUsersConfig
|
||||
AuthLoginUrl string
|
||||
AuthAllowGuest bool
|
||||
AuthOAuth2RedirectURL string
|
||||
|
|
@ -140,7 +140,7 @@ type Config struct {
|
|||
usedConfigDir string
|
||||
}
|
||||
|
||||
type AuthUsernamePasswordConfig struct {
|
||||
type AuthLocalUsersConfig struct {
|
||||
Enabled bool
|
||||
Users []*LocalUser
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"google.golang.org/genproto/googleapis/api/httpbody"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"errors"
|
||||
|
|
@ -94,16 +95,31 @@ func (api *oliveTinAPI) StartAction(ctx ctx.Context, req *pb.StartActionRequest)
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (api *oliveTinAPI) Authenticate(ctx ctx.Context, req *pb.LocalUserLoginRequest) (*pb.LocalUserLoginResponse, error) {
|
||||
/*
|
||||
user := acl.Authenticate(cfg, req.Username, req.Password)
|
||||
func (api *oliveTinAPI) PasswordHash(ctx ctx.Context, req *pb.PasswordHashRequest) (*httpbody.HttpBody, error) {
|
||||
hash, err := createHash(req.Password)
|
||||
|
||||
if user == nil {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "Invalid username or password.")
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Error creating hash.")
|
||||
}
|
||||
|
||||
ret := &httpbody.HttpBody{
|
||||
ContentType: "text/plain",
|
||||
Data: []byte("Your password hash is: " + hash),
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (api *oliveTinAPI) LocalUserLogin(ctx ctx.Context, req *pb.LocalUserLoginRequest) (*pb.LocalUserLoginResponse, error) {
|
||||
match := checkUserPassword(cfg, req.Username, req.Password)
|
||||
|
||||
if match {
|
||||
header := metadata.Pairs("set-user", req.Username)
|
||||
grpc.SendHeader(ctx, header)
|
||||
}
|
||||
*/
|
||||
|
||||
return &pb.LocalUserLoginResponse{
|
||||
Success: match,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
package grpcapi
|
||||
|
||||
import (
|
||||
config "github.com/OliveTin/OliveTin/internal/config"
|
||||
"github.com/alexedwards/argon2id"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var defaultParams = argon2id.Params{
|
||||
Memory: 64 * 1024,
|
||||
Iterations: 4,
|
||||
Parallelism: uint8(runtime.NumCPU()),
|
||||
SaltLength: 16,
|
||||
KeyLength: 32,
|
||||
}
|
||||
|
||||
func createHash(password string) (string, error) {
|
||||
hash, err := argon2id.CreateHash(password, &defaultParams)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("Error creating hash: ", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return hash, nil
|
||||
}
|
||||
|
||||
func comparePasswordAndHash(password, hash string) bool {
|
||||
match, err := argon2id.ComparePasswordAndHash(password, hash)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Error comparing password and hash: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
func checkUserPassword(cfg *config.Config, username, password string) bool {
|
||||
for _, user := range cfg.AuthLocalUsers.Users {
|
||||
if user.Username == username {
|
||||
match := comparePasswordAndHash(password, user.Password)
|
||||
|
||||
if match {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"net/http"
|
||||
|
||||
gw "github.com/OliveTin/OliveTin/gen/grpc"
|
||||
|
|
@ -45,6 +46,7 @@ func parseHttpHeaderForAuth(req *http.Request) (string, string) {
|
|||
return username[0], ""
|
||||
}
|
||||
|
||||
//gocyclo:ignore
|
||||
func parseRequestMetadata(ctx context.Context, req *http.Request) metadata.MD {
|
||||
username := ""
|
||||
usergroup := ""
|
||||
|
|
@ -61,6 +63,10 @@ func parseRequestMetadata(ctx context.Context, req *http.Request) metadata.MD {
|
|||
username, usergroup = parseOAuth2Cookie(req)
|
||||
}
|
||||
|
||||
if cfg.AuthLocalUsers.Enabled {
|
||||
username, usergroup = parseLocalUserCookie(req)
|
||||
}
|
||||
|
||||
md := metadata.New(map[string]string{
|
||||
"username": username,
|
||||
"usergroup": usergroup,
|
||||
|
|
@ -71,6 +77,12 @@ func parseRequestMetadata(ctx context.Context, req *http.Request) metadata.MD {
|
|||
return md
|
||||
}
|
||||
|
||||
func forwardResponseHandler(ctx context.Context, w http.ResponseWriter, msg protoreflect.ProtoMessage) error {
|
||||
forwardResponseHandlerLoginLocalUser(ctx, w, msg)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetGlobalRestConfig(config *config.Config) {
|
||||
cfg = config
|
||||
}
|
||||
|
|
@ -91,6 +103,7 @@ func newMux() *runtime.ServeMux {
|
|||
// The MarshalOptions set some important compatibility settings for the webui. See below.
|
||||
mux := runtime.NewServeMux(
|
||||
runtime.WithMetadata(parseRequestMetadata),
|
||||
runtime.WithForwardResponseOption(forwardResponseHandler),
|
||||
runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{
|
||||
Marshaler: &runtime.JSONPb{
|
||||
MarshalOptions: protojson.MarshalOptions{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
package httpservers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
acl "github.com/OliveTin/OliveTin/internal/acl"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
)
|
||||
|
||||
var (
|
||||
localUserSessions = make(map[string]string) // sid -> username, used for local user sessions
|
||||
)
|
||||
|
||||
func parseLocalUserCookie(req *http.Request) (string, string) {
|
||||
cookie, err := req.Cookie("olivetin_local_user_sid")
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
cookieValue := cookie.Value
|
||||
|
||||
username, ok := localUserSessions[cookieValue]
|
||||
|
||||
if !ok {
|
||||
log.Warnf("Could not find local user session: %v", cookieValue)
|
||||
return "", ""
|
||||
}
|
||||
|
||||
return username, ""
|
||||
}
|
||||
|
||||
func forwardResponseHandlerLoginLocalUser(ctx context.Context, w http.ResponseWriter, msg protoreflect.ProtoMessage) error {
|
||||
md, ok := runtime.ServerMetadataFromContext(ctx)
|
||||
|
||||
if !ok {
|
||||
log.Warn("Could not get ServerMetadata from context")
|
||||
return nil
|
||||
}
|
||||
|
||||
setUser := acl.SetUserFromMetadata(md.HeaderMD)
|
||||
|
||||
sid := uuid.NewString()
|
||||
localUserSessions[sid] = setUser
|
||||
|
||||
if setUser != "" {
|
||||
http.SetCookie(
|
||||
w,
|
||||
&http.Cookie{
|
||||
Name: "olivetin_local_user_sid",
|
||||
Value: sid,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -92,35 +92,6 @@
|
|||
</fieldset>
|
||||
</section>
|
||||
|
||||
<section id = "content-login" title = "Login" hidden>
|
||||
<div class = "flex-col">
|
||||
<form class = "box-shadow padded-content">
|
||||
<h2>OAuth Login</h2>
|
||||
<div>
|
||||
<button>GitHub</button>
|
||||
<button>Google</button>
|
||||
</div>
|
||||
<hr />
|
||||
<h2>Local Login</h2>
|
||||
<div class = "arguments">
|
||||
<label for = "username">
|
||||
<span>Username:</span>
|
||||
</label>
|
||||
<input type = "text" name = "username" />
|
||||
<span></span>
|
||||
|
||||
<label for = "password">
|
||||
<span>Password:</span>
|
||||
</label>
|
||||
<input type = "password" name = "password" />
|
||||
<span></span>
|
||||
|
||||
<button type = "submit">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<noscript>
|
||||
<div class = "error">Sorry, JavaScript is required to use OliveTin.</div>
|
||||
</noscript>
|
||||
|
|
@ -181,7 +152,7 @@
|
|||
<template id = "tplLoginForm">
|
||||
<section id = "content-login" title = "Login" hidden>
|
||||
<div class = "flex-col">
|
||||
<form class = "box-shadow padded-content border-radius">
|
||||
<form class = "box-shadow padded-content border-radius" id = "local-user-login">
|
||||
<p class = "login-disabled">Login with OAuth or Local users is not enabled on this server.</p>
|
||||
|
||||
<div class = "login-oauth2" hidden>
|
||||
|
|
@ -192,17 +163,18 @@
|
|||
|
||||
<div class = "login-local" hidden>
|
||||
<h2>Local Login</h2>
|
||||
<div class = "error"></div>
|
||||
<div class = "arguments">
|
||||
<label for = "username">
|
||||
<span>Username:</span>
|
||||
</label>
|
||||
<input type = "text" name = "username" />
|
||||
<input type = "text" name = "username" class = "username" />
|
||||
<span></span>
|
||||
|
||||
<label for = "password">
|
||||
<span>Password:</span>
|
||||
</label>
|
||||
<input type = "password" name = "password" />
|
||||
<input type = "password" name = "password" class = "password" />
|
||||
<span></span>
|
||||
|
||||
<button type = "submit">Login</button>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,47 @@ export class LoginForm extends window.HTMLElement {
|
|||
this.content = tpl.content.cloneNode(true)
|
||||
|
||||
this.appendChild(this.content)
|
||||
|
||||
this.querySelector('#local-user-login').addEventListener('submit', (e) => {
|
||||
e.preventDefault()
|
||||
this.localLoginRequest()
|
||||
})
|
||||
}
|
||||
|
||||
localLoginRequest () {
|
||||
const username = this.querySelector('input.username').value
|
||||
const password = this.querySelector('input.password').value
|
||||
|
||||
document.querySelector('.error').innerHTML = ''
|
||||
|
||||
window.fetch('/api/LocalUserLogin', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: username,
|
||||
password: password
|
||||
})
|
||||
}).then((response) => {
|
||||
if (response.ok) {
|
||||
const res = response.json()
|
||||
|
||||
if (res !== undefined) {
|
||||
return res
|
||||
} else {
|
||||
throw new Error('Failed to login - no res')
|
||||
}
|
||||
} else {
|
||||
throw new Error('Failed to login')
|
||||
}
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
window.location.reload()
|
||||
} else {
|
||||
document.querySelector('.error').innerHTML = 'Login failed.'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
processOAuth2Providers (providers) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue