Skip to content
This repository has been archived by the owner on Mar 11, 2021. It is now read-only.

Commit

Permalink
Support notifications to users with private email ( using fabric8 ser…
Browse files Browse the repository at this point in the history
…vice account ) (#54)

* start using latest auth
* get SA token
* inject service account token on startup
* remove the WITs user api call
* add custom code for user api client
  • Loading branch information
sbose78 authored Jan 24, 2018
1 parent b07a204 commit 581c5fb
Show file tree
Hide file tree
Showing 20 changed files with 403 additions and 149 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ tool/cli/
migration/sqlbindata.go
template/bindata.go
wit/api
auth/api

# Ignore artifacts directory
bin/
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ clean-generated:
-rm -rf ./swagger/
-rm -f ./migration/sqlbindata.go
-rm -f ./template/bindata.go
-rm -rf auth/api

CLEAN_TARGETS += clean-vendor
.PHONY: clean-vendor
Expand All @@ -211,6 +212,7 @@ app/controllers.go: $(DESIGNS) $(GOAGEN_BIN) $(VENDOR_DIR)
$(GOAGEN_BIN) app -d ${PACKAGE_NAME}/${DESIGN_DIR}
$(GOAGEN_BIN) controller -d ${PACKAGE_NAME}/${DESIGN_DIR} -o controller/ --pkg controller --app-pkg app
$(GOAGEN_BIN) client -d github.com/fabric8-services/fabric8-wit/design --notool --pkg api -o wit
$(GOAGEN_BIN) client -d github.com/fabric8-services/fabric8-auth/design --notool --pkg api -o auth

.PHONY: migrate-database
## Compiles the server and runs the database migration with it
Expand Down
50 changes: 50 additions & 0 deletions auth/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package auth

import (
"context"
"net/http"
"net/url"

"fmt"

"github.com/fabric8-services/fabric8-notification/auth/api"
"github.com/fabric8-services/fabric8-wit/goasupport"
goaclient "github.com/goadesign/goa/client"
"github.com/goadesign/goa/uuid"
"github.com/gregjones/httpcache"
)

func NewCachedClient(hostURL string) (*api.Client, error) {

u, err := url.Parse(hostURL)
if err != nil {
return nil, err
}

tp := httpcache.NewMemoryCacheTransport()
client := http.Client{Transport: tp}

c := api.New(goaclient.HTTPClientDoer(&client))
c.Host = u.Host
c.Scheme = u.Scheme
return c, nil
}

func GetSpaceCollaborators(ctx context.Context, client *api.Client, spaceID uuid.UUID) (*api.UserList, error) {
pageLimit := 100
pageOffset := "0"
resp, err := client.ListCollaborators(goasupport.ForwardContextRequestID(ctx), api.ListCollaboratorsPath(spaceID), &pageLimit, &pageOffset, nil, nil)
if err != nil {
return nil, err
}
if resp != nil {
defer resp.Body.Close()
} else {
return nil, fmt.Errorf("failed to make request to get list of collaborators")
}

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("non %v status code for %v, returned %v", http.StatusOK, "GET collaborators", resp.StatusCode)
}
return client.DecodeUserList(resp)
}
36 changes: 36 additions & 0 deletions auth/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package auth_test

import (
"context"
"testing"

"github.com/fabric8-services/fabric8-notification/auth"
"github.com/fabric8-services/fabric8-notification/auth/api"
"github.com/goadesign/goa/uuid"
"github.com/stretchr/testify/assert"
)

const (
openshiftIOAPI = "http://auth.openshift.io"
)

func createClient(t *testing.T) *api.Client {
c, err := auth.NewCachedClient(openshiftIOAPI)
if err != nil {
t.Fatal(err)
}
return c
}

func TestSpaceCollaborators(t *testing.T) {

c := createClient(t)
id, _ := uuid.FromString("020f756e-b51a-4b43-b113-45cec16b9ce9")

u, err := auth.GetSpaceCollaborators(context.Background(), c, id)
if err != nil {
t.Fatal(err)
}

assert.True(t, len(u.Data) > 10)
}
77 changes: 77 additions & 0 deletions auth/user_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package auth

