move auth module into a new project (#522)

* move auth module into a new project

* upgrade go 1.22.2. to 1.22.4

---------

Co-authored-by: rick <LinuxSuRen@users.noreply.github.com>
This commit is contained in:
Rick 2024-08-09 14:28:10 +08:00 committed by GitHub
parent ad1521bbd1
commit f63520e765
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 21 additions and 424 deletions

View File

@ -5,7 +5,7 @@ COPY console/atest-ui .
RUN npm install --ignore-scripts --registry=https://registry.npmmirror.com
RUN npm run build-only
FROM docker.io/golang:1.22.2 AS builder
FROM docker.io/golang:1.22.4 AS builder
ARG VERSION
ARG GOPROXY

View File

@ -42,7 +42,6 @@ import (
"github.com/linuxsuren/api-testing/pkg/downloader"
"github.com/linuxsuren/api-testing/pkg/logging"
"github.com/linuxsuren/api-testing/pkg/mock"
"github.com/linuxsuren/api-testing/pkg/oauth"
template "github.com/linuxsuren/api-testing/pkg/render"
"github.com/linuxsuren/api-testing/pkg/server"
"github.com/linuxsuren/api-testing/pkg/service"
@ -51,6 +50,8 @@ import (
"github.com/linuxsuren/api-testing/pkg/testing/remote"
"github.com/linuxsuren/api-testing/pkg/util"
fakeruntime "github.com/linuxsuren/go-fake-runtime"
atestoauth "github.com/linuxsuren/api-testing/pkg/oauth"
"github.com/linuxsuren/oauth-hub"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
@ -182,7 +183,7 @@ func (o *serverOption) preRunE(cmd *cobra.Command, args []string) (err error) {
return
}
grpcOpts = append(grpcOpts, oauth.NewAuthInterceptor(o.oauthGroup))
grpcOpts = append(grpcOpts, atestoauth.NewAuthInterceptor(o.oauthGroup))
}
if o.tls {
if o.tlsCert != "" && o.tlsKey != "" {

5
go.mod
View File

@ -1,6 +1,6 @@
module github.com/linuxsuren/api-testing
go 1.22.2
go 1.22.4
require (
github.com/Masterminds/sprig/v3 v3.2.3
@ -33,7 +33,7 @@ require (
github.com/tidwall/gjson v1.14.4
github.com/xeipuuv/gojsonschema v1.2.0
go.uber.org/zap v1.27.0
golang.org/x/oauth2 v0.18.0
golang.org/x/oauth2 v0.22.0
golang.org/x/sync v0.6.0
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80
google.golang.org/grpc v1.62.1
@ -64,6 +64,7 @@ require (
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/linuxsuren/oauth-hub v0.0.0-20240809060240-e78c21b5d8d4 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect

6
go.sum
View File

@ -119,6 +119,10 @@ github.com/linuxsuren/go-fake-runtime v0.0.4 h1:y+tvBuw6MKTCav8Bo5HWwaXhBx1Z//VA
github.com/linuxsuren/go-fake-runtime v0.0.4/go.mod h1:zmh6J78hSnWZo68faMA2eKOdaEp8eFbERHi3ZB9xHCQ=
github.com/linuxsuren/go-service v0.0.0-20231225060426-efabcd3a5161 h1:dSL/ah6zaRGqH3FW0ogtMjP6xCFXX5NsgWJTaNIofI4=
github.com/linuxsuren/go-service v0.0.0-20231225060426-efabcd3a5161/go.mod h1:QX22v61PxpOfJa4Xug8qzGTbPjclDZFx2j1PlGLknJw=
github.com/linuxsuren/oauth-hub v0.0.0-20240809035103-220d1f431cc3 h1:Nmh7TETH85skH7Wfw6M5J8DhdAyAGodGOwshNfzpM88=
github.com/linuxsuren/oauth-hub v0.0.0-20240809035103-220d1f431cc3/go.mod h1:NrsEbf2IUUmNUoNbTkeWMcswb+4nEMniZ/8xTUhk2zw=
github.com/linuxsuren/oauth-hub v0.0.0-20240809060240-e78c21b5d8d4 h1:muVmKxx+JneaVgUKHqLc+As5vpgKXZAfVu6h+iyb5LQ=
github.com/linuxsuren/oauth-hub v0.0.0-20240809060240-e78c21b5d8d4/go.mod h1:6K1L5ajpFTNO8iJSsNrxMWAigAqczI0UPfEV9NSE0nc=
github.com/linuxsuren/unstructured v0.0.1 h1:ilUA8MUYbR6l9ebo/YPV2bKqlf62bzQursDSE+j00iU=
github.com/linuxsuren/unstructured v0.0.1/go.mod h1:KH6aTj+FegzGBzc1vS6mzZx3/duhTUTEVyW5sO7p4as=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
@ -234,6 +238,8 @@ golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=

View File

@ -1,4 +1,4 @@
go 1.22.2
go 1.22.4
use (
.

View File

@ -144,6 +144,7 @@ cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzc
cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/contactcenterinsights v1.10.0 h1:YR2aPedGVQPpFBZXJnPkqRj8M//8veIZZH5ZvICoXnI=
cloud.google.com/go/contactcenterinsights v1.10.0/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM=
cloud.google.com/go/contactcenterinsights v1.12.1 h1:EiGBeejtDDtr3JXt9W7xlhXyZ+REB5k2tBgVPVtmNb0=
@ -753,8 +754,6 @@ github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edY
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bool64/httpmock v0.1.13 h1:3QpRXQ5kwHLW8xnVT8+Ug7VS6RerhdEFV+RWYC61aVo=
github.com/bool64/httpmock v0.1.13/go.mod h1:YMTLaypQ3o5DAx78eA/kDRSLec0f+42sLMDmHdmeY+E=
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=

View File

@ -1,50 +0,0 @@
/*
Copyright 2023 API Testing Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oauth
type dexOAuthProvider struct {
server string
}
// AllScopes returns all the supported scopes
func (p *dexOAuthProvider) AllScopes() []string {
return []string{"openid", "email", "groups", "profile", "offline_access"}
}
func (p *dexOAuthProvider) MinimalScopes() []string {
return p.AllScopes()
}
func (p *dexOAuthProvider) GetName() string {
return "dex"
}
func (p *dexOAuthProvider) GetServer() string {
return p.server
}
func (p *dexOAuthProvider) SetServer(server string) {
p.server = server
}
func (p *dexOAuthProvider) GetTokenURL() string {
return "/api/dex/token"
}
func (p *dexOAuthProvider) GetAuthURL() string {
return "/api/dex/auth"
}
func (p *dexOAuthProvider) GetUserInfoURL() string {
return "/api/dex/userinfo"
}
func init() {
RegisterOAuthProvider(&dexOAuthProvider{})
}

View File

@ -1,56 +0,0 @@
/*
Copyright 2023 API Testing Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oauth
import "github.com/linuxsuren/api-testing/pkg/logging"
var (
githubLogger = logging.DefaultLogger(logging.LogLevelInfo).WithName("github")
)
type githubOAuthProvider struct {
}
// AllScopes returns all the supported scopes
// See also https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps
func (p *githubOAuthProvider) AllScopes() []string {
return []string{"repo", "public_repo", "read:org", "user", "read:user", "user:email"}
}
func (p *githubOAuthProvider) MinimalScopes() []string {
return []string{"read:user", "public_repo"}
}
func (p *githubOAuthProvider) GetName() string {
return "github"
}
func (p *githubOAuthProvider) GetServer() string {
return "https://github.com/login"
}
func (p *githubOAuthProvider) SetServer(_ string) {
githubLogger.Info("not support")
}
func (p *githubOAuthProvider) GetTokenURL() string {
return "/oauth/access_token"
}
func (p *githubOAuthProvider) GetAuthURL() string {
return "/oauth/authorize"
}
func (p *githubOAuthProvider) GetUserInfoURL() string {
return "https://api.github.com/user"
}
func init() {
RegisterOAuthProvider(&githubOAuthProvider{})
}

View File

@ -1,66 +0,0 @@
/*
Copyright 2023 API Testing Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oauth
type gitlabOAuthProvider struct {
}
// AllScopes returns all the supported scopes
// See also https://docs.gitlab.com/ee/integration/oauth_provider.html
func (p *gitlabOAuthProvider) AllScopes() []string {
return []string{"api", "read_user", "read_api", "read_repository", "profile", "email"}
}
func (p *gitlabOAuthProvider) MinimalScopes() []string {
return []string{"read_user", "read_api", "read_repository", "profile"}
}
func (p *gitlabOAuthProvider) GetName() string {
return "gitlab"
}
func (p *gitlabOAuthProvider) GetServer() string {
return "https://gitlab.com"
}
func (p *gitlabOAuthProvider) SetServer(_ string) {
githubLogger.Info("not support")
}
func (p *gitlabOAuthProvider) GetTokenURL() string {
return "/oauth/token"
}
func (p *gitlabOAuthProvider) GetAuthURL() string {
return "/oauth/authorize"
}
func (p *gitlabOAuthProvider) GetUserInfoURL() string {
return "/oauth/userinfo"
}
type privateGitlabOAuthProvider struct {
*gitlabOAuthProvider
server string
}
func (p *privateGitlabOAuthProvider) GetName() string {
return "private-gitlab"
}
func (p *privateGitlabOAuthProvider) GetServer() string {
return p.server
}
func (p *privateGitlabOAuthProvider) SetServer(server string) {
p.server = server
}
func init() {
RegisterOAuthProvider(&gitlabOAuthProvider{})
RegisterOAuthProvider(&privateGitlabOAuthProvider{})
}

View File

@ -20,6 +20,7 @@ import (
"errors"
"github.com/linuxsuren/oauth-hub"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
@ -77,11 +78,11 @@ func (a *authInter) authInterceptor(ctx context.Context, req interface{}, info *
return
}
func GetUserFromContext(ctx context.Context) (user *UserInfo) {
func GetUserFromContext(ctx context.Context) (user *oauth.UserInfo) {
if md, ok := metadata.FromIncomingContext(ctx); ok {
data := md.Get("auth")
if len(data) > 0 {
user = GetUser(data[0])
user = oauth.GetUser(data[0])
}
}
return

View File

@ -19,6 +19,7 @@ import (
"context"
"testing"
"github.com/linuxsuren/oauth-hub"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
@ -47,7 +48,7 @@ func TestAuthInterceptor(t *testing.T) {
})
t.Run("normal", func(t *testing.T) {
accessToken["fake"] = &UserInfo{}
oauth.SetUser("fake", &oauth.UserInfo{})
ctx := metadata.NewIncomingContext(context.TODO(), metadata.Pairs("auth", "fake"))
inter := &authInter{}

View File

@ -1,141 +0,0 @@
/*
Copyright 2023 API Testing Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oauth
import (
"encoding/json"
"fmt"
"net/http"
"context"
"github.com/linuxsuren/api-testing/pkg/logging"
"github.com/linuxsuren/api-testing/pkg/util"
"golang.org/x/oauth2"
)
var (
accessToken = make(map[string]*UserInfo)
oauthLogger = logging.DefaultLogger(logging.LogLevelInfo).WithName("oauth")
)
func GetUser(token string) *UserInfo {
return accessToken[token]
}
type auth struct {
provider OAuthProvider
config oauth2.Config
verifier string
skipTlsVerify bool
state string
}
// NewAuth creates a new auth handler
func NewAuth(provider OAuthProvider, config oauth2.Config, skipTlsVerify bool) *auth {
config.Scopes = provider.MinimalScopes()
config.Endpoint.TokenURL = fmt.Sprintf("%s%s", provider.GetServer(), provider.GetTokenURL())
config.Endpoint.AuthURL = fmt.Sprintf("%s%s", provider.GetServer(), provider.GetAuthURL())
config.Endpoint.DeviceAuthURL = "https://github.com/login/device/code"
return &auth{
provider: provider,
config: config,
verifier: oauth2.GenerateVerifier(),
skipTlsVerify: skipTlsVerify,
state: util.String(6),
}
}
func (a *auth) Callback(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
r.ParseForm()
state := r.Form.Get("state")
if state != a.state {
http.Error(w, "State invalid", http.StatusBadRequest)
return
}
code := r.Form.Get("code")
if code == "" {
http.Error(w, "Code not found", http.StatusBadRequest)
return
}
oauthLogger.Info("get code", "code", code)
sslcli := util.TlsAwareHTTPClient(a.skipTlsVerify)
ctx := context.WithValue(r.Context(), oauth2.HTTPClient, sslcli)
token, err := a.config.Exchange(ctx, code, oauth2.VerifierOption(a.verifier))
a.getUserInfo(w, r, token, err)
}
func (a *auth) getUserInfo(w http.ResponseWriter, r *http.Request, token *oauth2.Token, err error) {
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
accessToken[token.AccessToken] = nil
// get userInfo, save it to session
if userInfo, err := GetUserInfo(a.provider, token.AccessToken, a.skipTlsVerify); err == nil {
accessToken[token.AccessToken] = userInfo
oauthLogger.Info("has login", "username", userInfo.Name)
} else {
oauthLogger.Info("failed to get userinfo", "error", err)
}
http.Redirect(w, r, "/?access_token="+token.AccessToken, http.StatusFound)
}
func (a *auth) RequestLocalToken(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
deviceCode := r.FormValue("device_code")
response, ok := deviceAuthResponseMap[deviceCode]
if !ok {
http.Error(w, "device code not found", http.StatusBadRequest)
return
}
token, err := a.config.DeviceAccessToken(r.Context(), response)
a.getUserInfo(w, r, token, err)
}
var deviceAuthResponseMap = map[string]*oauth2.DeviceAuthResponse{}
func (a *auth) RequestLocalCode(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
response, err := a.config.DeviceAuth(context.Background())
if err != nil {
oauthLogger.Info("failed to get device auth", "error", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
deviceAuthResponseMap[response.DeviceCode] = response
data, _ := json.Marshal(response)
w.Write(data)
}
func (a *auth) RequestCode(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
ref := r.Header.Get("Referer")
oauthLogger.Info("callback host", "host", r.Host)
if ref == "" {
a.config.RedirectURL = fmt.Sprintf("https://%s/oauth2/callback", r.Host)
} else {
a.config.RedirectURL = fmt.Sprintf("%soauth2/callback", ref)
}
u := a.config.AuthCodeURL(a.state, oauth2.S256ChallengeOption(a.verifier))
http.Redirect(w, r, u, http.StatusFound)
}

View File

@ -1,99 +0,0 @@
/*
Copyright 2023 API Testing Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oauth
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"github.com/linuxsuren/api-testing/pkg/logging"
"github.com/linuxsuren/api-testing/pkg/util"
)
var (
userLogger = logging.DefaultLogger(logging.LogLevelInfo).WithName("user")
)
type OAuthProvider interface {
AllScopes() []string
MinimalScopes() []string
GetName() string
GetServer() string
SetServer(string)
GetTokenURL() string
GetAuthURL() string
GetUserInfoURL() string
}
var allOAuthProviders = make(map[string]OAuthProvider)
func RegisterOAuthProvider(provier OAuthProvider) {
name := provier.GetName()
_, ok := allOAuthProviders[name]
if !ok {
allOAuthProviders[name] = provier
} else {
panic(fmt.Sprintf("duplicated oauth provider: %q", name))
}
}
func GetOAuthProvider(name string) OAuthProvider {
return allOAuthProviders[name]
}
type UserInfo struct {
Sub string `json:"sub"`
Name string `json:"name"`
PreferredUsername string `json:"preferred_username"`
Email string `json:"email"`
Picture string `json:"picture"`
Groups []string `json:"groups"`
}
func GetUserInfo(server OAuthProvider, token string, skipTlsVerify bool) (userInfo *UserInfo, err error) {
api := server.GetUserInfoURL()
if !strings.HasPrefix(api, "http://") && !strings.HasPrefix(api, "https://") {
api = fmt.Sprintf("%s%s", server.GetServer(), server.GetUserInfoURL())
}
req, err := http.NewRequest(http.MethodGet, api, nil)
if err != nil {
return
}
req.Header.Set("Authorization", "Bearer "+token)
client := util.TlsAwareHTTPClient(skipTlsVerify)
var resp *http.Response
if resp, err = client.Do(req); err != nil {
return
}
defer resp.Body.Close()
userLogger.Info("getting userinfo from", "name", server.GetName())
if resp.StatusCode == http.StatusOK {
var data []byte
if data, err = io.ReadAll(resp.Body); err != nil {
return
}
userInfo = &UserInfo{}
err = json.Unmarshal(data, userInfo)
}
return
}

View File

@ -1,6 +1,6 @@
module local
go 1.22.2
go 1.22.4
require github.com/golangci/golangci-lint v1.57.2