Skip to content

Commit

Permalink
#6 get docker token in graphql api
Browse files Browse the repository at this point in the history
  • Loading branch information
whymatter authored and whymatter committed May 14, 2020
1 parent 47ae834 commit cdc7643
Show file tree
Hide file tree
Showing 15 changed files with 110 additions and 30 deletions.

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

25 changes: 21 additions & 4 deletions pkg/apiclient/apiclient.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
package apiclient

import (
"context"
"encoding/json"
"fmt"
context2 "github.com/harbourrocks/harbour/pkg/context"
l "github.com/sirupsen/logrus"
"io/ioutil"
"net/http"
)

// Get issues a GET request against the url.
//The response is unmarshalled into response
func Get(url string, response interface{}) (resp *http.Response, err error) {
resp, err = http.Get(url)
func Get(hRock context2.HRock, url string, response interface{}, token string) (resp *http.Response, err error) {
log := hRock.L

req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
if err != nil {
log.
WithField("url", url).WithError(err).
Error("Failed to create request")
return
}

if token != "" {
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
}

resp, err = http.DefaultClient.Do(req)
if err != nil {
l.
WithError(err).
WithField("Method", resp.Request.Method).
WithField("Url", url).
WithField("method", req.Method).
WithField("url", url).
Error("Failed to send request")
return
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/auth/headerauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"strings"
)

func HeaderAuth(request *http.Request, oidcConfig OIDCConfig) (token *oidc.IDToken, err error) {
func HeaderAuth(request *http.Request, oidcConfig OIDCConfig) (token *oidc.IDToken, tokenString string, err error) {
authorizationHeader := request.Header.Get("Authorization")

// trim any spaces
Expand All @@ -24,7 +24,7 @@ func HeaderAuth(request *http.Request, oidcConfig OIDCConfig) (token *oidc.IDTok
l.WithField("Authorization", authorizationHeader).Trace("Authorization Header")

// extract actual token
tokenString := authorizationHeader[7:]
tokenString = authorizationHeader[7:]

// validate token string
token, err = JwtAuth(tokenString, oidcConfig) // error is logged in AuthJwt
Expand Down
7 changes: 4 additions & 3 deletions pkg/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (
)

type HRock struct {
L *logrus.Entry
CtxIdent uuid.UUID
IdToken *auth.IdToken
L *logrus.Entry
CtxIdent uuid.UUID
IdToken *auth.IdToken
IdTokenStr string
}
7 changes: 4 additions & 3 deletions pkg/context/traits.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ func AddHRock(trait HRockTrait) {
rqId := uuid.New()

trait.SetHRock(HRock{
L: l.WithField("rqId", rqId.String()),
CtxIdent: rqId,
IdToken: trait.GetToken(),
L: l.WithField("rqId", rqId.String()),
CtxIdent: rqId,
IdToken: trait.GetToken(),
IdTokenStr: trait.GetTokenStr(),
})
}
23 changes: 22 additions & 1 deletion pkg/harbourgateway/graphql/repository.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package graphql

import (
"errors"
"github.com/graphql-go/graphql"
"github.com/harbourrocks/harbour/pkg/apiclient"
"github.com/harbourrocks/harbour/pkg/context"
"github.com/harbourrocks/harbour/pkg/harbourgateway/configuration"
"github.com/harbourrocks/harbour/pkg/harbourgateway/model"
"github.com/harbourrocks/harbour/pkg/registry/models"
Expand All @@ -23,13 +25,32 @@ var repositoryType = graphql.NewObject(
},
)

func acquireDockerToken(hRock context.HRock, tokenUrl string) (token string, err error) {
var tokenResponse models.DockerTokenResponse
resp, err := apiclient.Get(hRock, tokenUrl, &tokenResponse, hRock.IdTokenStr)
if err != nil {
return // error logged in Get
}

if resp.StatusCode >= 400 {
err = errors.New("request failed")
return // error logged in
}

token = tokenResponse.Token
return
}

func RepositoriesField(options configuration.Options) *graphql.Field {
return &graphql.Field{
Type: repositoryListType,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
hRock := p.Context.Value("hRock").(context.HRock)
dockerToken, err := acquireDockerToken(hRock, options.DockerRegistry.TokenURL("registry", "catalog", "*"))

// query repositories from docker registry
var regRepositories models.Repositories
rsp, err := apiclient.Get(options.DockerRegistry.RepositoriesURL(), &regRepositories)
rsp, err := apiclient.Get(hRock, options.DockerRegistry.RepositoriesURL(), &regRepositories, dockerToken)
if err != nil || rsp.StatusCode >= 300 {
return nil, err
}
Expand Down
7 changes: 6 additions & 1 deletion pkg/harbourgateway/graphql/tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package graphql
import (
"github.com/graphql-go/graphql"
"github.com/harbourrocks/harbour/pkg/apiclient"
"github.com/harbourrocks/harbour/pkg/context"
"github.com/harbourrocks/harbour/pkg/harbourgateway/configuration"
"github.com/harbourrocks/harbour/pkg/harbourgateway/model"
"github.com/harbourrocks/harbour/pkg/registry/models"
Expand Down Expand Up @@ -37,15 +38,19 @@ func TagsField(options configuration.Options) *graphql.Field {
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
hRock := p.Context.Value("hRock").(context.HRock)

// parse query parameter
repositoryQuery, isOK := p.Args["repository"].(string)
if !isOK {
return nil, nil
}

dockerToken, err := acquireDockerToken(hRock, options.DockerRegistry.TokenURL("repository", repositoryQuery, "pull"))

// query tags of repository from docker registry
var regTags models.Tags
_, err := apiclient.Get(options.DockerRegistry.RepositoryTagsURL(repositoryQuery), &regTags)
_, err = apiclient.Get(hRock, options.DockerRegistry.RepositoryTagsURL(repositoryQuery), &regTags, dockerToken)
if err != nil {
return nil, err
}
Expand Down
8 changes: 6 additions & 2 deletions pkg/harbourgateway/handler/graphqlhandler.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package handler

import (
"context"
"encoding/json"
"fmt"
"github.com/graphql-go/graphql"
context2 "github.com/harbourrocks/harbour/pkg/context"
traits2 "github.com/harbourrocks/harbour/pkg/harbourgateway/traits"
"github.com/harbourrocks/harbour/pkg/httphandler/traits"
)

func executeQuery(query string, schema graphql.Schema) *graphql.Result {
func executeQuery(query string, schema graphql.Schema, hRock context2.HRock) *graphql.Result {
result := graphql.Do(graphql.Params{
Schema: schema,
RequestString: query,
Context: context.WithValue(context.Background(), "hRock", hRock),
})

if len(result.Errors) > 0 {
Expand All @@ -26,13 +29,14 @@ type GraphQLModel struct {
traits.HttpModel
traits.IdTokenModel
traits2.GraphQLModel
context2.HRockModel
}

func (h GraphQLModel) Handle() (err error) {
r := h.GetRequest()
w := h.GetResponse()
s := h.GetSchema()

result := executeQuery(r.URL.Query().Get("query"), s)
result := executeQuery(r.URL.Query().Get("query"), s, h.GetHRock())
return json.NewEncoder(w).Encode(result)
}
3 changes: 3 additions & 0 deletions pkg/harbourgateway/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package server
import (
"fmt"
"github.com/graphql-go/graphql"
"github.com/harbourrocks/harbour/pkg/context"
"github.com/harbourrocks/harbour/pkg/harbourgateway/configuration"
graphql2 "github.com/harbourrocks/harbour/pkg/harbourgateway/graphql"
"github.com/harbourrocks/harbour/pkg/harbourgateway/handler"
Expand Down Expand Up @@ -38,6 +39,8 @@ func RunGatewayServer(o *configuration.Options) error {
traits.AddHttp(&model, r, w, o.OIDCConfig)
traits.AddIdToken(&model)
traits2.AddGraphQL(&model, schema)
context.AddHRock(&model)

if err := httphandler.ForceAuthenticated(&model); err == nil {
_ = model.Handle()
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/harbouriam/handler/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (a AuthModel) Handle() {
r := a.GetRequest()
w := a.GetResponse()

_, err := auth.HeaderAuth(r, a.GetOidcConfig())
_, _, err := auth.HeaderAuth(r, a.GetOidcConfig())

if err != nil {
w.WriteHeader(http.StatusUnauthorized)
Expand Down
14 changes: 4 additions & 10 deletions pkg/harbouriam/handler/dockertoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,14 @@ import (
redis2 "github.com/harbourrocks/harbour/pkg/harbouriam/redis"
"github.com/harbourrocks/harbour/pkg/httphandler/traits"
"github.com/harbourrocks/harbour/pkg/redisconfig"
"github.com/harbourrocks/harbour/pkg/registry/models"
l "github.com/sirupsen/logrus"
"golang.org/x/crypto/bcrypt"
"net/http"
"strings"
"time"
)

// DockerTokenResponse is the response model of the token request
type DockerTokenResponse struct {
Token string `json:"token"`
AccessToken string `json:"access_token"`
ExpiresIn int64 `json:"expires_in"`
IssuedAt string `json:"issued_at"`
}

// DockerScope is part of the claims and represents allowed actions
type DockerScope struct {
Type string `json:"type"`
Expand All @@ -45,6 +38,7 @@ type DockerTokenModel struct {
var supportedScopes = map[string]struct{}{
"pull": {},
"push": {},
"*": {},
}

func (h DockerTokenModel) Handle() {
Expand Down Expand Up @@ -137,7 +131,7 @@ func (h DockerTokenModel) Handle() {
log.WithField("token", signedToken).Trace("Signed JWT token")

w.WriteHeader(http.StatusOK)
_ = h.WriteResponse(DockerTokenResponse{
_ = h.WriteResponse(models.DockerTokenResponse{
Token: signedToken,
AccessToken: signedToken,
ExpiresIn: int64(h.GetIamConfig().Docker.TokenLifetime.Seconds()),
Expand All @@ -155,7 +149,7 @@ func dockerScopeFromString(scope string) DockerScope {
resourceName = split[1]
actions = split[2]
} else {
resourceName = split[1] + split[2]
resourceName = split[1] + ":" + split[2]
actions = split[3]
}

Expand Down
16 changes: 14 additions & 2 deletions pkg/httphandler/traits/idtoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import (
type IdTokenTrait interface {
GetToken() *auth.IdToken
SetToken(t auth.IdToken)
GetTokenStr() string
SetTokenStr(string)
HttpTrait
}

// IdTokenModel holds the idToken
type IdTokenModel struct {
idToken *auth.IdToken
idToken *auth.IdToken
idTokenStr string
}

func (m IdTokenModel) GetToken() *auth.IdToken {
Expand All @@ -24,10 +27,18 @@ func (m *IdTokenModel) SetToken(t auth.IdToken) {
m.idToken = &t
}

func (m IdTokenModel) GetTokenStr() string {
return m.idTokenStr
}

func (m *IdTokenModel) SetTokenStr(t string) {
m.idTokenStr = t
}

func AddIdToken(trait IdTokenTrait) {
r := trait.GetRequest()

token, err := auth.HeaderAuth(r, trait.GetOidcConfig())
token, tokenStr, err := auth.HeaderAuth(r, trait.GetOidcConfig())
if err != nil {
return
}
Expand All @@ -38,4 +49,5 @@ func AddIdToken(trait IdTokenTrait) {
}

trait.SetToken(idToken)
trait.SetTokenStr(tokenStr)
}
6 changes: 5 additions & 1 deletion pkg/registry/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (

// RegistryConfig is the required minimum docker registry connections
type RegistryConfig struct {
Url string
Url string
IAMUrl string
}

// ParseViperConfig tries to map a viper configuration
Expand All @@ -17,5 +18,8 @@ func ParseViperConfig() RegistryConfig {
s.Url = viper.GetString("REGISTRY_URL")
s.Url = strings.Trim(s.Url, " /")

s.IAMUrl = viper.GetString("IAM_BASE_URL")
s.IAMUrl = strings.Trim(s.IAMUrl, " /")

return s
}
9 changes: 9 additions & 0 deletions pkg/registry/models/tokenResponse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package models

// DockerTokenResponse is the response model of the token request
type DockerTokenResponse struct {
Token string `json:"token"`
AccessToken string `json:"access_token"`
ExpiresIn int64 `json:"expires_in"`
IssuedAt string `json:"issued_at"`
}
8 changes: 8 additions & 0 deletions pkg/registry/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ func (c RegistryConfig) RepositoryTagsURL(repositoryName string) string {
return combine(c.Url, RepositoryTagsURL(repositoryName))
}

func TokenURL() string {
return "/docker/auth/token"
}

func (c RegistryConfig) TokenURL(type_, name, action string) string {
return fmt.Sprintf("%s?service=%s&scope=%s:%s:%s", combine(c.IAMUrl, TokenURL()), c.Url, type_, name, action)
}

func combine(host, path string) string {
return fmt.Sprintf("%s%s", host, path)
}

0 comments on commit cdc7643

Please sign in to comment.