import (
"context"
"net/http"
"net/url"

"fmt"

"github.com/fabric8-services/fabric8-notification/auth/api"
"github.com/fabric8-services/fabric8-wit/goasupport"
"github.com/goadesign/goa/uuid"
)

/*
Took out the auth api client code relevant to User API in order to add a custom
client.JWTSigner.Sign(req) before the http request is made.
This wasn't present in the auto-generated client for GET /api/users/ID
because we don't set "a.Security("jwt")" in auth's design/account.go
for the `Show Users` action.
*/

func GetUser(ctx context.Context, client *api.Client, uID uuid.UUID) (*api.User, error) {
resp, err := showUsers(goasupport.ForwardContextRequestID(ctx), client, api.ShowUsersPath(uID.String()), nil, nil)
if resp != nil {
defer resp.Body.Close()
}

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("non %v status code for %v, returned %v", http.StatusOK, "GET user", resp.StatusCode)
}

if err != nil {
return nil, err
}
return client.DecodeUser(resp)
}

// retrieve user for the given ID.
func showUsers(ctx context.Context, client *api.Client, path string, ifModifiedSince *string, ifNoneMatch *string) (*http.Response, error) {
req, err := newShowUsersRequest(ctx, client, path, ifModifiedSince, ifNoneMatch)
if err != nil {
return nil, err
}
return client.Do(ctx, req)
}

// newShowUsersRequest create the request corresponding to the show action endpoint of the users resource.
func newShowUsersRequest(ctx context.Context, client *api.Client, path string, ifModifiedSince *string, ifNoneMatch *string) (*http.Request, error) {
scheme := client.Scheme
if scheme == "" {
scheme = "http"
}
u := url.URL{Host: client.Host, Scheme: scheme, Path: path}
req, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
return nil, err
}
header := req.Header
if ifModifiedSince != nil {

header.Set("If-Modified-Since", *ifModifiedSince)
}
if ifNoneMatch != nil {

header.Set("If-None-Match", *ifNoneMatch)
}

// This wasn't present in the auto-generated client for GET /api/users/ID
// because we don't set "a.Security("jwt")" in auth's design/account.go
// for the `Show Users` action.
if client.JWTSigner != nil {
client.JWTSigner.Sign(req)
}
return req, nil
}
2 changes: 1 addition & 1 deletion collector/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"context"
"fmt"

"github.com/fabric8-services/fabric8-notification/wit/api"
"github.com/fabric8-services/fabric8-notification/auth/api"
"github.com/goadesign/goa/uuid"
)

Expand Down
4 changes: 2 additions & 2 deletions collector/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (
)

