diff --git a/main.go b/main.go index 2dfba37..e999bf8 100644 --- a/main.go +++ b/main.go @@ -92,6 +92,12 @@ type RemoveCollaboratorOptions struct { CollaboratorName string `json:"collaborator_name"` } +type CreateSSHOptions struct { + KeyName string `json:"key_name"` + Key string `json:"key"` + Username string `json:"username"` +} + type ForkOptions struct { Owner string `json:"owner"` NewOwner string `json:"newOwner"` @@ -308,7 +314,7 @@ func findForks(repoURL, username, password string) ([]api.Repository, error) { } func getRemoteUrlFromRepo(repo *api.Repository) string { - return repo.CloneURL + return repo.SSHURL } func getRemoteUrl(giteaBaseURL, adminUsername, adminPassword, owner string, repo string) (string, error) { @@ -1184,6 +1190,200 @@ func handleUser(w http.ResponseWriter, r *http.Request) { } } +func getSSHKeysForUser(giteaBaseURL, adminUsername, adminPassword, username string) ([]api.PublicKey, error) { + req, err := http.NewRequest("GET", giteaBaseURL+"/users/"+username+"/keys/", nil) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", "application/json") + req.SetBasicAuth(string(adminUsername), string(adminPassword)) + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + log.Printf("Error reading Gitea response %v", err) + return nil, err + } + + var publicKeys []api.PublicKey + err = json.Unmarshal(bodyBytes, &publicKeys) + if err != nil { + log.Printf("Error reading Gitea response %v", err) + return nil, err + } + + return publicKeys, nil +} + +func handleGetUserSSHKeys(w http.ResponseWriter, r *http.Request) { + username := r.URL.Query().Get("username") + if username == "" { + http.Error(w, "Username must be provided to list SSH keys", http.StatusBadRequest) + return + } + + if keys, err := getSSHKeysForUser(access.URL, access.Username, access.Password, username); err == nil { + jsonData, _ := json.Marshal(keys) + w.WriteHeader(http.StatusOK) + w.Write([]byte(jsonData)) + } else { + http.Error(w, "Failed to list keys", http.StatusBadRequest) + log.Printf("Failed to list keys %v", err) + } +} + +func deleteSSHKeyForUser(giteaBaseURL, adminUsername, adminPassword, username, name string) error { + keys, err := getSSHKeysForUser(giteaBaseURL, adminUsername, adminPassword, username) + if err != nil { + return err + } + + var id int64 = -1 + for _, key := range keys { + if key.Title == name { + id = key.ID + break + } + } + if id == -1 { + return nil + } + + url := fmt.Sprintf("%s/admin/users/%s/keys/%d", giteaBaseURL, username, id) + req, err := http.NewRequest("DELETE", url, nil) + if err != nil { + return err + } + + req.Header.Add("Content-Type", "application/json") + req.SetBasicAuth(string(adminUsername), string(adminPassword)) + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusNoContent && resp.StatusCode != 404 { + return fmt.Errorf("HTTP Error: %d", resp.StatusCode) + } + + return nil +} + +func handleDeleteUserSSHKey(w http.ResponseWriter, r *http.Request) { + keyName := r.URL.Query().Get("key_name") + username := r.URL.Query().Get("username") + if keyName == "" || username == "" { + http.Error(w, "Key name and username must be provided to delete the SSH key", http.StatusBadRequest) + return + } + + if err := deleteSSHKeyForUser(access.URL, access.Username, access.Password, username, keyName); err == nil { + w.WriteHeader(http.StatusOK) + w.Write([]byte("Successfully deleted SSH key")) + } else { + http.Error(w, "Key deletion failed", http.StatusBadRequest) + log.Printf("Key deletion failed %v", err) + } +} + +func createSSHKeyForUser(giteaBaseURL, adminUsername, adminPassword, username, key, name string) error { + err := deleteSSHKeyForUser(giteaBaseURL, adminUsername, adminPassword, username, name) + if err != nil { + return err + } + + data := api.CreateKeyOption{ + Key: key, + Title: name, + ReadOnly: true, + } + + jsonData, _ := json.Marshal(data) + + req, err := http.NewRequest("POST", giteaBaseURL+"/admin/users/"+username+"/keys", bytes.NewBuffer(jsonData)) + if err != nil { + return err + } + + req.Header.Add("Content-Type", "application/json") + req.SetBasicAuth(string(adminUsername), string(adminPassword)) + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return fmt.Errorf("HTTP Error: %d", resp.StatusCode) + } + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + log.Printf("Error reading Gitea response %v", err) + return err + } + + var publicKeyResponse api.PublicKey + err = json.Unmarshal(bodyBytes, &publicKeyResponse) + if err != nil { + log.Printf("Error reading Gitea response %v", err) + return err + } + + fmt.Println(publicKeyResponse.ID, publicKeyResponse.Key) + + return nil +} + +func handleCreateUserSSHKey(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + defer r.Body.Close() + + if err != nil { + http.Error(w, "Failed reading request body", http.StatusInternalServerError) + return + } + + var options CreateSSHOptions + err = json.Unmarshal(body, &options) + if err != nil { + http.Error(w, "Failed parsing request body", http.StatusBadRequest) + return + } + + if options.Key == "" || options.KeyName == "" || options.Username == "" { + http.Error(w, "Key, KeyName, and Username must be provided to create the SSH key", http.StatusBadRequest) + return + } + + if err := createSSHKeyForUser(access.URL, access.Username, access.Password, options.Username, options.Key, options.KeyName); err == nil { + w.WriteHeader(http.StatusCreated) + w.Write([]byte("Successfully created SSH key")) + } else { + http.Error(w, "Key creation failed", http.StatusBadRequest) + log.Printf("Key creation failed %v", err) + } +} + +func handleUserSsh(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + handleGetUserSSHKeys(w, r) + case http.MethodPost: + handleCreateUserSSHKey(w, r) + case http.MethodDelete: + handleDeleteUserSSHKey(w, r) + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + func createRepoForUser(giteaBaseURL, adminUsername, adminPassword, username, name, description string, private bool) (*api.Repository, error) { data := api.CreateRepoOption{ Name: name, @@ -2006,6 +2206,7 @@ func main() { r := mux.NewRouter() r.HandleFunc("/onPush", webhookHandler) r.HandleFunc("/users", handleUser) + r.HandleFunc("/users/ssh", handleUserSsh) r.HandleFunc("/repos", handleRepo) r.HandleFunc("/repos/collaborators", handleRepoCollaborator) r.HandleFunc("/repos/modify", handleModifyRepoFiles).Methods("POST")