Skip to content

Commit

Permalink
Add consent management endpoints to DAM
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 319835873
Change-Id: Ib63456a8c84f67cb4d57d5654be7ed7d74453de3
  • Loading branch information
chaopeng authored and copybara-github committed Jul 6, 2020
1 parent 6da25d7 commit b5aae7b
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 36 deletions.
59 changes: 49 additions & 10 deletions lib/consentsapi/consents.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"net/http"
"regexp"
"sort"
"time"

"github.com/gorilla/mux" /* copybara-comment */
"google.golang.org/grpc/codes" /* copybara-comment */
Expand All @@ -40,14 +41,14 @@ const (
)

var (
uuidRE = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
uuidRE = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
timeNow = time.Now
)

// Service contains store and funcs to access data.
type Service struct {
Store storage.Store
FindRememberedConsentsByUser func(store storage.Store, subject, realm, clientName string, offset, pageSize int, tx storage.Tx) (map[string]*storepb.RememberedConsentPreference, error)
Clients func(tx storage.Tx) (map[string]*cpb.Client, error)
Store storage.Store
Clients func(tx storage.Tx) (map[string]*cpb.Client, error)
}

// ListConsentsFactory http handler for "/identity/v1alpha/{realm}/users/{user}/consents"
Expand Down Expand Up @@ -75,7 +76,7 @@ func (s *listConsentsHandler) Setup(r *http.Request, tx storage.Tx) (int, error)
userID := mux.Vars(r)["user"]
realm := mux.Vars(r)["realm"]

rcs, err := s.s.FindRememberedConsentsByUser(s.s.Store, userID, realm, "", 0, maxRememberedConsent, tx)
rcs, err := findRememberedConsentsByUser(s.s.Store, userID, realm, "", 0, maxRememberedConsent, tx)
if err != nil {
return httputils.FromError(err), err
}
Expand Down Expand Up @@ -151,18 +152,22 @@ func toConsentVisas(list []*storepb.RememberedConsentPreference_Visa) []*cspb.Co
}

