···
-
"github.com/bluesky-social/indigo/atproto/identity"
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/urfave/cli/v3"
-
"tangled.org/core/idresolver"
-
"tangled.org/core/knotserver/config"
···
func Run(ctx context.Context, cmd *cli.Command) error {
l := log.FromContext(ctx)
-
c, err := config.Load(ctx)
-
return fmt.Errorf("failed to load config: %w", err)
incomingUser := cmd.String("user")
gitDir := cmd.String("git-dir")
logPath := cmd.String("log-path")
···
l.Info("access denied: no interactive shells", "user", incomingUser)
fmt.Fprintf(os.Stderr, "Hi @%s! You've successfully authenticated.\n", incomingUser)
···
gitCommand := cmdParts[0]
-
// did:foo/repo-name or
-
// any of the above with a leading slash (/)
-
components := strings.Split(strings.TrimPrefix(strings.Trim(cmdParts[1], "'"), "/"), "/")
-
l.Info("command components", "components", components)
-
if len(components) != 2 {
-
l.Error("invalid repo format", "components", components)
-
fmt.Fprintln(os.Stderr, "invalid repo format, needs <user>/<repo> or /<user>/<repo>")
-
didOrHandle := components[0]
-
identity := resolveIdentity(ctx, c, l, didOrHandle)
-
did := identity.DID.String()
-
repoName := components[1]
-
qualifiedRepoName, _ := securejoin.SecureJoin(did, repoName)
validCommands := map[string]bool{
"git-receive-pack": true,
···
return fmt.Errorf("access denied: invalid git command")
-
if gitCommand != "git-upload-pack" {
-
if !isPushPermitted(l, incomingUser, qualifiedRepoName, endpoint) {
-
l.Error("access denied: user not allowed",
-
"reponame", qualifiedRepoName)
-
fmt.Fprintln(os.Stderr, "access denied: user not allowed")
-
fullPath, _ := securejoin.SecureJoin(gitDir, qualifiedRepoName)
l.Info("processing command",
···
gitCmd.Env = append(os.Environ(),
fmt.Sprintf("GIT_USER_DID=%s", incomingUser),
-
fmt.Sprintf("GIT_USER_PDS_ENDPOINT=%s", identity.PDSEndpoint()),
if err := gitCmd.Run(); err != nil {
···
l.Info("command completed",
-
func resolveIdentity(ctx context.Context, c *config.Config, l *slog.Logger, didOrHandle string) *identity.Identity {
-
resolver := idresolver.DefaultResolver(c.Server.PlcUrl)
-
ident, err := resolver.ResolveIdent(ctx, didOrHandle)
-
l.Error("Error resolving handle", "error", err, "handle", didOrHandle)
-
fmt.Fprintf(os.Stderr, "error resolving handle: %v\n", err)
-
if ident.Handle.IsInvalidHandle() {
-
l.Error("Error resolving handle", "invalid handle", didOrHandle)
-
fmt.Fprintf(os.Stderr, "error resolving handle: invalid handle\n")
-
func isPushPermitted(l *slog.Logger, user, qualifiedRepoName, endpoint string) bool {
-
u, _ := url.Parse(endpoint + "/push-allowed")
-
q.Add("repo", qualifiedRepoName)
-
req, err := http.Get(u.String())
-
l.Error("Error verifying permissions", "error", err)
-
fmt.Fprintf(os.Stderr, "error verifying permissions: %v\n", err)
-
l.Info("Checking push permission",
-
return req.StatusCode == http.StatusNoContent
···
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/urfave/cli/v3"
···
func Run(ctx context.Context, cmd *cli.Command) error {
l := log.FromContext(ctx)
incomingUser := cmd.String("user")
gitDir := cmd.String("git-dir")
logPath := cmd.String("log-path")
···
+
// TODO: greet user with their resolved handle instead of did
l.Info("access denied: no interactive shells", "user", incomingUser)
fmt.Fprintf(os.Stderr, "Hi @%s! You've successfully authenticated.\n", incomingUser)
···
gitCommand := cmdParts[0]
+
repoPath := cmdParts[1]
validCommands := map[string]bool{
"git-receive-pack": true,
···
return fmt.Errorf("access denied: invalid git command")
+
// qualify repo path from internal server which holds the knot config
+
qualifiedRepoPath, err := guardAndQualifyRepo(l, endpoint, incomingUser, repoPath, gitCommand)
+
l.Error("failed to run guard", "err", err)
+
fmt.Fprintln(os.Stderr, err)
+
fullPath, _ := securejoin.SecureJoin(gitDir, qualifiedRepoPath)
l.Info("processing command",
···
gitCmd.Env = append(os.Environ(),
fmt.Sprintf("GIT_USER_DID=%s", incomingUser),
if err := gitCmd.Run(); err != nil {
···
l.Info("command completed",
+
// runs guardAndQualifyRepo logic
+
func guardAndQualifyRepo(l *slog.Logger, endpoint, incomingUser, repo, gitCommand string) (string, error) {
+
u, _ := url.Parse(endpoint + "/guard")
+
q.Add("user", incomingUser)
+
q.Add("gitCmd", gitCommand)
+
resp, err := http.Get(u.String())
+
defer resp.Body.Close()
+
l.Info("Running guard", "url", u.String(), "status", resp.Status)
+
body, err := io.ReadAll(resp.Body)
+
switch resp.StatusCode {
+
case http.StatusForbidden:
+
l.Error("access denied: user not allowed", "did", incomingUser, "reponame", text)
+
return text, errors.New("access denied: user not allowed")
+
return "", errors.New(text)