Skip to content

Commit

Permalink
Merge pull request #159 from 0xff-dev/main
Browse files Browse the repository at this point in the history
feat: support oidc auth
  • Loading branch information
bjwswang authored Oct 31, 2023
2 parents 7903096 + 5f2273a commit 1c1f8fd
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 19 deletions.
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.20
require (
github.com/99designs/gqlgen v0.17.40
github.com/amikos-tech/chroma-go v0.0.0-20230901221218-d0087270239e
github.com/coreos/go-oidc/v3 v3.7.0
github.com/go-logr/logr v1.2.0
github.com/gofiber/fiber/v2 v2.49.1
github.com/golang-jwt/jwt v3.2.2+incompatible
Expand All @@ -26,6 +27,7 @@ require (
require (
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.3 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
Expand Down Expand Up @@ -112,13 +114,13 @@ require (
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/oauth2 v0.13.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
Expand Down
13 changes: 10 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u9
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-oidc/v3 v3.7.0 h1:FTdj0uexT4diYIPlF4yoFVI5MRO1r5+SEcIpEw9vC0o=
github.com/coreos/go-oidc/v3 v3.7.0/go.mod h1:yQzSCqBnK3e6Fs5l+f5i0F8Kwf0zpH9bPEsbY00KanM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
Expand Down Expand Up @@ -179,6 +181,8 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
Expand Down Expand Up @@ -633,6 +637,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
Expand Down Expand Up @@ -748,8 +753,8 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -851,6 +856,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
Expand Down Expand Up @@ -953,8 +959,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
Expand Down
34 changes: 34 additions & 0 deletions graphql-server/go-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,37 @@ func (r *queryResolver) FindX(ctx context.Context, input string) (string, error)
The file `model/model_gen.go` has a new structure x that we defined.

All we have to do is just implement the `FindX` function. And the content of the function is up to you to play with.


## How to run

in the root dir of the project

```shell
# 1. build
make build-graphql-server

# 2. run parameters
$ ./go-bff-server -h
Usage of ./main:
-client-id string
oidc client id
-client-secret string
oidc client secret
-enable-playgroud
whether to open the graphql playground (default true)
-host string
bind to the host, default is 0.0.0.0
-issuer-url string
oidc issuer url
-kubeconfig string
Paths to a kubeconfig. Only required if out-of-cluster.
-master-url string
k8s master url
-port int
service listening port (default 8081)

# 3. run
./go-bff-server --client-id=bff-client --client-secret=some-secret --master-url=https://k8s-adress --issuer-url=https://oidc-server
```
17 changes: 15 additions & 2 deletions graphql-server/go-server/graph/datasource.resolvers.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion graphql-server/go-server/graph/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ package graph
//
// It serves as dependency injection for your app, add any dependencies you require here.

type Resolver struct{}
type Resolver struct{
}
21 changes: 15 additions & 6 deletions graphql-server/go-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,29 @@ import (
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground"
"k8s.io/klog/v2"
ctrl "sigs.k8s.io/controller-runtime"

"github.com/kubeagi/arcadia/graphql-server/go-server/graph"
"github.com/kubeagi/arcadia/graphql-server/go-server/pkg/client"
"github.com/kubeagi/arcadia/graphql-server/go-server/pkg/auth"
"github.com/kubeagi/arcadia/graphql-server/go-server/pkg/oidc"

_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
)

var (
host = flag.String("host", "", "bind to the host, default is 0.0.0.0")
port = flag.Int("port", 8081, "service listening port")
enablePlayground = flag.Bool("enable-playgroud", true, "whether to open the graphql playground")

issuerURL = flag.String("issuer-url", "", "oidc issuer url")
masterURL = flag.String("master-url", "", "k8s master url")
clientSecret = flag.String("client-secret", "", "oidc client secret")
clientID = flag.String("client-id", "", "oidc client id")
)

func main() {
cfg := ctrl.GetConfigOrDie()
client.InitClient(cfg)
flag.Parse()

oidc.InitOIDCArgs(*issuerURL, *masterURL, *clientSecret, *clientID)

srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}}))
srv.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) {
Expand All @@ -53,9 +61,10 @@ func main() {
})