// DeleteConsentFactory http handler for "/identity/v1alpha/{realm}/users/{user}/consents/{consent_id}"
func DeleteConsentFactory(serv *Service, consentPath string) *handlerfactory.Options {
return &handlerfactory.Options{
func DeleteConsentFactory(serv *Service, consentPath string, consentIDUseUUID bool) *handlerfactory.Options {
opts := &handlerfactory.Options{
TypeName: "consent",
PathPrefix: consentPath,
HasNamedIdentifiers: false,
NameChecker: map[string]*regexp.Regexp{
"consent_id": uuidRE,
},
Service: func() handlerfactory.Service {
return &deleteConsentHandler{s: serv}
},
}

if consentIDUseUUID {
opts.NameChecker = map[string]*regexp.Regexp{
"consent_id": uuidRE,
}
}
return opts
}

type deleteConsentHandler struct {
Expand Down Expand Up @@ -191,3 +196,37 @@ func (s *deleteConsentHandler) Save(r *http.Request, tx storage.Tx, name string,
}
return nil
}

// findRememberedConsentsByUser returns all RememberedConsents of user of client.
func findRememberedConsentsByUser(store storage.Store, subject, realm, clientName string, offset, pageSize int, tx storage.Tx) (map[string]*storepb.RememberedConsentPreference, error) {
content := make(map[string]map[string]proto.Message)
count, err := store.MultiReadTx(storage.RememberedConsentDatatype, realm, subject, nil, offset, pageSize, content, &storepb.RememberedConsentPreference{}, tx)
if err != nil {
return nil, status.Errorf(codes.Unavailable, "findRememberedConsentsByUser MultiReadTx() failed: %v", err)
}

res := map[string]*storepb.RememberedConsentPreference{}
if count == 0 {
return res, nil
}

now := timeNow().Unix()
for k, v := range content[subject] {
rcp, ok := v.(*storepb.RememberedConsentPreference)
if !ok {
return nil, status.Errorf(codes.Internal, "findRememberedConsentsByUser obj type incorrect: user=%s, id=%s", subject, k)
}
// remove expired items
if rcp.ExpireTime.Seconds < now {
continue
}
// filter for clientName
if len(clientName) > 0 && rcp.ClientName != clientName {
continue
}

res[k] = rcp
}

return res, nil
}
44 changes: 24 additions & 20 deletions lib/consentsapi/consents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,7 @@ import (
)

func TestListConsents(t *testing.T) {
stub := &stub{}
store := fakestore.New()

handler := handlerfactory.MakeHandler(store, ListConsentsFactory(&Service{
Store: store,
FindRememberedConsentsByUser: stub.findRememberedConsentsByUser,
Clients: stub.clients,
}, "/identity/v1alpha/{realm}/users/{user}/consents"))

timeNow = func() time.Time { return time.Time{} }
time1 := timeutil.TimestampProto(time.Time{}.Add(100 * time.Hour))
time2 := timeutil.TimestampProto(time.Time{}.Add(200 * time.Hour))
consents := map[string]*storepb.RememberedConsentPreference{
Expand Down Expand Up @@ -213,10 +205,19 @@ func TestListConsents(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
stub.consents = tc.consents
stub := &stub{}
store := fakestore.New()

handler := handlerfactory.MakeHandler(store, ListConsentsFactory(&Service{
Store: store,
Clients: stub.clients,
}, "/identity/v1alpha/{realm}/users/{user}/consents"))

stub.clis = tc.clients

r := httptest.NewRequest(http.MethodGet, "/identity/v1alpha/masterusers/user1/consents", nil)
storeRememberedConsents(t, store, "user1", storage.DefaultRealm, tc.consents)

r := httptest.NewRequest(http.MethodGet, "/identity/v1alpha/master/user1/consents", nil)
r = mux.SetURLVars(r, map[string]string{
"user": "user1",
"realm": "master",
Expand Down Expand Up @@ -244,10 +245,9 @@ func TestDeleteConsent(t *testing.T) {

store := fakestore.New()
handler := handlerfactory.MakeHandler(store, DeleteConsentFactory(&Service{
Store: store,
FindRememberedConsentsByUser: stub.findRememberedConsentsByUser,
Clients: stub.clients,
}, "/identity/v1alpha/{realm}/users/{user}/consents/{consent_id}"))
Store: store,
Clients: stub.clients,
}, "/identity/v1alpha/{realm}/users/{user}/consents/{consent_id}", true))

consentID := "00000000-0000-0000-0000-000000000001"
invalidConsentID := "00000000-0000-0000-0000-000000000000"
Expand Down Expand Up @@ -292,13 +292,17 @@ func TestDeleteConsent(t *testing.T) {
}
}

type stub struct {
consents map[string]*storepb.RememberedConsentPreference
clis map[string]*cpb.Client
func storeRememberedConsents(t *testing.T, store storage.Store, subject, realm string, consents map[string]*storepb.RememberedConsentPreference) {
t.Helper()
for id, rcp := range consents {
if err := store.Write(storage.RememberedConsentDatatype, realm, subject, id, storage.LatestRev, rcp, nil); err != nil {
t.Fatalf("store RememberedConsentData failed: %v", err)
}
}
}

func (s *stub) findRememberedConsentsByUser(store storage.Store, subject, realm, clientName string, offset, pageSize int, tx storage.Tx) (map[string]*storepb.RememberedConsentPreference, error) {
return s.consents, nil
type stub struct {
clis map[string]*cpb.Client
}

func (s *stub) clients(tx storage.Tx) (map[string]*cpb.Client, error) {
Expand Down
9 changes: 7 additions & 2 deletions lib/dam/dam.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ type Options struct {
// Store: data storage and configuration storage
Store storage.Store
// Warehouse: resource token creator service
Warehouse clouds.ResourceTokenCreator
Warehouse clouds.ResourceTokenCreator
// AWSClient: a client for interacting with the AWS API
AWSClient aws.APIClient
AWSClient aws.APIClient
ServiceAccountManager *saw.AccountWarehouse
// Logger: audit log logger
Logger *logging.Client
Expand Down Expand Up @@ -1538,6 +1538,11 @@ func registerHandlers(r *mux.Router, s *Service) {
r.HandleFunc(fakeTokenPath, auth.MustWithAuth(faketokensapi.NewTokensHandler(s.tokens).DeleteToken, s.checker, auth.RequireUserToken)).Methods(http.MethodDelete)

// consents service endpoints
consentService := s.consentService()
r.HandleFunc(listConsentPath, auth.MustWithAuth(handlerfactory.MakeHandler(s.GetStore(), consentsapi.ListConsentsFactory(consentService, listConsentPath)), s.checker, auth.RequireUserToken)).Methods(http.MethodGet)
r.HandleFunc(deleteConsentPath, auth.MustWithAuth(handlerfactory.MakeHandler(s.GetStore(), consentsapi.DeleteConsentFactory(consentService, deleteConsentPath, false)), s.checker, auth.RequireUserToken)).Methods(http.MethodDelete)

// TODO: delete the mocked endpoints when complete.
consents := &consentsapi.StubConsents{Consent: consentsapi.FakeConsent}
r.HandleFunc(consentsPath, auth.MustWithAuth(consentsapi.NewMockConsentsHandler(consents).ListConsents, s.checker, auth.RequireUserToken)).Methods(http.MethodGet)
r.HandleFunc(consentPath, auth.MustWithAuth(consentsapi.NewMockConsentsHandler(consents).DeleteConsent, s.checker, auth.RequireUserToken)).Methods(http.MethodDelete)
Expand Down
3 changes: 3 additions & 0 deletions lib/dam/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ const (
fakeTokenPath = "/tokens/"

// End-point for managing consents. See "proto/tokens/v1/tokens.proto"
listConsentPath = "/dam/v1alpha/{realm}/users/{user}/consents"
deleteConsentPath = "/dam/v1alpha/{realm}/users/{user}/consents/{consent_id}"
// TODO: delete the mocked endpoints when complete.
consentsPath = "/consents"
consentPath = "/consents/"

Expand Down
4 changes: 4 additions & 0 deletions lib/dam/endpoints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ var (
"GET /dam/gatekeeper/.well-known/jwks",
"GET /dam/gatekeeper/.well-known/openid-configuration",

// consent management endpoints
"GET /dam/v1alpha/{realm}/users/{user}/consents",
"DELETE /dam/v1alpha/{realm}/users/{user}/consents/{consent_id}",

// token management endpoints
"GET /dam/v1alpha/users/{user}/tokens",
"DELETE /dam/v1alpha/users/{user}/tokens/{token_id}",
Expand Down
8 changes: 8 additions & 0 deletions lib/dam/info_release.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"google.golang.org/grpc/status" /* copybara-comment */
"bitbucket.org/creachadair/stringset" /* copybara-comment */
"github.com/GoogleCloudPlatform/healthcare-federated-access-services/apis/hydraapi" /* copybara-comment: hydraapi */
"github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/consentsapi" /* copybara-comment: consentsapi */
"github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/errutil" /* copybara-comment: errutil */
"github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/httputils" /* copybara-comment: httputils */
"github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/hydra" /* copybara-comment: hydra */
Expand Down Expand Up @@ -244,3 +245,10 @@ func (s *Service) rejectInformationRelease(r *http.Request) (_ string, ferr erro

return challenge, errutil.WithErrorReason("user_denied", status.Errorf(codes.Unauthenticated, "User denied releasing consent"))
}

func (s *Service) consentService() *consentsapi.Service {
return &consentsapi.Service{
Store: s.store,
Clients: s.clients,
}
}
2 changes: 1 addition & 1 deletion lib/ic/ic.go
Original file line number Diff line number Diff line change
Expand Up @@ -1511,7 +1511,7 @@ func registerHandlers(r *mux.Router, s *Service) {
// consents service endpoints
consentService := s.consentService()
r.HandleFunc(listConsentPath, auth.MustWithAuth(handlerfactory.MakeHandler(s.GetStore(), consentsapi.ListConsentsFactory(consentService, listConsentPath)), s.checker, auth.RequireUserToken)).Methods(http.MethodGet)
r.HandleFunc(deleteConsentPath, auth.MustWithAuth(handlerfactory.MakeHandler(s.GetStore(), consentsapi.DeleteConsentFactory(consentService, deleteConsentPath)), s.checker, auth.RequireUserToken)).Methods(http.MethodDelete)
r.HandleFunc(deleteConsentPath, auth.MustWithAuth(handlerfactory.MakeHandler(s.GetStore(), consentsapi.DeleteConsentFactory(consentService, deleteConsentPath, true)), s.checker, auth.RequireUserToken)).Methods(http.MethodDelete)

// TODO: delete the mocked endpoints when complete.
consents := &consentsapi.StubConsents{Consent: consentsapi.FakeConsent}
Expand Down
5 changes: 2 additions & 3 deletions lib/ic/info_release.go
Original file line number Diff line number Diff line change
Expand Up @@ -677,8 +677,7 @@ func (s *Service) clients(tx storage.Tx) (map[string]*cpb.Client, error) {

func (s *Service) consentService() *consentsapi.Service {
return &consentsapi.Service{
Store: s.store,
FindRememberedConsentsByUser: findRememberedConsentsByUser,
Clients: s.clients,
Store: s.store,
Clients: s.clients,
}
}

0 comments on commit b5aae7b

Please sign in to comment.