func TestUser(t *testing.T) {
c := createClient(t)
_, authClient := createClient(t)
uID, _ := uuid.FromString("3383826c-51e4-401b-9ccd-b898f7e2397d")
users, vars, err := collector.User(context.Background(), c, uID)
users, vars, err := collector.User(context.Background(), authClient, uID)

assert.Nil(t, err)
assert.Len(t, users, 1)
Expand Down
42 changes: 22 additions & 20 deletions collector/workitem.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"

jwt "github.com/dgrijalva/jwt-go"
"github.com/fabric8-services/fabric8-notification/auth"
authapi "github.com/fabric8-services/fabric8-notification/auth/api"
"github.com/fabric8-services/fabric8-notification/configuration"
"github.com/fabric8-services/fabric8-notification/wit"
"github.com/fabric8-services/fabric8-notification/wit/api"
Expand All @@ -13,23 +15,23 @@ import (
"github.com/goadesign/goa/uuid"
)

func NewCommentResolver(c *api.Client) ReceiverResolver {
func NewCommentResolver(authclient *authapi.Client, c *api.Client) ReceiverResolver {
return func(ctx context.Context, id string) ([]Receiver, map[string]interface{}, error) {
cID, err := uuid.FromString(id)
if err != nil {
return []Receiver{}, nil, fmt.Errorf("unable to lookup comment based on id %v", id)
}
return Comment(ctx, c, cID)
return Comment(ctx, authclient, c, cID)
}
}

func NewWorkItemResolver(c *api.Client) ReceiverResolver {
func NewWorkItemResolver(authclient *authapi.Client, c *api.Client) ReceiverResolver {
return func(ctx context.Context, id string) ([]Receiver, map[string]interface{}, error) {
wID, err := uuid.FromString(id)
if err != nil {
return []Receiver{}, nil, fmt.Errorf("unable to lookup Workitem based on id %v", id)
}
return WorkItem(ctx, c, wID)
return WorkItem(ctx, authclient, c, wID)
}
}

Expand All @@ -45,7 +47,7 @@ func ConfiguredVars(config *configuration.Data, resolver ReceiverResolver) Recei
}
}

func Comment(ctx context.Context, c *api.Client, cID uuid.UUID) ([]Receiver, map[string]interface{}, error) {
func Comment(ctx context.Context, authClient *authapi.Client, c *api.Client, cID uuid.UUID) ([]Receiver, map[string]interface{}, error) {
var values = map[string]interface{}{}
var errors []error
var users []uuid.UUID
Expand All @@ -57,7 +59,7 @@ func Comment(ctx context.Context, c *api.Client, cID uuid.UUID) ([]Receiver, map
values["comment"] = comment
users = append(users, collectCommentUsers(comment)...)

commentOwner, err := wit.GetUser(ctx, c, *comment.Data.Relationships.CreatedBy.Data.ID)
commentOwner, err := auth.GetUser(ctx, authClient, *comment.Data.Relationships.CreatedBy.Data.ID)
if err != nil {
errors = append(errors, err)
}
Expand All @@ -73,7 +75,7 @@ func Comment(ctx context.Context, c *api.Client, cID uuid.UUID) ([]Receiver, map
values["workitem"] = wi

ownerID, _ := uuid.FromString(*wi.Data.Relationships.Creator.Data.ID)
workitemOwner, err := wit.GetUser(ctx, c, ownerID)
workitemOwner, err := auth.GetUser(ctx, authClient, ownerID)
if err != nil {
errors = append(errors, err)
}
Expand All @@ -100,7 +102,7 @@ func Comment(ctx context.Context, c *api.Client, cID uuid.UUID) ([]Receiver, map
users = append(users, collectSpaceUsers(s)...)
values["space"] = s

spaceOwner, err := wit.GetUser(ctx, c, *s.Data.Relationships.OwnedBy.Data.ID)
spaceOwner, err := auth.GetUser(ctx, authClient, *s.Data.Relationships.OwnedBy.Data.ID)
if err != nil {
errors = append(errors, err)
}
Expand All @@ -114,20 +116,20 @@ func Comment(ctx context.Context, c *api.Client, cID uuid.UUID) ([]Receiver, map

actorID, err := getActorID(ctx)
if err == nil {
actor, err := wit.GetUser(ctx, c, actorID)
actor, err := auth.GetUser(ctx, authClient, actorID)
if err != nil {
errors = append(errors, err)
}
values["actor"] = actor
}

sc, err := wit.GetSpaceCollaborators(ctx, c, spaceID)
sc, err := auth.GetSpaceCollaborators(ctx, authClient, spaceID)
if err != nil {
errors = append(errors, err)
}
users = append(users, collectSpaceCollaboratorUsers(sc)...)

resolved, err := resolveAllUsers(ctx, c, SliceUniq(users), sc.Data)
resolved, err := resolveAllUsers(ctx, authClient, SliceUniq(users), sc.Data)
if err != nil {
errors = append(errors, err)
}
Expand All @@ -140,7 +142,7 @@ func Comment(ctx context.Context, c *api.Client, cID uuid.UUID) ([]Receiver, map
return resolved, values, nil
}

func WorkItem(ctx context.Context, c *api.Client, wiID uuid.UUID) ([]Receiver, map[string]interface{}, error) {
func WorkItem(ctx context.Context, authclient *authapi.Client, c *api.Client, wiID uuid.UUID) ([]Receiver, map[string]interface{}, error) {
var values = map[string]interface{}{}
var errors []error
var users []uuid.UUID
Expand All @@ -153,7 +155,7 @@ func WorkItem(ctx context.Context, c *api.Client, wiID uuid.UUID) ([]Receiver, m
users = append(users, collectWorkItemUsers(wi)...)

ownerID, _ := uuid.FromString(*wi.Data.Relationships.Creator.Data.ID)
workitemOwner, err := wit.GetUser(ctx, c, ownerID)
workitemOwner, err := auth.GetUser(ctx, authclient, ownerID)
if err != nil {
errors = append(errors, err)
}
Expand All @@ -180,7 +182,7 @@ func WorkItem(ctx context.Context, c *api.Client, wiID uuid.UUID) ([]Receiver, m
values["space"] = s
users = append(users, collectSpaceUsers(s)...)

spaceOwner, err := wit.GetUser(ctx, c, *s.Data.Relationships.OwnedBy.Data.ID)
spaceOwner, err := auth.GetUser(ctx, authclient, *s.Data.Relationships.OwnedBy.Data.ID)
if err != nil {
errors = append(errors, err)
}
Expand All @@ -194,20 +196,20 @@ func WorkItem(ctx context.Context, c *api.Client, wiID uuid.UUID) ([]Receiver, m

actorID, err := getActorID(ctx)
if err == nil {
actor, err := wit.GetUser(ctx, c, actorID)
actor, err := auth.GetUser(ctx, authclient, actorID)
if err != nil {
errors = append(errors, err)
}
values["actor"] = actor
}

sc, err := wit.GetSpaceCollaborators(ctx, c, spaceID)
sc, err := auth.GetSpaceCollaborators(ctx, authclient, spaceID)
if err != nil {
errors = append(errors, err)
}
users = append(users, collectSpaceCollaboratorUsers(sc)...)

resolved, err := resolveAllUsers(ctx, c, SliceUniq(users), sc.Data)
resolved, err := resolveAllUsers(ctx, authclient, SliceUniq(users), sc.Data)
if err != nil {
errors = append(errors, err)
}
Expand All @@ -220,7 +222,7 @@ func WorkItem(ctx context.Context, c *api.Client, wiID uuid.UUID) ([]Receiver, m
return resolved, values, nil
}

func resolveAllUsers(ctx context.Context, c *api.Client, users []uuid.UUID, collaborators []*api.UserData) ([]Receiver, error) {
func resolveAllUsers(ctx context.Context, c *authapi.Client, users []uuid.UUID, collaborators []*authapi.UserData) ([]Receiver, error) {
var resolved []Receiver

for _, u := range users {
Expand All @@ -238,7 +240,7 @@ func resolveAllUsers(ctx context.Context, c *api.Client, users []uuid.UUID, coll
}
}
if !found {
usr, err := wit.GetUser(ctx, c, u)
usr, err := auth.GetUser(ctx, c, u)
if err == nil {
if usr.Data.Attributes.Email != nil {
user := Receiver{EMail: *usr.Data.Attributes.Email}
Expand All @@ -260,7 +262,7 @@ func resolveAllUsers(ctx context.Context, c *api.Client, users []uuid.UUID, coll
return resolved, nil
}

func collectSpaceCollaboratorUsers(cl *api.UserList) []uuid.UUID {
func collectSpaceCollaboratorUsers(cl *authapi.UserList) []uuid.UUID {
var users []uuid.UUID
for _, c := range cl.Data {
cID, err := uuid.FromString(*c.ID)
Expand Down
Loading

0 comments on commit 581c5fb

Please sign in to comment.