Check for repo write permissions -- needed to create labels
This commit is contained in:
parent
6f2ceb214d
commit
57177b126c
|
|
@ -429,6 +429,42 @@ func (c *Client) CreateLabel(owner, repo, labelName, color string) (*Label, erro
|
||||||
return &label, nil
|
return &label, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckRepoPermission verifies the bot user has write (push) access to the repo.
|
||||||
|
func (c *Client) CheckRepoPermission(owner, repo string) error {
|
||||||
|
reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s", c.baseURL, owner, repo)
|
||||||
|
httpReq, err := http.NewRequest("GET", reqURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
httpReq.Header.Set("Authorization", "token "+c.apiToken)
|
||||||
|
|
||||||
|
resp, err := c.httpClient.Do(httpReq)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("forgejo API request failed: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
respBody, _ := io.ReadAll(resp.Body)
|
||||||
|
return fmt.Errorf("forgejo API returned %d: %s", resp.StatusCode, string(respBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
var result struct {
|
||||||
|
Permissions struct {
|
||||||
|
Admin bool `json:"admin"`
|
||||||
|
Push bool `json:"push"`
|
||||||
|
Pull bool `json:"pull"`
|
||||||
|
} `json:"permissions"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !result.Permissions.Push {
|
||||||
|
return fmt.Errorf("bot user does not have write access to %s/%s — add it as a collaborator with Write permission", owner, repo)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetOrCreateLabel looks up a label by name, creating it if it doesn't exist.
|
// GetOrCreateLabel looks up a label by name, creating it if it doesn't exist.
|
||||||
func (c *Client) GetOrCreateLabel(owner, repo, labelName, color string) (*Label, error) {
|
func (c *Client) GetOrCreateLabel(owner, repo, labelName, color string) (*Label, error) {
|
||||||
label, err := c.GetLabel(owner, repo, labelName)
|
label, err := c.GetLabel(owner, repo, labelName)
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,19 @@ func (h *RepoHandler) Create(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify the bot user has write access to the Forgejo repo
|
||||||
|
if err := h.deps.ForgejoClient.CheckRepoPermission(forgejoOwner, forgejoRepo); err != nil {
|
||||||
|
log.Error().Err(err).Msg("repo permission check failed")
|
||||||
|
h.deps.Renderer.Render(c.Writer, c.Request, "admin/repos/new", map[string]interface{}{
|
||||||
|
"Error": "Forgejo repo check failed: " + err.Error(),
|
||||||
|
"Name": name,
|
||||||
|
"Slug": slug,
|
||||||
|
"ForgejoOwner": forgejoOwner,
|
||||||
|
"ForgejoRepo": forgejoRepo,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
webhookSecret, err := forgejo.GenerateWebhookSecret()
|
webhookSecret, err := forgejo.GenerateWebhookSecret()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("generate webhook secret error")
|
log.Error().Err(err).Msg("generate webhook secret error")
|
||||||
|
|
@ -105,10 +118,17 @@ func (h *RepoHandler) EditForm(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check bot write access to the Forgejo repo
|
||||||
|
var repoPermErr string
|
||||||
|
if err := h.deps.ForgejoClient.CheckRepoPermission(repo.ForgejoOwner, repo.ForgejoRepo); err != nil {
|
||||||
|
repoPermErr = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
h.deps.Renderer.Render(c.Writer, c.Request, "admin/repos/edit", map[string]interface{}{
|
h.deps.Renderer.Render(c.Writer, c.Request, "admin/repos/edit", map[string]interface{}{
|
||||||
"Repo": repo,
|
"Repo": repo,
|
||||||
"BaseURL": h.deps.Config.BaseURL,
|
"BaseURL": h.deps.Config.BaseURL,
|
||||||
"ForgejoURL": h.deps.Config.ForgejoURL,
|
"ForgejoURL": h.deps.Config.ForgejoURL,
|
||||||
|
"RepoPermErr": repoPermErr,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,27 @@
|
||||||
|
|
||||||
<h1 class="text-2xl font-bold text-gray-900 mb-6">Edit Repo</h1>
|
<h1 class="text-2xl font-bold text-gray-900 mb-6">Edit Repo</h1>
|
||||||
|
|
||||||
|
{{if .RepoPermErr}}
|
||||||
|
<div class="mb-6 rounded-md bg-red-50 p-4 ring-1 ring-red-200">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<svg class="h-5 w-5 text-red-600" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
|
||||||
|
</svg>
|
||||||
|
<p class="text-sm font-semibold text-red-800">Bot write access missing</p>
|
||||||
|
</div>
|
||||||
|
<p class="mt-1 text-sm text-red-700">{{.RepoPermErr}}</p>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="mb-6 rounded-md bg-green-50 p-4 ring-1 ring-green-200">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<svg class="h-5 w-5 text-green-600" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
<p class="text-sm font-semibold text-green-800">Bot write access verified</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{if .Repo.WebhookVerified}}
|
{{if .Repo.WebhookVerified}}
|
||||||
<div class="mb-6 rounded-md bg-green-50 p-4 ring-1 ring-green-200">
|
<div class="mb-6 rounded-md bg-green-50 p-4 ring-1 ring-green-200">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue