|
|
|
|
@@ -20,13 +20,20 @@ package common
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"encoding/base64"
|
|
|
|
|
"encoding/xml"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"log"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"regexp"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/url"
|
|
|
|
|
"slices"
|
|
|
|
|
"strings"
|
|
|
|
|
"syscall"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type ObsClient struct {
|
|
|
|
|
@@ -34,6 +41,8 @@ type ObsClient struct {
|
|
|
|
|
client *http.Client
|
|
|
|
|
user, password string
|
|
|
|
|
cookie string
|
|
|
|
|
sshkey string
|
|
|
|
|
sshkeyfile string
|
|
|
|
|
|
|
|
|
|
HomeProject string
|
|
|
|
|
}
|
|
|
|
|
@@ -45,10 +54,12 @@ func NewObsClient(host string) (*ObsClient, error) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &ObsClient{
|
|
|
|
|
baseUrl: baseUrl,
|
|
|
|
|
client: &http.Client{},
|
|
|
|
|
user: obsUser,
|
|
|
|
|
password: obsPassword,
|
|
|
|
|
baseUrl: baseUrl,
|
|
|
|
|
client: &http.Client{},
|
|
|
|
|
user: obsUser,
|
|
|
|
|
password: obsPassword,
|
|
|
|
|
sshkey: obsSshkey,
|
|
|
|
|
sshkeyfile: obsSshkeyFile,
|
|
|
|
|
|
|
|
|
|
HomeProject: fmt.Sprintf("home:%s", obsUser),
|
|
|
|
|
}, nil
|
|
|
|
|
@@ -139,13 +150,7 @@ func parseProjectMeta(data []byte) (*ProjectMeta, error) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ObsClient) GetGroupMeta(gid string) (*GroupMeta, error) {
|
|
|
|
|
req, err := http.NewRequest("GET", c.baseUrl.JoinPath("group", gid).String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
req.SetBasicAuth(c.user, c.password)
|
|
|
|
|
res, err := c.client.Do(req)
|
|
|
|
|
|
|
|
|
|
res, err := c.ObsRequest("GET", c.baseUrl.JoinPath("group", gid).String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
@@ -174,13 +179,7 @@ func (c *ObsClient) GetGroupMeta(gid string) (*GroupMeta, error) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ObsClient) GetUserMeta(uid string) (*UserMeta, error) {
|
|
|
|
|
req, err := http.NewRequest("GET", c.baseUrl.JoinPath("person", uid).String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
req.SetBasicAuth(c.user, c.password)
|
|
|
|
|
res, err := c.client.Do(req)
|
|
|
|
|
|
|
|
|
|
res, err := c.ObsRequest("GET", c.baseUrl.JoinPath("person", uid).String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
@@ -208,13 +207,100 @@ func (c *ObsClient) GetUserMeta(uid string) (*UserMeta, error) {
|
|
|
|
|
return &meta, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ObsClient) GetProjectMeta(project string) (*ProjectMeta, error) {
|
|
|
|
|
req, err := http.NewRequest("GET", c.baseUrl.JoinPath("source", project, "_meta").String(), nil)
|
|
|
|
|
func (c *ObsClient) ObsRequest(method string, url string, body io.Reader) (*http.Response, error) {
|
|
|
|
|
req, err := http.NewRequest(method, url, body)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
req.SetBasicAuth(c.user, c.password)
|
|
|
|
|
if body != nil {
|
|
|
|
|
req.Body = io.NopCloser(body)
|
|
|
|
|
}
|
|
|
|
|
if c.cookie != "" {
|
|
|
|
|
req.Header.Add("cookie", c.cookie)
|
|
|
|
|
}
|
|
|
|
|
res, err := c.client.Do(req)
|
|
|
|
|
if err == nil && res.StatusCode == 200 {
|
|
|
|
|
auth_cookie := res.Header.Get("set-cookie")
|
|
|
|
|
if auth_cookie != "" {
|
|
|
|
|
c.cookie = auth_cookie
|
|
|
|
|
}
|
|
|
|
|
return res, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if res.StatusCode == 401 {
|
|
|
|
|
// log.Printf("new authentication is needed ...")
|
|
|
|
|
if c.sshkey == "" {
|
|
|
|
|
req.SetBasicAuth(c.user, c.password)
|
|
|
|
|
} else {
|
|
|
|
|
www := res.Header.Get("www-authenticate")
|
|
|
|
|
// log.Printf("www-authenticate %s", www)
|
|
|
|
|
re := regexp.MustCompile(`Signature realm="(.*?)",headers="\(created\)`)
|
|
|
|
|
match := re.FindStringSubmatch(www)
|
|
|
|
|
if len(match) < 1 {
|
|
|
|
|
return nil, errors.New("No realm found")
|
|
|
|
|
}
|
|
|
|
|
realm := string(match[1])
|
|
|
|
|
sshKeygenPath, err := exec.LookPath("ssh-keygen")
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println("ssh-keygen not found")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SSH Sign
|
|
|
|
|
cmd := exec.Command(sshKeygenPath, "-Y", "sign", "-f", c.sshkeyfile, "-n", realm, "-q")
|
|
|
|
|
now := time.Now().Unix()
|
|
|
|
|
sigdata := fmt.Sprintf("(created): %d", now)
|
|
|
|
|
cmd.Stdin = strings.NewReader(sigdata)
|
|
|
|
|
stdout, err := cmd.Output()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Panic("SSH sign error:%s", cmd.Stderr)
|
|
|
|
|
if exitError, ok := err.(*exec.ExitError); ok {
|
|
|
|
|
waitStatus := exitError.Sys().(syscall.WaitStatus)
|
|
|
|
|
exitCode := waitStatus.ExitStatus()
|
|
|
|
|
return nil, errors.New(fmt.Sprintf("ssh-keygen signature creation failed: %d", exitCode))
|
|
|
|
|
}
|
|
|
|
|
return nil, errors.New("ssh-keygen signature creation failed")
|
|
|
|
|
}
|
|
|
|
|
reg := regexp.MustCompile("(?s)-----BEGIN SSH SIGNATURE-----\n(.*?)\n-----END SSH SIGNATURE-----")
|
|
|
|
|
match = reg.FindStringSubmatch(string(stdout))
|
|
|
|
|
if len(match) < 2 {
|
|
|
|
|
return nil, errors.New("could not extract ssh signature")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signature, err := base64.StdEncoding.DecodeString(string(match[1]))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signatureBase64 := base64.StdEncoding.EncodeToString(signature)
|
|
|
|
|
authorization := fmt.Sprintf(`keyId="%s",algorithm="ssh",headers="(created)",created=%d,signature="%s"`,
|
|
|
|
|
c.user, now, signatureBase64)
|
|
|
|
|
|
|
|
|
|
// log.Printf("Add Authorization Signature ", authorization)
|
|
|
|
|
req.Header.Add("Authorization", "Signature " + authorization)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Another time with authentification header
|
|
|
|
|
// log.Printf("Trying again with authorization: %s", req.Header.Get("Authorization"))
|
|
|
|
|
|
|
|
|
|
res, err = c.client.Do(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Panic("Authentification failed: %d", res.StatusCode)
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store the cookie for next call
|
|
|
|
|
auth_cookie := res.Header.Get("set-cookie")
|
|
|
|
|
if auth_cookie != "" {
|
|
|
|
|
c.cookie = auth_cookie
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ObsClient) GetProjectMeta(project string) (*ProjectMeta, error) {
|
|
|
|
|
res, err := c.ObsRequest("GET", c.baseUrl.JoinPath("source", project, "_meta").String(), nil)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
@@ -238,12 +324,7 @@ func (c *ObsClient) GetProjectMeta(project string) (*ProjectMeta, error) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ObsClient) GetPackageMeta(project, pkg string) (*PackageMeta, error) {
|
|
|
|
|
req, err := http.NewRequest("GET", c.baseUrl.JoinPath("source", project, pkg, "_meta").String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
req.SetBasicAuth(c.user, c.password)
|
|
|
|
|
res, err := c.client.Do(req)
|
|
|
|
|
res, err := c.ObsRequest("GET", c.baseUrl.JoinPath("source", project, pkg, "_meta").String(), nil)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
@@ -311,19 +392,12 @@ func (c *ObsClient) SetProjectMeta(meta *ProjectMeta) error {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req, err := http.NewRequest("PUT", c.baseUrl.JoinPath("source", meta.Name, "_meta").String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
req.SetBasicAuth(c.user, c.password)
|
|
|
|
|
xml, err := xml.Marshal(meta)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
req.Body = io.NopCloser(bytes.NewReader(xml))
|
|
|
|
|
log.Printf("xml: %s", xml)
|
|
|
|
|
res, err := c.client.Do(req)
|
|
|
|
|
|
|
|
|
|
res, err := c.ObsRequest("PUT", c.baseUrl.JoinPath("source", meta.Name, "_meta").String(), io.NopCloser(bytes.NewReader(xml)))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
@@ -339,14 +413,7 @@ func (c *ObsClient) SetProjectMeta(meta *ProjectMeta) error {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ObsClient) DeleteProject(project string) error {
|
|
|
|
|
req, err := http.NewRequest("DELETE", c.baseUrl.JoinPath("source", project).String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.SetBasicAuth(c.user, c.password)
|
|
|
|
|
res, err := c.client.Do(req)
|
|
|
|
|
|
|
|
|
|
res, err := c.ObsRequest("DELETE", c.baseUrl.JoinPath("source", project).String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
@@ -597,10 +664,7 @@ func (obs ObsProjectNotFound) Error() string {
|
|
|
|
|
func (c *ObsClient) ProjectConfig(project string) (string, error) {
|
|
|
|
|
u := c.baseUrl.JoinPath("source", project, "_config")
|
|
|
|
|
|
|
|
|
|
req, err := http.NewRequest("GET", u.String(), nil)
|
|
|
|
|
req.SetBasicAuth(c.user, c.password)
|
|
|
|
|
res, err := c.client.Do(req)
|
|
|
|
|
|
|
|
|
|
res, err := c.ObsRequest("GET", u.String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
@@ -625,15 +689,7 @@ func (c *ObsClient) BuildStatus(project string, packages ...string) (*BuildResul
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
u.RawQuery = query.Encode()
|
|
|
|
|
req, err := http.NewRequest("GET", u.String(), nil)
|
|
|
|
|
log.Print(u.String())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.SetBasicAuth(c.user, c.password)
|
|
|
|
|
res, err := c.client.Do(req)
|
|
|
|
|
|
|
|
|
|
res, err := c.ObsRequest("GET", u.String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
looks fine, but whitespace is broken. mix of tab/space?