feature: Local user login!

This commit is contained in:
jamesread 2024-10-18 01:20:04 +01:00
parent 71ad5d2e3a
commit 1af2e92132
11 changed files with 244 additions and 43 deletions

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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{

View File

@ -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
}

View File

@ -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>

View File

@ -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) {