if *enablePlayground {
http.Handle("/", playground.Handler("Arcadia", "/query"))
http.Handle("/", auth.AuthInterceptor(oidc.Verifier, playground.Handler("Arcadia-BFF-Server", "/query")))
}
http.Handle("/query", srv)
http.Handle("/query", auth.AuthInterceptor(oidc.Verifier, srv.ServeHTTP))

klog.Infof("listening server on port: %d", *port)
log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%d", *host, *port), nil))
}
62 changes: 62 additions & 0 deletions graphql-server/go-server/pkg/auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
Copyright 2023 KubeAGI.
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 auth

import (
"context"
"net/http"
"strings"

"github.com/coreos/go-oidc/v3/oidc"
)

type idtokenKey struct{}

func isBearerToken(token string) (bool, string) {
if len(token) < 6 {
return false, ""
}
head := strings.ToLower(token[:6])
payload := strings.TrimSpace(token[6:])
return head == "bearer" && len(payload) > 0, payload
}

func AuthInterceptor(oidcVerifier *oidc.IDTokenVerifier, hf http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
idtoken := r.Header.Get("Authorization")
ok, rawToken := isBearerToken(idtoken)
if !ok {
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte("Unauthorized. Please provide an oidc token"))
return
}
_, err := oidcVerifier.Verify(context.TODO(), rawToken)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte(err.Error()))
return
}
ctx := context.WithValue(r.Context(), idtokenKey{}, rawToken)
r = r.WithContext(ctx)
hf(w, r)
}
}

func ForOIDCToken(ctx context.Context) string {
v, _ := ctx.Value(idtokenKey{}).(string)
return v
}
11 changes: 11 additions & 0 deletions graphql-server/go-server/pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import (

"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"

"github.com/kubeagi/arcadia/graphql-server/go-server/pkg/oidc"
)

var (
Expand All @@ -37,3 +40,11 @@ func InitClient(cfg *rest.Config) {
func GetClient() dynamic.Interface {
return c
}

func GetClientByIDToken(idtoken string) (dynamic.Interface, error) {
cfg, err := clientcmd.BuildConfigFromKubeconfigGetter("", oidc.OIDCKubeGetter(idtoken))
if err != nil {
return nil, err
}
return dynamic.NewForConfig(cfg)
}
8 changes: 3 additions & 5 deletions graphql-server/go-server/pkg/datasource/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"

"github.com/kubeagi/arcadia/api/v1alpha1"
"github.com/kubeagi/arcadia/graphql-server/go-server/graph/model"
"github.com/kubeagi/arcadia/graphql-server/go-server/pkg/client"
)

func datasource2model(obj *unstructured.Unstructured) *model.Datasource {
Expand Down Expand Up @@ -58,8 +58,7 @@ func datasource2model(obj *unstructured.Unstructured) *model.Datasource {
return &md
}

func CreateDatasource(ctx context.Context, name, namespace, url, authsecret string, insecure bool) (*model.Datasource, error) {
c := client.GetClient()
func CreateDatasource(ctx context.Context, c dynamic.Interface, name, namespace, url, authsecret string, insecure bool) (*model.Datasource, error) {
datasource := v1alpha1.Datasource{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Expand Down Expand Up @@ -94,9 +93,8 @@ func CreateDatasource(ctx context.Context, name, namespace, url, authsecret stri
return ds, nil
}

func DatasourceList(ctx context.Context, name, namespace, labelSelector, fieldSelector string) ([]*model.Datasource, error) {
func DatasourceList(ctx context.Context, c dynamic.Interface, name, namespace, labelSelector, fieldSelector string) ([]*model.Datasource, error) {
dsSchema := schema.GroupVersionResource{Group: v1alpha1.GroupVersion.Group, Version: v1alpha1.GroupVersion.Version, Resource: "datasources"}
c := client.GetClient()
if name != "" {
u, err := c.Resource(dsSchema).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
Expand Down
Loading

0 comments on commit 1c1f8fd

Please sign in to comment.