This repository has been archived by the owner on Mar 11, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Renaming some `Client` into `Service` * Moving generate auth client code in `auth/client` * Adapted some tests (using `require.Error(t, err)` and `require.NoError(t, err)` when applicable) * Rename 'TokenResolver' and 'TenantResolver' * Renaming to `ResolveToken` and `ResolveTenant` respectively, so their name is a verb, since these are functions. * More 'TenantResolver' -> 'ResolveTenant' vars renaming * Rename func 'ClusterResolver' to 'ResolveCluster' * Dispatch user, cluster and token in their own packages and simplify names * Moving OpenShift Config init in openshift pkg and removing some unnecessary pointers * Move some code from 'keycloak' pkg to 'tenant' * Move public keys into token, fetch using auth client. Remove pointers Signed-off-by: Xavier Coulon <[email protected]> Fixes openshiftio/openshift.io#2266
- Loading branch information
1 parent
c25ee74
commit 5419db7
Showing
49 changed files
with
1,336 additions
and
1,142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package auth | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
|
||
authclient "github.com/fabric8-services/fabric8-tenant/auth/client" | ||
goaclient "github.com/goadesign/goa/client" | ||
) | ||
|
||
type clientImpl struct { | ||
client authclient.Client | ||
} | ||
|
||
// NewClient returns a new auth client | ||
func NewClient(authURL string, options ...ClientOption) (*authclient.Client, error) { | ||
u, err := url.Parse(authURL) | ||
if err != nil { | ||
return nil, err | ||
} | ||
c := doerConfig{ | ||
httpClient: http.DefaultClient, | ||
} | ||
// apply options | ||
for _, opt := range options { | ||
opt(&c) | ||
} | ||
client := authclient.New(newDoer(c)) | ||
client.Host = u.Host | ||
client.Scheme = u.Scheme | ||
return client, nil | ||
} | ||
|
||
// ClientOption a function to customize the auth client | ||
type ClientOption func(*doerConfig) | ||
|
||
type doerConfig struct { | ||
httpClient *http.Client | ||
token *string | ||
} | ||
|
||
// WithHTTPClient an option to specify the http client to use | ||
func WithHTTPClient(httpClient *http.Client) ClientOption { | ||
return func(c *doerConfig) { | ||
c.httpClient = httpClient | ||
} | ||
} | ||
|
||
// WithToken an option to specify the token to use | ||
func WithToken(token string) ClientOption { | ||
return func(c *doerConfig) { | ||
c.token = &token | ||
} | ||
} | ||
|
||
type doer struct { | ||
target goaclient.Doer | ||
token *string | ||
} | ||
|
||
// Doer adds Authorization to all Requests, un related to goa design | ||
func newDoer(config doerConfig) goaclient.Doer { | ||
return &doer{ | ||
target: goaclient.HTTPClientDoer(config.httpClient), | ||
token: config.token, | ||
} | ||
} | ||
|
||
func (d *doer) Do(ctx context.Context, req *http.Request) (*http.Response, error) { | ||
if d.token != nil { | ||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *d.token)) | ||
} | ||
return d.target.Do(ctx, req) | ||
} | ||
|
||
// ValidateError function when given client and response checks if the | ||
// response has any errors by also looking at the status code | ||
func ValidateError(c *authclient.Client, res *http.Response) error { | ||
if res.StatusCode == http.StatusNotFound { | ||
return fmt.Errorf("404 Not found") | ||
} else if res.StatusCode != http.StatusOK { | ||
goaErr, err := c.DecodeJSONAPIErrors(res) | ||
if err != nil { | ||
return err | ||
} | ||
if len(goaErr.Errors) != 0 { | ||
var output string | ||
for _, error := range goaErr.Errors { | ||
output += fmt.Sprintf("%s: %s %s, %s\n", *error.Title, *error.Status, *error.Code, error.Detail) | ||
} | ||
return fmt.Errorf("%s", output) | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package cluster | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
) | ||
|
||
// Resolve a func to resolve a cluster | ||
type Resolve func(ctx context.Context, target string) (*Cluster, error) | ||
|
||
// NewResolve returns a new Cluster | ||
func NewResolve(clusters []*Cluster) Resolve { | ||
return func(ctx context.Context, target string) (*Cluster, error) { | ||
for _, cluster := range clusters { | ||
if cleanURL(target) == cleanURL(cluster.APIURL) { | ||
return cluster, nil | ||
} | ||
} | ||
return nil, fmt.Errorf("unable to resolve cluster") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package cluster | ||
|
||
import ( | ||
"context" | ||
"io/ioutil" | ||
"strings" | ||
|
||
"github.com/fabric8-services/fabric8-tenant/auth" | ||
authclient "github.com/fabric8-services/fabric8-tenant/auth/client" | ||
"github.com/fabric8-services/fabric8-tenant/token" | ||
goaclient "github.com/goadesign/goa/client" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
// Cluster a cluster | ||
type Cluster struct { | ||
APIURL string | ||
ConsoleURL string | ||
MetricsURL string | ||
AppDNS string | ||
|
||
User string | ||
Token string | ||
} | ||
|
||
func cleanURL(url string) string { | ||
if !strings.HasSuffix(url, "/") { | ||
return url + "/" | ||
} | ||
return url | ||
} | ||
|
||
// Service the interface for the cluster service | ||
type Service interface { | ||
GetClusters(context.Context) ([]*Cluster, error) | ||
} | ||
|
||
// NewService creates a Resolver that rely on the Auth service to retrieve tokens | ||
func NewService(authURL string, serviceToken string, resolveToken token.Resolve, decode token.Decode, options ...auth.ClientOption) Service { | ||
return &clusterService{authURL: authURL, serviceToken: serviceToken, resolveToken: resolveToken, decode: decode, clientOptions: options} | ||
} | ||
|
||
type clusterService struct { | ||
authURL string | ||
clientOptions []auth.ClientOption | ||
serviceToken string | ||
resolveToken token.Resolve | ||
decode token.Decode | ||
} | ||
|
||
func (s *clusterService) GetClusters(ctx context.Context) ([]*Cluster, error) { | ||
client, err := auth.NewClient(s.authURL, s.clientOptions...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
client.SetJWTSigner( | ||
&goaclient.JWTSigner{ | ||
TokenSource: &goaclient.StaticTokenSource{ | ||
StaticToken: &goaclient.StaticToken{ | ||
Value: s.serviceToken, | ||
Type: "Bearer"}}}) | ||
|
||
res, err := client.ShowClusters(ctx, authclient.ShowClustersPath()) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "error while doing the request") | ||
} | ||
defer func() { | ||
ioutil.ReadAll(res.Body) | ||
res.Body.Close() | ||
}() | ||
|
||
validationerror := auth.ValidateError(client, res) | ||
if validationerror != nil { | ||
return nil, errors.Wrapf(validationerror, "error from server %q", s.authURL) | ||
} | ||
|
||
clusters, err := client.DecodeClusterList(res) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "error from server %q", s.authURL) | ||
} | ||
|
||
var cls []*Cluster | ||
for _, cluster := range clusters.Data { | ||
clusterUser, clusterToken, err := s.resolveToken(ctx, cluster.APIURL, s.serviceToken, s.decode) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "Unable to resolve token for cluster %v", cluster.APIURL) | ||
} | ||
cls = append(cls, &Cluster{ | ||
APIURL: cluster.APIURL, | ||
AppDNS: cluster.AppDNS, | ||
ConsoleURL: cluster.ConsoleURL, | ||
MetricsURL: cluster.MetricsURL, | ||
User: clusterUser, | ||
Token: clusterToken, | ||
}) | ||
} | ||
return cls, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.