repos / git-pr

a self-hosted git collaboration server
git clone https://github.com/picosh/git-pr.git

commit
067bd37
parent
c44fec8
author
Eric Bower
date
2026-02-23 11:40:18 -0500 EST
refactor: require user to create an account

We have noticed that users are accidentally creating accounts because we automatically create an account for any operation.  So instead we are going to require a one-line remote cli command to register the account first we can help users figure out they are using the wrong pubkey.
4 files changed,  +53, -24
M cli.go
M pr.go
M cli.go
+34, -15
  1@@ -13,6 +13,10 @@ import (
  2 	"github.com/urfave/cli/v2"
  3 )
  4 
  5+func errNotExist(host, pubkey string) error {
  6+	return fmt.Errorf("User does not exist, run `ssh <username>@%s register` to create an account\nPubkey: %s", host, pubkey)
  7+}
  8+
  9 func NewTabWriter(out io.Writer) *tabwriter.Writer {
 10 	return tabwriter.NewWriter(out, 0, 0, 1, ' ', tabwriter.TabIndent)
 11 }
 12@@ -237,7 +241,7 @@ To get started, submit a new patch request:
 13 					pubkey := be.Pubkey(sesh.PublicKey())
 14 					user, err := pr.GetUserByPubkey(pubkey)
 15 					if err != nil {
 16-						return err
 17+						return errNotExist(be.Cfg.Host, pubkey)
 18 					}
 19 					isPubkey := cCtx.Bool("pubkey")
 20 					prID := cCtx.Int64("pr")
 21@@ -290,6 +294,21 @@ To get started, submit a new patch request:
 22 					return nil
 23 				},
 24 			},
 25+			{
 26+				Name:  "register",
 27+				Usage: "Create an account",
 28+				Args:  true,
 29+				Flags: []cli.Flag{},
 30+				Action: func(cCtx *cli.Context) error {
 31+					pubkey := be.Pubkey(sesh.PublicKey())
 32+					user, err := pr.RegisterUser(pubkey, userName)
 33+					if err != nil {
 34+						return err
 35+					}
 36+					wish.Printf(sesh, "User created successfully!\nUser: %s\nPubkey: %s\n", user.Name, pubkey)
 37+					return nil
 38+				},
 39+			},
 40 			{
 41 				Name:  "ps",
 42 				Usage: "Mange patchsets",
 43@@ -347,9 +366,9 @@ To get started, submit a new patch request:
 44 						Args:      true,
 45 						ArgsUsage: "[repoName]",
 46 						Action: func(cCtx *cli.Context) error {
 47-							user, err := pr.UpsertUser(pubkey, userName)
 48+							user, err := pr.GetUserByPubkey(pubkey)
 49 							if err != nil {
 50-								return err
 51+								return errNotExist(be.Cfg.Host, pubkey)
 52 							}
 53 
 54 							args := cCtx.Args()
 55@@ -528,9 +547,9 @@ To get started, submit a new patch request:
 56 						Args:      true,
 57 						ArgsUsage: "[repoName]",
 58 						Action: func(cCtx *cli.Context) error {
 59-							user, err := pr.UpsertUser(pubkey, userName)
 60+							user, err := pr.GetUserByPubkey(pubkey)
 61 							if err != nil {
 62-								return err
 63+								return errNotExist(be.Cfg.Host, pubkey)
 64 							}
 65 
 66 							args := cCtx.Args()
 67@@ -631,9 +650,9 @@ To get started, submit a new patch request:
 68 									return err
 69 								}
 70 
 71-								user, err := pr.UpsertUser(pubkey, userName)
 72+								user, err := pr.GetUserByPubkey(pubkey)
 73 								if err != nil {
 74-									return err
 75+									return errNotExist(be.Cfg.Host, pubkey)
 76 								}
 77 
 78 								repo, err := pr.GetRepoByID(prq.RepoID)
 79@@ -717,9 +736,9 @@ To get started, submit a new patch request:
 80 									return fmt.Errorf("PR has already been closed")
 81 								}
 82 
 83-								user, err := pr.UpsertUser(pubkey, userName)
 84+								user, err := pr.GetUserByPubkey(pubkey)
 85 								if err != nil {
 86-									return err
 87+									return errNotExist(be.Cfg.Host, pubkey)
 88 								}
 89 
 90 								err = pr.UpdatePatchRequestStatus(prID, user.ID, StatusClosed, cCtx.String("comment"))
 91@@ -782,9 +801,9 @@ To get started, submit a new patch request:
 92 								return fmt.Errorf("PR is already open")
 93 							}
 94 
 95-							user, err := pr.UpsertUser(pubkey, userName)
 96+							user, err := pr.GetUserByPubkey(pubkey)
 97 							if err != nil {
 98-								return err
 99+								return errNotExist(be.Cfg.Host, pubkey)
100 							}
101 
102 							err = pr.UpdatePatchRequestStatus(prID, user.ID, StatusOpen, cCtx.String("comment"))
103@@ -814,9 +833,9 @@ To get started, submit a new patch request:
104 								return err
105 							}
106 
107-							user, err := pr.UpsertUser(pubkey, userName)
108+							user, err := pr.GetUserByPubkey(pubkey)
109 							if err != nil {
110-								return err
111+								return errNotExist(be.Cfg.Host, pubkey)
112 							}
113 
114 							repo, err := pr.GetRepoByID(prq.RepoID)
115@@ -885,9 +904,9 @@ To get started, submit a new patch request:
116 								return err
117 							}
118 
119-							user, err := pr.UpsertUser(pubkey, userName)
120+							user, err := pr.GetUserByPubkey(pubkey)
121 							if err != nil {
122-								return err
123+								return errNotExist(be.Cfg.Host, pubkey)
124 							}
125 
126 							isReview := cCtx.Bool("review")
M e2e_test.go
+6, -0
 1@@ -31,6 +31,9 @@ func testSingleTenantE2E(t *testing.T) {
 2 	// Hack to wait for startup
 3 	time.Sleep(time.Millisecond * 100)
 4 
 5+	suite.userKey.MustCmd(suite.patch, "register")
 6+	suite.adminKey.MustCmd(suite.patch, "register")
 7+
 8 	t.Log("User cannot create repo")
 9 	_, err := suite.userKey.Cmd(suite.patch, "pr create test")
10 	if err == nil {
11@@ -63,6 +66,9 @@ func testMultiTenantE2E(t *testing.T) {
12 
13 	time.Sleep(time.Millisecond * 100)
14 
15+	suite.userKey.MustCmd(suite.patch, "register")
16+	suite.adminKey.MustCmd(suite.patch, "register")
17+
18 	t.Log("Admin should be able to create a repo")
19 	suite.adminKey.MustCmd(nil, "repo create test")
20 
M pr.go
+6, -6
 1@@ -31,7 +31,7 @@ type GitPatchRequest interface {
 2 	GetRepoByID(repoID int64) (*Repo, error)
 3 	GetRepoByName(user *User, repoName string) (*Repo, error)
 4 	CreateRepo(user *User, repoName string) (*Repo, error)
 5-	UpsertUser(pubkey, name string) (*User, error)
 6+	RegisterUser(pubkey, name string) (*User, error)
 7 	IsBanned(pubkey, ipAddress string) error
 8 	SubmitPatchRequest(repoID int64, userID int64, patchset io.Reader) (*PatchRequest, error)
 9 	SubmitPatchset(prID, userID int64, op PatchsetOp, patchset io.Reader) ([]*Patch, error)
10@@ -194,16 +194,16 @@ func (pr PrCmd) createUser(pubkey, name string) (*User, error) {
11 	return user, err
12 }
13 
14-func (pr PrCmd) UpsertUser(pubkey, name string) (*User, error) {
15+func (pr PrCmd) RegisterUser(pubkey, name string) (*User, error) {
16 	sanName := strings.ToLower(name)
17 	if pubkey == "" {
18 		return nil, fmt.Errorf("must provide pubkey during upsert")
19 	}
20-	user, err := pr.GetUserByPubkey(pubkey)
21-	if err != nil {
22-		user, err = pr.createUser(pubkey, sanName)
23+	_, err := pr.GetUserByPubkey(pubkey)
24+	if err == nil {
25+		return nil, fmt.Errorf("pubkey is already registered by another user")
26 	}
27-	return user, err
28+	return pr.createUser(pubkey, sanName)
29 }
30 
31 func (pr PrCmd) GetPatchsetsByPrID(prID int64) ([]*Patchset, error) {
M tmpl/pages/index.html
+7, -3
 1@@ -227,12 +227,16 @@ git push origin main
 2     <summary>First time user?</summary>
 3 
 4     <div>
 5-      Using this service for the first time?  Creating a patch request is simple:
 6+      Using this service for the first time? First you need to create an account:
 7     </div>
 8 
 9-    <pre>git format-patch main --stdout | ssh {{.MetaData.URL}} pr create {repo}</pre>
10+    <blockquote>{username} is the name of your account</blockquote>
11+
12+    <pre>ssh {username}@{{.MetaData.URL}} register</pre>
13 
14-    <div>When running that command we will automatically create a user and a repo if one doesn't exist.</div>
15+    <div>After that, creating a patch request is simple:</div>
16+
17+    <pre>git format-patch main --stdout | ssh {{.MetaData.URL}} pr create {repo}</pre>
18 
19     <div>Want to submit a v2 of the patch request?</div>
20