diff --git a/README.md b/README.md index 11810df..82d3aea 100644 --- a/README.md +++ b/README.md @@ -33,12 +33,8 @@ Flags ``` Usage of ./git-http-backend: - -require_auth bool - set require auth enable/disable - -auth_pass_env_var string - set an env var to provide the basic auth pass as - -auth_user_env_var string - set an env var to provide the basic auth user as + -auth_data string + set auth info data path -default_env string set the default env -git_bin_path string diff --git a/authentication_info_example.json b/authentication_info_example.json new file mode 100644 index 0000000..e434fba --- /dev/null +++ b/authentication_info_example.json @@ -0,0 +1,17 @@ +[ + { + "Username": "user1", + "Password": "123456", + "Repositories": [ + "user1-repo0.git", + "user1-repo1.git" + ] + }, + { + "Username": "user2", + "Password": "abcdef", + "Repositories": [ + "user2-repo0.git" + ] + } +] diff --git a/main.go b/main.go index 1a4e6e0..e5a2aa5 100644 --- a/main.go +++ b/main.go @@ -9,9 +9,7 @@ import ( ) func init() { - flag.BoolVar(&server.DefaultConfig.RequireAuth, "require_auth", server.DefaultConfig.RequireAuth, "enable basic auth") - flag.StringVar(&server.DefaultConfig.AuthPassEnvVar, "auth_pass_env_var", server.DefaultConfig.AuthPassEnvVar, "set an env var to provide the basic auth pass as") - flag.StringVar(&server.DefaultConfig.AuthUserEnvVar, "auth_user_env_var", server.DefaultConfig.AuthUserEnvVar, "set an env var to provide the basic auth user as") + flag.StringVar(&server.DefaultConfig.AuthInfoFilePath, "auth_data", server.DefaultConfig.AuthInfoFilePath, "set auth info data path") flag.StringVar(&server.DefaultConfig.DefaultEnv, "default_env", server.DefaultConfig.DefaultEnv, "set the default env") flag.StringVar(&server.DefaultConfig.ProjectRoot, "project_root", server.DefaultConfig.ProjectRoot, "set project root") flag.StringVar(&server.DefaultConfig.GitBinPath, "git_bin_path", server.DefaultConfig.GitBinPath, "set git bin path") diff --git a/server/server.go b/server/server.go index 6891bf6..ad2c979 100644 --- a/server/server.go +++ b/server/server.go @@ -5,6 +5,7 @@ import ( "compress/gzip" "fmt" "io" + "encoding/json" "log" "net/http" "os" @@ -16,6 +17,12 @@ import ( "time" ) +type AuthInfo struct { + Username string + Password string + Repositories []string +} + type Service struct { Method string Handler func(HandlerReq) @@ -23,9 +30,7 @@ type Service struct { } type Config struct { - RequireAuth bool - AuthPassEnvVar string - AuthUserEnvVar string + AuthInfoFilePath string DefaultEnv string ProjectRoot string GitBinPath string @@ -47,9 +52,7 @@ var ( DefaultAddress = ":8080" DefaultConfig = Config{ - RequireAuth: false, - AuthPassEnvVar: "", - AuthUserEnvVar: "", + AuthInfoFilePath: "/tmp/authentication_info_example.json", DefaultEnv: "", ProjectRoot: "/tmp", GitBinPath: "/usr/bin/git", @@ -122,28 +125,46 @@ func serviceRpc(hr HandlerReq) { return } - w.Header().Set("Content-Type", fmt.Sprintf("application/x-git-%s-result", rpc)) - w.Header().Set("Connection", "Keep-Alive") - w.Header().Set("Transfer-Encoding", "chunked") - w.Header().Set("X-Content-Type-Options", "nosniff") - w.WriteHeader(http.StatusOK) - env := os.Environ() if DefaultConfig.DefaultEnv != "" { env = append(env, DefaultConfig.DefaultEnv) } - user, password, authok := r.BasicAuth() + username, password, authok := r.BasicAuth() + user := FindUser(username) if authok { - if DefaultConfig.AuthUserEnvVar != "" { - env = append(env, fmt.Sprintf("%s=%s", DefaultConfig.AuthUserEnvVar, user)) + // Check is user has access to repository + if user.Username != "" && user.Password != "" { + requestRepo := strings.Replace(dir, DefaultConfig.ProjectRoot, "", 1) + allow := false + for _, repo := range user.Repositories { + if repo == requestRepo { + allow = true + break + } + } + + if !allow { + renderNoAccess(w) + return + } + } + + if user.Username != "" { + env = append(env, fmt.Sprintf("%s=%s", user.Username, username)) } - if DefaultConfig.AuthPassEnvVar != "" { - env = append(env, fmt.Sprintf("%s=%s", DefaultConfig.AuthPassEnvVar, password)) + if user.Password != "" { + env = append(env, fmt.Sprintf("%s=%s", user.Password, password)) } } + w.Header().Set("Content-Type", fmt.Sprintf("application/x-git-%s-result", rpc)) + w.Header().Set("Connection", "Keep-Alive") + w.Header().Set("Transfer-Encoding", "chunked") + w.Header().Set("X-Content-Type-Options", "nosniff") + w.WriteHeader(http.StatusOK) + args := []string{rpc, "--stateless-rpc", dir} cmd := exec.Command(DefaultConfig.GitBinPath, args...) version := r.Header.Get("Git-Protocol") @@ -214,13 +235,15 @@ func getInfoRefs(hr HandlerReq) { access := hasAccess(r, dir, service_name, false) version := r.Header.Get("Git-Protocol") - user, password, authok := r.BasicAuth() - if DefaultConfig.RequireAuth && !authok { + username, password, authok := r.BasicAuth() + if DefaultConfig.AuthInfoFilePath != "" && !authok { renderAuthRequire(w) return } - if authok && user != DefaultConfig.AuthUserEnvVar && password != DefaultConfig.AuthPassEnvVar { + // Check user credential + user := FindUser(username) + if authok && !(username == user.Username && password == user.Password) { w.WriteHeader(http.StatusUnauthorized) return } @@ -244,6 +267,33 @@ func getInfoRefs(hr HandlerReq) { } } +func FindUser(username string) AuthInfo { + if username == "" { + return AuthInfo{} + } + + var authInfo []AuthInfo + var user AuthInfo + content, err := os.ReadFile(DefaultConfig.AuthInfoFilePath) + if err != nil { + log.Print(err) + } + + err = json.Unmarshal(content, &authInfo) + if err != nil { + log.Print(err) + } + + for _, auth := range authInfo { + if auth.Username == username { + user = auth + break + } + } + + return user +} + func getInfoPacks(hr HandlerReq) { hdrCacheForever(hr.w) sendFile("text/plain; charset=utf-8", hr)