Skip to content

Commit 2dbf11f

Browse files
authored
add gitee auth (#1)
* add gitee auth * fix
1 parent ca95c8c commit 2dbf11f

File tree

5 files changed

+122
-23
lines changed

5 files changed

+122
-23
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.vscode

BigFiles/main.go

+1-6
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@ import (
1212
)
1313

1414
func main() {
15-
user := os.Getenv("LFS_USER")
16-
pass := os.Getenv("LFS_PASS")
17-
if user == "" || pass == "" {
18-
log.Fatalln("LFS_USER and LFS_PASS must be set")
19-
}
2015
bucket := os.Getenv("LFS_BUCKET")
2116
if bucket == "" {
2217
log.Fatalln("LFS_BUCKET must be set")
@@ -25,7 +20,7 @@ func main() {
2520
s, err := server.New(server.Options{
2621
S3Accelerate: true,
2722
Bucket: bucket,
28-
IsAuthorized: auth.Static(user, pass),
23+
IsAuthorized: auth.GiteeAuth(),
2924
})
3025
if err != nil {
3126
log.Fatalln(err)

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,14 @@ v2.12.0](https://github.com/git-lfs/git-lfs/tree/v2.12.0/docs/api) server.
44
- It can be configured to use any S3-API-compatible backend for LFS storage.
55
- It does not currently implent the locking API.
66
- See [the default entrypoint](BigFiles/main.go).
7+
8+
9+
**TODO**:
10+
11+
1. 外部参数校验:username、password进行格式校验。
12+
2. 添加配置文件,将AKSK等写入配置文件中。可参考merlin-server配置文件的格式与读取方式。
13+
3. 添加测试用例。
14+
4. 认证方式支持token。
15+
5. 认证时校验用户在仓库内权限。
16+
6. 支持ssh。
17+
7. 仓库添加github action。

auth/gitee.go

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package auth
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"io"
7+
"net/http"
8+
"net/url"
9+
"os"
10+
"strings"
11+
)
12+
13+
type giteeUser struct {
14+
Login string `json:"login"`
15+
// Permission string `json:"permission"`
16+
}
17+
18+
type AccessToken struct {
19+
Token string `json:"access_token"`
20+
}
21+
22+
func GiteeAuth() func(string, string) error {
23+
return func(username, password string) error {
24+
token, err := getToken(username, password)
25+
if err != nil {
26+
return err
27+
}
28+
29+
return verifyUser(username, token)
30+
}
31+
}
32+
33+
// getToken gets access_token by username and password
34+
func getToken(username, password string) (string, error) {
35+
clientId := os.Getenv("CLIENT_ID")
36+
clientSecret := os.Getenv("CLIENT_SECRET")
37+
form := url.Values{}
38+
form.Add("scope", "user_info")
39+
form.Add("grant_type", "password")
40+
form.Add("username", username)
41+
form.Add("password", password)
42+
form.Add("client_id", clientId)
43+
form.Add("client_secret", clientSecret)
44+
45+
path := "https://gitee.com/oauth/token"
46+
response, err := http.Post(path, "application/x-www-form-urlencoded", strings.NewReader(form.Encode()))
47+
if err != nil {
48+
panic(err)
49+
}
50+
defer response.Body.Close()
51+
52+
if response.StatusCode != http.StatusOK {
53+
return "", errors.New("invalid credentials")
54+
}
55+
body, err := io.ReadAll(response.Body)
56+
if err != nil {
57+
panic(err)
58+
}
59+
var accessToken AccessToken
60+
err = json.Unmarshal(body, &accessToken)
61+
if err != nil {
62+
panic(err)
63+
}
64+
return accessToken.Token, nil
65+
}
66+
67+
// verifyUser verifies user info by access_token
68+
func verifyUser(username, token string) error {
69+
path := "https://gitee.com/api/v5/user?access_token=" + token
70+
req, err := http.NewRequest("GET", path, nil)
71+
if err != nil {
72+
panic(err)
73+
}
74+
req.Header.Set("Content-Type", "application/json;charset=UTF-8")
75+
76+
response, err := http.DefaultClient.Do(req)
77+
if err != nil {
78+
panic(err)
79+
}
80+
defer response.Body.Close()
81+
if response.StatusCode != http.StatusOK {
82+
return errors.New("invalid credentials")
83+
}
84+
body, err := io.ReadAll(response.Body)
85+
if err != nil {
86+
panic(err)
87+
}
88+
var giteeUser giteeUser
89+
err = json.Unmarshal(body, &giteeUser)
90+
if err != nil {
91+
panic(err)
92+
}
93+
if giteeUser.Login == username {
94+
return nil
95+
} else {
96+
return errors.New("username does not match")
97+
}
98+
}

server/server.go

+11-17
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"math"
88
"net/http"
9-
"net/url"
109
"os"
1110
"regexp"
1211
"time"
@@ -15,7 +14,6 @@ import (
1514
"github.com/metalogical/BigFiles/batch"
1615
"github.com/minio/minio-go/v7"
1716
"github.com/minio/minio-go/v7/pkg/credentials"
18-
"github.com/minio/minio-go/v7/pkg/s3utils"
1917
)
2018

2119
var S3PutLimit int = 5*int(math.Pow10(9)) - 1 // 5GB - 1
@@ -46,22 +44,18 @@ func (o Options) imputeFromEnv() (Options, error) {
4644
if region == "" {
4745
return o, errors.New("endpoint required")
4846
}
49-
o.Endpoint = fmt.Sprintf("s3.%s.amazonaws.com", region)
47+
o.Endpoint = region
5048
}
5149
if o.AccessKeyID == "" {
52-
if s3utils.IsAmazonEndpoint(url.URL{Host: o.Endpoint}) {
53-
o.AccessKeyID = os.Getenv("AWS_ACCESS_KEY_ID")
54-
if o.AccessKeyID == "" {
55-
return o, fmt.Errorf("AWS access key ID required for %s", o.Endpoint)
56-
}
57-
o.SecretAccessKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
58-
if o.SecretAccessKey == "" {
59-
return o, fmt.Errorf("AWS secret access key required for %s", o.Endpoint)
60-
}
61-
o.SessionToken = os.Getenv("AWS_SESSION_TOKEN")
62-
} else {
63-
return o, fmt.Errorf("access key & id required for %s", o.Endpoint)
50+
o.AccessKeyID = os.Getenv("AWS_ACCESS_KEY_ID")
51+
if o.AccessKeyID == "" {
52+
return o, fmt.Errorf("AWS access key ID required for %s", o.Endpoint)
53+
}
54+
o.SecretAccessKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
55+
if o.SecretAccessKey == "" {
56+
return o, fmt.Errorf("AWS secret access key required for %s", o.Endpoint)
6457
}
58+
o.SessionToken = os.Getenv("AWS_SESSION_TOKEN")
6559
}
6660
if o.Bucket == "" {
6761
return o, fmt.Errorf("bucket required")
@@ -100,7 +94,7 @@ func New(o Options) (http.Handler, error) {
10094
}
10195

10296
r := chi.NewRouter()
103-
r.Post("/objects/batch", s.handleBatch)
97+
r.Post("/{owner}/{repo}/objects/batch", s.handleBatch)
10498

10599
return r, nil
106100
}
@@ -127,7 +121,7 @@ func (s *server) handleBatch(w http.ResponseWriter, r *http.Request) {
127121
if username, password, ok := r.BasicAuth(); ok {
128122
err = s.isAuthorized(username, password)
129123
if err != nil {
130-
err = fmt.Errorf("Unauthorized: %w", err)
124+
err = fmt.Errorf("unauthorized: %w", err)
131125
}
132126
} else {
133127
err = errors.New("Unauthorized")

0 commit comments

Comments
 (0)