diff --git a/api/v1alpha1/agent/openapi.yaml b/api/v1alpha1/agent/openapi.yaml index 55c89c11..467a5784 100644 --- a/api/v1alpha1/agent/openapi.yaml +++ b/api/v1alpha1/agent/openapi.yaml @@ -47,6 +47,12 @@ paths: application/json: schema: $ref: '../openapi.yaml#/components/schemas/Error' + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '../openapi.yaml#/components/schemas/Error' "404": description: NotFound content: @@ -95,6 +101,12 @@ paths: application/json: schema: $ref: '../openapi.yaml#/components/schemas/Error' + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '../openapi.yaml#/components/schemas/Error' "410": description: Gone content: diff --git a/api/v1alpha1/agent/spec.gen.go b/api/v1alpha1/agent/spec.gen.go index 9cc1f5d6..0bb67f37 100644 --- a/api/v1alpha1/agent/spec.gen.go +++ b/api/v1alpha1/agent/spec.gen.go @@ -19,28 +19,28 @@ import ( // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/+RYTXPjNg/+Kxq+71FrOen24lvi7oenTZpJJruHjA+IBFvcSCRLQvGkGf/3DknJli1a", - "1qbJTju92SIIPAAefEjPLJWlkgIFGTZ5ZibNsQT382yJgm4IqDK3KgNC+1BpqVATRyeSasxQEIfiVhf2", - "AT0pZBNmSHOxZOuY8Sz42Di1PUczsZDB40fUhksROFvHTOMfFdeYscldY2JHYbyH2OHb6pzHjU55/w1T", - "svY+aC111/MSjYGlC0mGJtVckUPl5aPmOD4CspGbr2M2EwsNXUsZEBiS2v/jhKXpCi004hQUpJyePp23", - "gsMF4RK19YQkQXFUyD05Flt32tUY7+MIBbR+AFrDk/2fS0NXcoXaUs17A1nGbTihuNrx8hDclnarzVyh", - "nhaVIdQ7ITt4fYNFIK2kfuiLdPZoVpzSPEhOASUGD5qwoqjKmpwiA23Jl3Erdl8Run+N+phVwlRKSW0P", - "5nGgEgoQs2xwshy2IQnxWfXxMz1U+mxDHTrft78V3lfezVeXD62sxO1iCLkyE48oSOqnbt54U13/17hg", - "E/a/ZNv4krrrJb4EbWxTFDV9+uS/TL2YveHJ0it9YTrBaQx5BXENM+TbBV9qsFUxM6bqbQZgDBpToqAg", - "F1NZ7Zy08lrAPRbHGeXF4rahRu0Qgt3ISqeBaQLLZgptHOsLp1fjptSMsAxZSjUCYXbm3F1IXQKxiSUR", - "viNeBtpzM7A2slXlJkRXrM20fko1gn39oXID9juA7mXEgdx621YYysh+6EIUkim3Glpw76UsEMTgKIUw", - "thQfBta/dDiazN4uTfuwNyfxxnQIetMLup1nQI/mB3ReXKNxITnXCA+ZXImu/pwbkksNZXh3+M4RWHLx", - "BYoKw9KGUA1o+Rsl9Q3fuMPNwQ6EninzUWrf+eC+wKFyXznlX0ELLpam/86lpH71e55tg91AD+I8CuoQ", - "gjALAk0+VdW0WQr7h06XQmu3dTxMm0Hwwvt+h3zB5bIZZe0c9enZn322lbbDdo1gpHiJGvl3F071aqur", - "hvLFET1WRYNKaHj9hDY81jUVb1nauLdhTpuCLg27oTyQ4BB3uiWzdp3fv0AWPEVhnNt++rIzBWmO0elo", - "bOekfW9lOZEykyRZrVYjcMcjqZdJfdckv82mHy5vPrw7HY1HOZWFCxknG83tXhZdFSAE6ujsaha9i9ys", - "iFBkSnLn4+bNlVUiwwUXmDkGKhSgOJuwn0bj0YmNA1Duwp6A4snjSeIXo+SZZ+tk++asKuq+fvq5H3mp", - "SC4iytFDYc5Uzf+MTZifr623fGdaQ4l+8b/b1z37ZUcbt88s1ublYuJH/JYZpCuM608KQ/aFub+Mhs5l", - "5kZ2KgXVeywoVfDUwU++Gf8FYKu6r2a6HzLWa09ho6TNr1VwOh53o/n7rzZDp+OTQ0fv/a1Xgem/NDhk", - "u6bOIYuufVy8zZO3t3kroKJcav6nZ+n7kx/g6Ccp0Br7+UdEdWa3NQFFdIP6EXXUCMaMwHa9O7/tsbl9", - "1FSib73DSlGjKiDdr0WjMOULjlnkdXXK8tpfa2/CAwqz0d7o/NcUZ2DjX+/OFwv0QLW+IoIQRf5rNT5+", - "//ZGLyV9lJXI/kF1XheNL/QcoSD3dW+JgaL2x1GaY/rQKd3P/u7QydKCUFudO/zGAfWF7veThK3n678C", - "AAD//z2Y2earFwAA", + "H4sIAAAAAAAC/+xYzXLbNhB+FQ7aIyPKjnvRzVbzo2mTeuxJcsjoABErETEJoMDSGtejd+8AICRKhCjG", + "tTPtTG8SsNj9sD/fLvhIclkpKUCgIZNHYvICKup+Xq5A4C1SrM0nxSiCXVRaKtDIwYnkGhgI5LT8pEu7", + "gA8KyIQY1FysyCYlnEWXjVPbszUTSxndvgdtuBSRvU1KNPxZcw2MTL4GE3sK0wPEDt9O5zwNOuXiG+Ro", + "7b3RWuruzSswhq6cSxiYXHOFDpWXT8J2egJkkJtvUjITS027lhhFalBq/48jVKYrtNQAU6pozvHh3VXL", + "OVwgrEDbm6BEWp4UciunfOt2uxrTQxwxhzYLVGv6YP8X0uC1XIO2qeZvQxnj1p20vN675TG4Le1Wm7kG", + "PS1rg6D3XHb0+BaLAFxLfdfnaXZv1hzzIpqcglYQ3QhuBVFXTXIKRrVNPsat2KJGcP+C+pTUwtRKSW03", + "5mmkEkoqZmxwsBy2IQHxUfX+Mz2p9N66OrZ/aH8nfKi8G69uPrSikraLIXaVmbgHgVI/dOPGQ3X9rGFJ", + "JuSnbEd8WcN6mS9B69scRJM+ffKfp17MnvDJ0iv9wXScEwx5BWkDM3a3D3ylqa2KmTF1LxlQY8CYCgRG", + "czGX9d5OK64lXUB5OqO8WNo2FNQOSbBbWes80k3oKnSh7cX63OnVuC41Q6hilnINFIFduusupa4okolN", + "IniFvIrQc2hYW9m6dh2iK9bOtP6UCoJ9/FC7BvsdQA8i4kDubttWGIvIoetiKSRzbjW04C6kLIGKwV6K", + "YWwpPg6sf+hwaTJ7uTAdwt7upFvTMeiBC7rMM4Cj+RGdH27AOJdcaaB3TK5FV3/BDcqVplV8dvjOFlhx", + "8ZmWNcSlDYIaQPlbJc0JT9xxcrANoafLvJXaMx9dlDBU7gvH4gvVgouV6T/zUWK/+oOb7ZwdoEdxngR1", + "DEE8CyIkn6t6GobC/qbTTaGNmzrupqERPPG8nyGfcLgKrawdoz49h73PUmnbbTdAjRRPUSP/6cCpnm10", + "1bR6skdPVdGgEhpeP7EJj3RNpbssDdfbZk47BV0Y9l15JMCx3OmWzMYxv39AljwHYdy1ffcll4rmBSTn", + "o7Htk/bdSgpEZSZZtl6vR9Rtj6ReZc1Zk/0+m775ePvm1floPCqwKp3LOFpv7uay5LqkQoBOLq9nyavE", + "9YoEBFOSuztuX66kFgyWXABzGahAUMXJhLwejUdn1g8UC+f2jCqe3Z9lfjDKHjnbZLuXs6qx+/z0fT/x", + "UolcJliAh0KcqSb/GZkQ319br3xnWtMK/OD/9VD37Nc9bdyuWazhcTHxLX6XGahrSJtPCkPmhbk/DAav", + "JHMtO5cCmzmWKlXy3MHPvhn/BWCnuq9muh8yNhufwkZJG1+r4Hw87nrzj99shM7HZ8e2LvypZ4HpvzQ4", + "ZPumrihLbrxfvM2zl7f5SdAaC6n5Xz5LL8avX97oW6kXnDFwlHZx9gNc+04KsMZ++RFxnNn5UNAyuQV9", + "DzoJgilBann2q58vydwuhdr3ZD+s+DWokuaH1W8U5HzJgSVeV4cIbvyx9uw9gAqC9qDzP0MHkTfGZr+j", + "WaBH+OEZEcRS5H9WeWlWGV+8vMWPEt/KWrB/EbM0ZeqppQBaovuCuYIIjfjtJC8gv+uQxXt/dmj3bEFo", + "rM4dfuOAemrxM1hGNvPN3wEAAP//bPmVHo8YAAA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/v1alpha1/openapi.yaml b/api/v1alpha1/openapi.yaml index 83d24254..7440aa76 100644 --- a/api/v1alpha1/openapi.yaml +++ b/api/v1alpha1/openapi.yaml @@ -78,6 +78,12 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/Error' "404": description: NotFound content: @@ -116,6 +122,12 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/Error' "404": description: NotFound content: @@ -209,6 +221,12 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/Error' "500": description: Internal Server Error content: @@ -248,6 +266,12 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/Error' "404": description: NotFound content: diff --git a/api/v1alpha1/spec.gen.go b/api/v1alpha1/spec.gen.go index 4cc6ded9..97e7155b 100644 --- a/api/v1alpha1/spec.gen.go +++ b/api/v1alpha1/spec.gen.go @@ -18,37 +18,37 @@ import ( // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "vd3+JwAA//9Lhfm0hCYAAA==", + "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/api/client/agent/client.gen.go b/internal/api/client/agent/client.gen.go index eccc1d26..655c090d 100644 --- a/internal/api/client/agent/client.gen.go +++ b/internal/api/client/agent/client.gen.go @@ -349,6 +349,7 @@ type UpdateAgentStatusResponse struct { HTTPResponse *http.Response JSON400 *externalRef0.Error JSON401 *externalRef0.Error + JSON403 *externalRef0.Error JSON410 *externalRef0.Error JSON500 *externalRef0.Error } @@ -375,6 +376,7 @@ type ReplaceSourceStatusResponse struct { JSON200 *externalRef0.Source JSON400 *externalRef0.Error JSON401 *externalRef0.Error + JSON403 *externalRef0.Error JSON404 *externalRef0.Error JSON500 *externalRef0.Error } @@ -487,6 +489,13 @@ func ParseUpdateAgentStatusResponse(rsp *http.Response) (*UpdateAgentStatusRespo } response.JSON401 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest externalRef0.Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 410: var dest externalRef0.Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -541,6 +550,13 @@ func ParseReplaceSourceStatusResponse(rsp *http.Response) (*ReplaceSourceStatusR } response.JSON401 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest externalRef0.Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: var dest externalRef0.Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { diff --git a/internal/api/client/client.gen.go b/internal/api/client/client.gen.go index ad0a96ae..fa1bd141 100644 --- a/internal/api/client/client.gen.go +++ b/internal/api/client/client.gen.go @@ -610,6 +610,7 @@ type ListAgentsResponse struct { HTTPResponse *http.Response JSON200 *AgentList JSON401 *Error + JSON403 *Error JSON500 *Error } @@ -635,6 +636,7 @@ type DeleteAgentResponse struct { JSON200 *Agent JSON400 *Error JSON401 *Error + JSON403 *Error JSON404 *Error JSON500 *Error } @@ -753,6 +755,7 @@ type DeleteSourceResponse struct { JSON200 *Source JSON400 *Error JSON401 *Error + JSON403 *Error JSON404 *Error } @@ -778,6 +781,7 @@ type ReadSourceResponse struct { JSON200 *Source JSON400 *Error JSON401 *Error + JSON403 *Error JSON404 *Error } @@ -927,6 +931,13 @@ func ParseListAgentsResponse(rsp *http.Response) (*ListAgentsResponse, error) { } response.JSON401 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -974,6 +985,13 @@ func ParseDeleteAgentResponse(rsp *http.Response) (*DeleteAgentResponse, error) } response.JSON401 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -1157,6 +1175,13 @@ func ParseDeleteSourceResponse(rsp *http.Response) (*DeleteSourceResponse, error } response.JSON401 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -1204,6 +1229,13 @@ func ParseReadSourceResponse(rsp *http.Response) (*ReadSourceResponse, error) { } response.JSON401 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { diff --git a/internal/api/server/agent/server.gen.go b/internal/api/server/agent/server.gen.go index aabb0e80..26ebd26b 100644 --- a/internal/api/server/agent/server.gen.go +++ b/internal/api/server/agent/server.gen.go @@ -294,6 +294,15 @@ func (response UpdateAgentStatus401JSONResponse) VisitUpdateAgentStatusResponse( return json.NewEncoder(w).Encode(response) } +type UpdateAgentStatus403JSONResponse externalRef0.Error + +func (response UpdateAgentStatus403JSONResponse) VisitUpdateAgentStatusResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + type UpdateAgentStatus410JSONResponse externalRef0.Error func (response UpdateAgentStatus410JSONResponse) VisitUpdateAgentStatusResponse(w http.ResponseWriter) error { @@ -348,6 +357,15 @@ func (response ReplaceSourceStatus401JSONResponse) VisitReplaceSourceStatusRespo return json.NewEncoder(w).Encode(response) } +type ReplaceSourceStatus403JSONResponse externalRef0.Error + +func (response ReplaceSourceStatus403JSONResponse) VisitReplaceSourceStatusResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + type ReplaceSourceStatus404JSONResponse externalRef0.Error func (response ReplaceSourceStatus404JSONResponse) VisitReplaceSourceStatusResponse(w http.ResponseWriter) error { diff --git a/internal/api/server/server.gen.go b/internal/api/server/server.gen.go index eca8df57..4e815b2f 100644 --- a/internal/api/server/server.gen.go +++ b/internal/api/server/server.gen.go @@ -469,6 +469,15 @@ func (response ListAgents401JSONResponse) VisitListAgentsResponse(w http.Respons return json.NewEncoder(w).Encode(response) } +type ListAgents403JSONResponse Error + +func (response ListAgents403JSONResponse) VisitListAgentsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + type ListAgents500JSONResponse Error func (response ListAgents500JSONResponse) VisitListAgentsResponse(w http.ResponseWriter) error { @@ -513,6 +522,15 @@ func (response DeleteAgent401JSONResponse) VisitDeleteAgentResponse(w http.Respo return json.NewEncoder(w).Encode(response) } +type DeleteAgent403JSONResponse Error + +func (response DeleteAgent403JSONResponse) VisitDeleteAgentResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + type DeleteAgent404JSONResponse Error func (response DeleteAgent404JSONResponse) VisitDeleteAgentResponse(w http.ResponseWriter) error { @@ -727,6 +745,15 @@ func (response DeleteSource401JSONResponse) VisitDeleteSourceResponse(w http.Res return json.NewEncoder(w).Encode(response) } +type DeleteSource403JSONResponse Error + +func (response DeleteSource403JSONResponse) VisitDeleteSourceResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + type DeleteSource404JSONResponse Error func (response DeleteSource404JSONResponse) VisitDeleteSourceResponse(w http.ResponseWriter) error { @@ -771,6 +798,15 @@ func (response ReadSource401JSONResponse) VisitReadSourceResponse(w http.Respons return json.NewEncoder(w).Encode(response) } +type ReadSource403JSONResponse Error + +func (response ReadSource403JSONResponse) VisitReadSourceResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + type ReadSource404JSONResponse Error func (response ReadSource404JSONResponse) VisitReadSourceResponse(w http.ResponseWriter) error { diff --git a/internal/auth/none_authenticator.go b/internal/auth/none_authenticator.go index f7433db1..42ba39b9 100644 --- a/internal/auth/none_authenticator.go +++ b/internal/auth/none_authenticator.go @@ -20,7 +20,7 @@ func (n *NoneAuthenticator) Authenticator(next http.Handler) http.Handler { Username: "admin", Organization: "internal", } - ctx := newContext(r.Context(), user) + ctx := NewUserContext(r.Context(), user) next.ServeHTTP(w, r.WithContext(ctx)) }) } diff --git a/internal/auth/rhsso_authenticator.go b/internal/auth/rhsso_authenticator.go index 02b76471..b792b7e5 100644 --- a/internal/auth/rhsso_authenticator.go +++ b/internal/auth/rhsso_authenticator.go @@ -76,7 +76,7 @@ func (rh *RHSSOAuthenticator) Authenticator(next http.Handler) http.Handler { return } - ctx := newContext(r.Context(), user) + ctx := NewUserContext(r.Context(), user) next.ServeHTTP(w, r.WithContext(ctx)) }) } diff --git a/internal/auth/user.go b/internal/auth/user.go index 7806b363..be616d9b 100644 --- a/internal/auth/user.go +++ b/internal/auth/user.go @@ -20,7 +20,7 @@ func UserFromContext(ctx context.Context) (User, bool) { return val.(User), true } -func newContext(ctx context.Context, u User) context.Context { +func NewUserContext(ctx context.Context, u User) context.Context { return context.WithValue(ctx, usernameKey, u) } diff --git a/internal/service/agent.go b/internal/service/agent.go index ed284b24..014c8c8e 100644 --- a/internal/service/agent.go +++ b/internal/service/agent.go @@ -5,15 +5,21 @@ import ( "errors" "github.com/kubev2v/migration-planner/internal/api/server" + "github.com/kubev2v/migration-planner/internal/auth" + "github.com/kubev2v/migration-planner/internal/service/mappers" "github.com/kubev2v/migration-planner/internal/store" ) func (h *ServiceHandler) ListAgents(ctx context.Context, request server.ListAgentsRequestObject) (server.ListAgentsResponseObject, error) { - result, err := h.store.Agent().List(ctx, store.NewAgentQueryFilter(), store.NewAgentQueryOptions().WithIncludeSoftDeleted(false)) + qf := store.NewAgentQueryFilter() + if user, found := auth.UserFromContext(ctx); found { + qf = qf.ByUsername(user.Username) + } + result, err := h.store.Agent().List(ctx, qf, store.NewAgentQueryOptions().WithIncludeSoftDeleted(false)) if err != nil { return nil, err } - return server.ListAgents200JSONResponse(result), nil + return server.ListAgents200JSONResponse(mappers.AgentListToApi(result)), nil } func (h *ServiceHandler) DeleteAgent(ctx context.Context, request server.DeleteAgentRequestObject) (server.DeleteAgentResponseObject, error) { @@ -24,8 +30,15 @@ func (h *ServiceHandler) DeleteAgent(ctx context.Context, request server.DeleteA } return server.DeleteAgent500JSONResponse{}, nil } - if agent.DeletedAt != nil { - return server.DeleteAgent200JSONResponse(*agent), nil + + if user, found := auth.UserFromContext(ctx); found { + if agent.Username != user.Username { + return server.DeleteAgent403JSONResponse{}, nil + } + } + + if agent.DeletedAt.Valid { + return server.DeleteAgent200JSONResponse(mappers.AgentToApi(*agent)), nil } if agent.Associated { return server.DeleteAgent400JSONResponse{Message: "cannot delete an associated agent"}, nil @@ -37,5 +50,5 @@ func (h *ServiceHandler) DeleteAgent(ctx context.Context, request server.DeleteA return server.DeleteAgent500JSONResponse{}, nil } - return server.DeleteAgent200JSONResponse(*agent), nil + return server.DeleteAgent200JSONResponse(mappers.AgentToApi(*agent)), nil } diff --git a/internal/service/agent/handler.go b/internal/service/agent/handler.go index 1689c3c9..3cdd3cc1 100644 --- a/internal/service/agent/handler.go +++ b/internal/service/agent/handler.go @@ -9,7 +9,9 @@ import ( api "github.com/kubev2v/migration-planner/api/v1alpha1" agentServer "github.com/kubev2v/migration-planner/internal/api/server/agent" + "github.com/kubev2v/migration-planner/internal/auth" "github.com/kubev2v/migration-planner/internal/events" + "github.com/kubev2v/migration-planner/internal/service/mappers" "github.com/kubev2v/migration-planner/internal/store" "go.uber.org/zap" ) @@ -36,6 +38,11 @@ func (h *AgentServiceHandler) ReplaceSourceStatus(ctx context.Context, request a return agentServer.ReplaceSourceStatus500JSONResponse{}, nil } + username, orgID := "", "" + if user, found := auth.UserFromContext(ctx); found { + username, orgID = user.Username, user.Organization + } + agent, err := h.store.Agent().Get(ctx, request.Body.AgentId.String()) if err != nil && !errors.Is(err, store.ErrRecordNotFound) { return agentServer.ReplaceSourceStatus400JSONResponse{}, nil @@ -45,6 +52,10 @@ func (h *AgentServiceHandler) ReplaceSourceStatus(ctx context.Context, request a return agentServer.ReplaceSourceStatus400JSONResponse{}, nil } + if username != agent.Username { + return agentServer.ReplaceSourceStatus403JSONResponse{}, nil + } + source, err := h.store.Source().Get(ctx, request.Id) if err != nil && !errors.Is(err, store.ErrRecordNotFound) { return agentServer.ReplaceSourceStatus400JSONResponse{}, nil @@ -52,7 +63,7 @@ func (h *AgentServiceHandler) ReplaceSourceStatus(ctx context.Context, request a associated := false if source == nil { - source, err = h.store.Source().Create(ctx, request.Id) + source, err = h.store.Source().Create(ctx, mappers.SourceFromApi(request.Id, username, orgID, nil)) if err != nil { return agentServer.ReplaceSourceStatus400JSONResponse{}, nil } @@ -62,8 +73,8 @@ func (h *AgentServiceHandler) ReplaceSourceStatus(ctx context.Context, request a // connect the agent to the source // If agent is already connected to a source but the source is different from the current one, connect it anyway. // An agent is allowed to change sources. - if agent.SourceId == nil || *agent.SourceId != source.Id.String() { - if agent, err = h.store.Agent().UpdateSourceID(ctx, agent.Id, request.Id.String(), associated); err != nil { + if agent.SourceID == nil || *agent.SourceID != source.ID.String() { + if agent, err = h.store.Agent().UpdateSourceID(ctx, agent.ID, request.Id.String(), associated); err != nil { _, _ = store.Rollback(ctx) return agentServer.ReplaceSourceStatus400JSONResponse{}, nil } @@ -71,14 +82,15 @@ func (h *AgentServiceHandler) ReplaceSourceStatus(ctx context.Context, request a // We are not allowing updates from agents not associated with the source ("first come first serve"). if !agent.Associated { - zap.S().Errorf("Failed to update status of source %s from agent %s. Agent is not the associated with the source", source.Id, agent.Id) + zap.S().Errorf("Failed to update status of source %s from agent %s. Agent is not the associated with the source", source.ID, agent.ID) if _, err := store.Commit(ctx); err != nil { return agentServer.ReplaceSourceStatus500JSONResponse{}, nil } return agentServer.ReplaceSourceStatus400JSONResponse{}, nil } - result, err := h.store.Source().Update(ctx, request.Id, &request.Body.Inventory) + newSource := mappers.SourceFromApi(request.Id, username, "", &request.Body.Inventory) + result, err := h.store.Source().Update(ctx, newSource) if err != nil { _, _ = store.Rollback(ctx) return agentServer.ReplaceSourceStatus400JSONResponse{}, nil @@ -93,7 +105,7 @@ func (h *AgentServiceHandler) ReplaceSourceStatus(ctx context.Context, request a zap.S().Named("agent_handler").Errorw("failed to write event", "error", err, "event_kind", kind) } - return agentServer.ReplaceSourceStatus200JSONResponse(*result), nil + return agentServer.ReplaceSourceStatus200JSONResponse(mappers.SourceToApi(*result)), nil } func (h *AgentServiceHandler) Health(ctx context.Context, request agentServer.HealthRequestObject) (agentServer.HealthResponseObject, error) { @@ -106,13 +118,20 @@ func (h *AgentServiceHandler) UpdateAgentStatus(ctx context.Context, request age if err != nil { return agentServer.UpdateAgentStatus500JSONResponse{}, nil } + + username, orgID := "", "" + if user, found := auth.UserFromContext(ctx); found { + username, orgID = user.Username, user.Organization + } + agent, err := h.store.Agent().Get(ctx, request.Id.String()) if err != nil && !errors.Is(err, store.ErrRecordNotFound) { return agentServer.UpdateAgentStatus400JSONResponse{}, nil } if agent == nil { - a, err := h.store.Agent().Create(ctx, *request.Body) + newAgent := mappers.AgentFromApi(username, orgID, request.Body) + a, err := h.store.Agent().Create(ctx, newAgent) if err != nil { return agentServer.UpdateAgentStatus400JSONResponse{}, nil } @@ -120,7 +139,7 @@ func (h *AgentServiceHandler) UpdateAgentStatus(ctx context.Context, request age return agentServer.UpdateAgentStatus500JSONResponse{}, nil } - kind, agentEvent := h.newAgentEvent(*a) + kind, agentEvent := h.newAgentEvent(mappers.AgentToApi(*a)) if err := h.eventWriter.Write(ctx, kind, agentEvent); err != nil { zap.S().Named("agent_handler").Errorw("failed to write event", "error", err, "event_kind", kind) } @@ -128,12 +147,16 @@ func (h *AgentServiceHandler) UpdateAgentStatus(ctx context.Context, request age return agentServer.UpdateAgentStatus201Response{}, nil } + if username != agent.Username { + return agentServer.UpdateAgentStatus403JSONResponse{}, nil + } + // check if agent is marked for deletion - if agent.DeletedAt != nil { + if agent.DeletedAt.Valid { return agentServer.UpdateAgentStatus410JSONResponse{}, nil } - if _, err := h.store.Agent().Update(ctx, *request.Body); err != nil { + if _, err := h.store.Agent().Update(ctx, mappers.AgentFromApi(username, orgID, request.Body)); err != nil { _, _ = store.Rollback(ctx) return agentServer.UpdateAgentStatus400JSONResponse{}, nil } @@ -142,7 +165,7 @@ func (h *AgentServiceHandler) UpdateAgentStatus(ctx context.Context, request age return agentServer.UpdateAgentStatus500JSONResponse{}, nil } - kind, agentEvent := h.newAgentEvent(*agent) + kind, agentEvent := h.newAgentEvent(mappers.AgentToApi(*agent)) if err := h.eventWriter.Write(ctx, kind, agentEvent); err != nil { zap.S().Named("agent_handler").Errorw("failed to write event", "error", err, "event_kind", kind) } diff --git a/internal/service/agent/handler_test.go b/internal/service/agent/handler_test.go index 8804cadf..21ba2cb9 100644 --- a/internal/service/agent/handler_test.go +++ b/internal/service/agent/handler_test.go @@ -11,6 +11,7 @@ import ( v1alpha1 "github.com/kubev2v/migration-planner/api/v1alpha1" apiAgent "github.com/kubev2v/migration-planner/api/v1alpha1/agent" server "github.com/kubev2v/migration-planner/internal/api/server/agent" + "github.com/kubev2v/migration-planner/internal/auth" "github.com/kubev2v/migration-planner/internal/config" "github.com/kubev2v/migration-planner/internal/events" service "github.com/kubev2v/migration-planner/internal/service/agent" @@ -22,6 +23,7 @@ import ( const ( insertAgentStm = "INSERT INTO agents (id, status, status_info, cred_url, version) VALUES ('%s', '%s', '%s', '%s', 'version_1');" + insertAgentWithUsernameStm = "INSERT INTO agents (id, status, status_info, cred_url,username, org_id, version) VALUES ('%s', '%s', '%s', '%s','%s','%s', 'version_1');" insertAgentWithSourceStm = "INSERT INTO agents (id, status, status_info, cred_url, source_id, version) VALUES ('%s', '%s', '%s', '%s', '%s', 'version_1');" insertAgentWithUpdateAtStm = "INSERT INTO agents (id, status, status_info, cred_url, updated_at, version) VALUES ('%s', '%s', '%s', '%s', '%s', 'version_1');" insertAgentWithDeletedAtStm = "INSERT INTO agents (id, status, status_info, cred_url,created_at, updated_at, deleted_at, version) VALUES ('%s', '%s', '%s', '%s', '%s','%s','%s', 'version_1');" @@ -80,6 +82,49 @@ var _ = Describe("agent store", Ordered, func() { Expect(eventWriter.Messages).To(HaveLen(1)) }) + It("successfully creates the agent -- under username scope", func() { + agentID := uuid.New() + + user := auth.User{ + Username: "admin", + Organization: "admin", + } + ctx := auth.NewUserContext(context.TODO(), user) + + eventWriter := newTestWriter() + srv := service.NewAgentServiceHandler(s, events.NewEventProducer(eventWriter)) + resp, err := srv.UpdateAgentStatus(ctx, server.UpdateAgentStatusRequestObject{ + Id: agentID, + Body: &apiAgent.UpdateAgentStatusJSONRequestBody{ + Id: agentID.String(), + Status: string(v1alpha1.AgentStatusWaitingForCredentials), + StatusInfo: "waiting-for-credentials", + CredentialUrl: "creds-url", + Version: "version-1", + }, + }) + Expect(err).To(BeNil()) + Expect(resp).To(Equal(server.UpdateAgentStatus201Response{})) + + count := -1 + tx := gormdb.Raw("SELECT COUNT(*) FROM agents;").Scan(&count) + Expect(tx.Error).To(BeNil()) + Expect(count).To(Equal(1)) + + d := struct { + Username string + Status string + }{} + tx = gormdb.Raw(fmt.Sprintf("SELECT username,status from agents WHERE id = '%s';", agentID)).Scan(&d) + Expect(tx.Error).To(BeNil()) + Expect(d.Status).To(Equal("waiting-for-credentials")) + Expect(d.Username).To(Equal("admin")) + + // should find one event + <-time.After(500 * time.Millisecond) + Expect(eventWriter.Messages).To(HaveLen(1)) + }) + It("successfully updates the agent", func() { agentID := uuid.New() tx := gormdb.Exec(fmt.Sprintf(insertAgentStm, agentID, "not-connected", "status-info-1", "cred_url-1")) @@ -115,6 +160,78 @@ var _ = Describe("agent store", Ordered, func() { Expect(eventWriter.Messages).To(HaveLen(1)) }) + It("successfully updates the agent -- under agent scope", func() { + agentID := uuid.New() + tx := gormdb.Exec(fmt.Sprintf(insertAgentWithUsernameStm, agentID, "not-connected", "status-info-1", "cred_url-1", "admin", "admin")) + Expect(tx.Error).To(BeNil()) + + user := auth.User{ + Username: "admin", + Organization: "admin", + } + ctx := auth.NewUserContext(context.TODO(), user) + + eventWriter := newTestWriter() + srv := service.NewAgentServiceHandler(s, events.NewEventProducer(eventWriter)) + resp, err := srv.UpdateAgentStatus(ctx, server.UpdateAgentStatusRequestObject{ + Id: agentID, + Body: &apiAgent.UpdateAgentStatusJSONRequestBody{ + Id: agentID.String(), + Status: string(v1alpha1.AgentStatusWaitingForCredentials), + StatusInfo: "waiting-for-credentials", + CredentialUrl: "creds-url", + Version: "version-1", + }, + }) + Expect(err).To(BeNil()) + Expect(resp).To(Equal(server.UpdateAgentStatus200Response{})) + + count := -1 + tx = gormdb.Raw("SELECT COUNT(*) FROM agents;").Scan(&count) + Expect(tx.Error).To(BeNil()) + Expect(count).To(Equal(1)) + + d := struct { + Username string + Status string + }{} + tx = gormdb.Raw(fmt.Sprintf("SELECT username,status from agents WHERE id = '%s';", agentID)).Scan(&d) + Expect(tx.Error).To(BeNil()) + Expect(d.Status).To(Equal("waiting-for-credentials")) + Expect(d.Username).To(Equal("admin")) + + // should find one event + <-time.After(500 * time.Millisecond) + Expect(eventWriter.Messages).To(HaveLen(1)) + }) + + It("failed to update the agent -- username is different", func() { + agentID := uuid.New() + tx := gormdb.Exec(fmt.Sprintf(insertAgentWithUsernameStm, agentID, "not-connected", "status-info-1", "cred_url-1", "user", "user")) + Expect(tx.Error).To(BeNil()) + + user := auth.User{ + Username: "admin", + Organization: "admin", + } + ctx := auth.NewUserContext(context.TODO(), user) + + eventWriter := newTestWriter() + srv := service.NewAgentServiceHandler(s, events.NewEventProducer(eventWriter)) + resp, err := srv.UpdateAgentStatus(ctx, server.UpdateAgentStatusRequestObject{ + Id: agentID, + Body: &apiAgent.UpdateAgentStatusJSONRequestBody{ + Id: agentID.String(), + Status: string(v1alpha1.AgentStatusWaitingForCredentials), + StatusInfo: "waiting-for-credentials", + CredentialUrl: "creds-url", + Version: "version-1", + }, + }) + Expect(err).To(BeNil()) + Expect(resp).To(Equal(server.UpdateAgentStatus403JSONResponse{})) + }) + It("should receive 410 when agent is soft deleted", func() { agentID := uuid.New() tx := gormdb.Exec(fmt.Sprintf(insertAgentWithDeletedAtStm, agentID, "not-connected", "status-info-1", "cred_url-1", time.Now().Format(time.RFC3339), time.Now().Format(time.RFC3339), time.Now().Format(time.RFC3339))) @@ -169,14 +286,82 @@ var _ = Describe("agent store", Ordered, func() { source, err := s.Source().Get(context.TODO(), sourceID) Expect(err).To(BeNil()) Expect(source.Agents).ToNot(BeNil()) - Expect(*source.Agents).To(HaveLen(1)) - Expect((*source.Agents)[0].Id).To(Equal(agentID)) + Expect(source.Agents).To(HaveLen(1)) + Expect(source.Agents[0].ID).To(Equal(agentID.String())) // should have one 1 event only <-time.After(500 * time.Millisecond) Expect(eventWriter.Messages).To(HaveLen(1)) }) + It("successfully creates the source -- under user's scope", func() { + agentID := uuid.New() + tx := gormdb.Exec(fmt.Sprintf(insertAgentWithUsernameStm, agentID, "not-connected", "status-info-1", "cred_url-1", "admin", "admin")) + Expect(tx.Error).To(BeNil()) + + sourceID := uuid.New() + eventWriter := newTestWriter() + srv := service.NewAgentServiceHandler(s, events.NewEventProducer(eventWriter)) + + user := auth.User{ + Username: "admin", + Organization: "admin", + } + ctx := auth.NewUserContext(context.TODO(), user) + + resp, err := srv.ReplaceSourceStatus(ctx, server.ReplaceSourceStatusRequestObject{ + Id: sourceID, + Body: &apiAgent.SourceStatusUpdate{ + AgentId: agentID, + Inventory: v1alpha1.Inventory{}, + }, + }) + Expect(err).To(BeNil()) + Expect(reflect.TypeOf(resp)).To(Equal(reflect.TypeOf(server.ReplaceSourceStatus200JSONResponse{}))) + + // according to the multi source model the agent should be associated with the source + agent, err := s.Agent().Get(context.TODO(), agentID.String()) + Expect(err).To(BeNil()) + Expect(agent.Associated).To(BeTrue()) + + // the source should have the agent associated + source, err := s.Source().Get(context.TODO(), sourceID) + Expect(err).To(BeNil()) + Expect(source.Agents).ToNot(BeNil()) + Expect(source.Agents).To(HaveLen(1)) + Expect(source.Agents[0].ID).To(Equal(agentID.String())) + + // should have one 1 event only + <-time.After(500 * time.Millisecond) + Expect(eventWriter.Messages).To(HaveLen(1)) + }) + + It("fail to creates the source -- under user's scope", func() { + agentID := uuid.New() + tx := gormdb.Exec(fmt.Sprintf(insertAgentWithUsernameStm, agentID, "not-connected", "status-info-1", "cred_url-1", "user", "user")) + Expect(tx.Error).To(BeNil()) + + sourceID := uuid.New() + eventWriter := newTestWriter() + srv := service.NewAgentServiceHandler(s, events.NewEventProducer(eventWriter)) + + user := auth.User{ + Username: "admin", + Organization: "admin", + } + ctx := auth.NewUserContext(context.TODO(), user) + + resp, err := srv.ReplaceSourceStatus(ctx, server.ReplaceSourceStatusRequestObject{ + Id: sourceID, + Body: &apiAgent.SourceStatusUpdate{ + AgentId: agentID, + Inventory: v1alpha1.Inventory{}, + }, + }) + Expect(err).To(BeNil()) + Expect(reflect.TypeOf(resp)).To(Equal(reflect.TypeOf(server.ReplaceSourceStatus403JSONResponse{}))) + }) + It("agents not associated with the source are not allowed to update inventory", func() { agentID := uuid.New() tx := gormdb.Exec(fmt.Sprintf(insertAgentStm, agentID, "not-connected", "status-info-1", "cred_url-1")) @@ -208,8 +393,8 @@ var _ = Describe("agent store", Ordered, func() { source, err := s.Source().Get(context.TODO(), sourceID) Expect(err).To(BeNil()) Expect(source.Agents).ToNot(BeNil()) - Expect(*source.Agents).To(HaveLen(1)) - Expect((*source.Agents)[0].Id).To(Equal(agentID)) + Expect(source.Agents).To(HaveLen(1)) + Expect(source.Agents[0].ID).To(Equal(agentID.String())) // make another request from another agent secondAgentID := uuid.New() diff --git a/internal/service/agent_test.go b/internal/service/agent_test.go index 7322d620..cfaf8da8 100644 --- a/internal/service/agent_test.go +++ b/internal/service/agent_test.go @@ -8,6 +8,7 @@ import ( "github.com/google/uuid" "github.com/kubev2v/migration-planner/internal/api/server" + "github.com/kubev2v/migration-planner/internal/auth" "github.com/kubev2v/migration-planner/internal/config" "github.com/kubev2v/migration-planner/internal/events" "github.com/kubev2v/migration-planner/internal/service" @@ -18,9 +19,10 @@ import ( ) const ( - insertAgentStm = "INSERT INTO agents (id, status, status_info, cred_url, version) VALUES ('%s', '%s', '%s', '%s', 'version_1');" - insertSoftDeletedAgentStm = "INSERT INTO agents (id, deleted_at) VALUES ('%s', '%s');" - insertAssociatedAgentStm = "INSERT INTO agents (id, associated) VALUES ('%s', TRUE);" + insertAgentStm = "INSERT INTO agents (id, status, status_info, cred_url, version) VALUES ('%s', '%s', '%s', '%s', 'version_1');" + insertAgentWithUsernameStm = "INSERT INTO agents (id, status, status_info, cred_url,username, org_id, version) VALUES ('%s', '%s', '%s', '%s','%s','%s', 'version_1');" + insertSoftDeletedAgentStm = "INSERT INTO agents (id, deleted_at) VALUES ('%s', '%s');" + insertAssociatedAgentStm = "INSERT INTO agents (id, associated) VALUES ('%s', TRUE);" ) var _ = Describe("agent handler", Ordered, func() { @@ -75,6 +77,27 @@ var _ = Describe("agent handler", Ordered, func() { Expect(resp).To(HaveLen(2)) }) + It("successfully list all the agents -- filtered by user", func() { + tx := gormdb.Exec(fmt.Sprintf(insertAgentWithUsernameStm, "agent-1", "not-connected", "status-info-1", "cred_url-1", "admin", "admin")) + Expect(tx.Error).To(BeNil()) + tx = gormdb.Exec(fmt.Sprintf(insertAgentWithUsernameStm, "agent-2", "not-connected", "status-info-2", "cred_url-2", "user", "user")) + Expect(tx.Error).To(BeNil()) + + eventWriter := newTestWriter() + srv := service.NewServiceHandler(s, events.NewEventProducer(eventWriter)) + + user := auth.User{ + Username: "admin", + Organization: "admin", + } + ctx := auth.NewUserContext(context.TODO(), user) + resp, err := srv.ListAgents(ctx, server.ListAgentsRequestObject{}) + + Expect(err).To(BeNil()) + Expect(reflect.TypeOf(resp)).To(Equal(reflect.TypeOf(server.ListAgents200JSONResponse{}))) + Expect(resp).To(HaveLen(1)) + }) + AfterEach(func() { gormdb.Exec("DELETE FROM agents;") }) @@ -121,6 +144,42 @@ var _ = Describe("agent handler", Ordered, func() { Expect(reflect.TypeOf(resp)).To(Equal(reflect.TypeOf(server.DeleteAgent400JSONResponse{}))) }) + It("successfully delete user's agent", func() { + agentID := uuid.New() + tx := gormdb.Exec(fmt.Sprintf(insertAgentWithUsernameStm, agentID, "not-connected", "status-info-1", "cred_url-1", "admin", "admin")) + Expect(tx.Error).To(BeNil()) + + eventWriter := newTestWriter() + srv := service.NewServiceHandler(s, events.NewEventProducer(eventWriter)) + + user := auth.User{ + Username: "admin", + Organization: "admin", + } + ctx := auth.NewUserContext(context.TODO(), user) + resp, err := srv.DeleteAgent(ctx, server.DeleteAgentRequestObject{Id: agentID}) + Expect(err).To(BeNil()) + Expect(reflect.TypeOf(resp)).To(Equal(reflect.TypeOf(server.DeleteAgent200JSONResponse{}))) + }) + + It("fails to delete other user's agent", func() { + agentID := uuid.New() + tx := gormdb.Exec(fmt.Sprintf(insertAgentWithUsernameStm, agentID, "not-connected", "status-info-1", "cred_url-1", "admin", "admin")) + Expect(tx.Error).To(BeNil()) + + eventWriter := newTestWriter() + srv := service.NewServiceHandler(s, events.NewEventProducer(eventWriter)) + + user := auth.User{ + Username: "user", + Organization: "user", + } + ctx := auth.NewUserContext(context.TODO(), user) + resp, err := srv.DeleteAgent(ctx, server.DeleteAgentRequestObject{Id: agentID}) + Expect(err).To(BeNil()) + Expect(reflect.TypeOf(resp)).To(Equal(reflect.TypeOf(server.DeleteAgent403JSONResponse{}))) + }) + AfterEach(func() { gormdb.Exec("DELETE FROM agents;") }) diff --git a/internal/service/mappers/inbound.go b/internal/service/mappers/inbound.go new file mode 100644 index 00000000..56f31027 --- /dev/null +++ b/internal/service/mappers/inbound.go @@ -0,0 +1,34 @@ +package mappers + +import ( + "github.com/google/uuid" + api "github.com/kubev2v/migration-planner/api/v1alpha1" + apiAgent "github.com/kubev2v/migration-planner/api/v1alpha1/agent" + "github.com/kubev2v/migration-planner/internal/store/model" +) + +func AgentFromApi(username string, orgID string, resource *apiAgent.AgentStatusUpdate) model.Agent { + return model.Agent{ + ID: resource.Id, + Status: resource.Status, + StatusInfo: resource.StatusInfo, + Username: username, + OrgID: orgID, + CredUrl: resource.CredentialUrl, + Version: resource.Version, + } +} + +func SourceFromApi(id uuid.UUID, username string, orgID string, inventory *api.Inventory) model.Source { + source := model.Source{ + ID: id, + Username: username, + OrgID: orgID, + } + + if inventory != nil { + source.Inventory = model.MakeJSONField(*inventory) + } + + return source +} diff --git a/internal/service/mappers/outbound.go b/internal/service/mappers/outbound.go new file mode 100644 index 00000000..b72aae4b --- /dev/null +++ b/internal/service/mappers/outbound.go @@ -0,0 +1,68 @@ +package mappers + +import ( + "github.com/google/uuid" + api "github.com/kubev2v/migration-planner/api/v1alpha1" + "github.com/kubev2v/migration-planner/internal/store/model" +) + +func SourceToApi(s model.Source) api.Source { + source := api.Source{ + Id: s.ID, + Inventory: nil, + CreatedAt: s.CreatedAt, + UpdatedAt: s.UpdatedAt, + } + + if len(s.Agents) > 0 { + agents := make([]api.SourceAgentItem, 0, len(s.Agents)) + for _, a := range s.Agents { + agents = append(agents, api.SourceAgentItem{Id: uuid.MustParse(a.ID), Associated: a.Associated}) + } + source.Agents = &agents + } + + if s.Inventory != nil { + source.Inventory = &s.Inventory.Data + } + return source +} + +func SourceListToApi(sources model.SourceList) api.SourceList { + sourceList := make([]api.Source, 0, len(sources)) + for _, source := range sources { + sourceList = append(sourceList, SourceToApi(source)) + } + return sourceList +} + +func AgentToApi(a model.Agent) api.Agent { + agent := api.Agent{ + Id: a.ID, + Status: api.StringToAgentStatus(a.Status), + StatusInfo: a.StatusInfo, + CreatedAt: a.CreatedAt, + UpdatedAt: a.UpdatedAt, + CredentialUrl: a.CredUrl, + Version: a.Version, + Associated: a.Associated, + } + + if a.DeletedAt.Valid { + agent.DeletedAt = &a.DeletedAt.Time + } + + if a.SourceID != nil { + agent.SourceId = a.SourceID + } + + return agent +} + +func AgentListToApi(agents model.AgentList) api.AgentList { + agentList := make([]api.Agent, 0, len(agents)) + for _, agent := range agents { + agentList = append(agentList, AgentToApi(agent)) + } + return agentList +} diff --git a/internal/service/source.go b/internal/service/source.go index 27ed8279..9040eb10 100644 --- a/internal/service/source.go +++ b/internal/service/source.go @@ -4,15 +4,21 @@ import ( "context" "github.com/kubev2v/migration-planner/internal/api/server" + "github.com/kubev2v/migration-planner/internal/auth" + "github.com/kubev2v/migration-planner/internal/service/mappers" "github.com/kubev2v/migration-planner/internal/store" ) func (h *ServiceHandler) ListSources(ctx context.Context, request server.ListSourcesRequestObject) (server.ListSourcesResponseObject, error) { - result, err := h.store.Source().List(ctx) + filter := store.NewSourceQueryFilter() + if user, found := auth.UserFromContext(ctx); found { + filter = filter.ByUsername(user.Username) + } + result, err := h.store.Source().List(ctx, filter) if err != nil { return nil, err } - return server.ListSources200JSONResponse(result), nil + return server.ListSources200JSONResponse(mappers.SourceListToApi(result)), nil } func (h *ServiceHandler) DeleteSources(ctx context.Context, request server.DeleteSourcesRequestObject) (server.DeleteSourcesResponseObject, error) { @@ -24,11 +30,16 @@ func (h *ServiceHandler) DeleteSources(ctx context.Context, request server.Delet } func (h *ServiceHandler) ReadSource(ctx context.Context, request server.ReadSourceRequestObject) (server.ReadSourceResponseObject, error) { - result, err := h.store.Source().Get(ctx, request.Id) + source, err := h.store.Source().Get(ctx, request.Id) if err != nil { return server.ReadSource404JSONResponse{}, nil } - return server.ReadSource200JSONResponse(*result), nil + if user, found := auth.UserFromContext(ctx); found { + if user.Username != source.Username { + return server.ReadSource403JSONResponse{}, nil + } + } + return server.ReadSource200JSONResponse(mappers.SourceToApi(*source)), nil } func (h *ServiceHandler) DeleteSource(ctx context.Context, request server.DeleteSourceRequestObject) (server.DeleteSourceResponseObject, error) { @@ -38,13 +49,29 @@ func (h *ServiceHandler) DeleteSource(ctx context.Context, request server.Delete return server.DeleteSource404JSONResponse{}, nil } - agents, err := h.store.Agent().List(ctx, store.NewAgentQueryFilter().BySourceID(request.Id.String()), store.NewAgentQueryOptions()) + source, err := h.store.Source().Get(ctx, request.Id) + if err != nil { + return server.DeleteSource404JSONResponse{}, nil + } + if user, found := auth.UserFromContext(ctx); found { + if user.Username != source.Username { + return server.DeleteSource403JSONResponse{}, nil + } + } + + // TODO check if user is admin + agentFilter := store.NewAgentQueryFilter().BySourceID(request.Id.String()) + if user, found := auth.UserFromContext(ctx); found { + agentFilter = agentFilter.ByUsername(user.Username) + } + + agents, err := h.store.Agent().List(ctx, agentFilter, store.NewAgentQueryOptions()) if err != nil { return server.DeleteSource400JSONResponse{}, nil } for _, agent := range agents { - if err := h.store.Agent().Delete(ctx, agent.Id, true); err != nil { + if err := h.store.Agent().Delete(ctx, agent.ID, true); err != nil { _, _ = store.Rollback(ctx) return server.DeleteSource400JSONResponse{}, nil } diff --git a/internal/service/source_test.go b/internal/service/source_test.go index 128d0ab1..63516415 100644 --- a/internal/service/source_test.go +++ b/internal/service/source_test.go @@ -8,6 +8,7 @@ import ( cloudevents "github.com/cloudevents/sdk-go/v2" "github.com/google/uuid" "github.com/kubev2v/migration-planner/internal/api/server" + "github.com/kubev2v/migration-planner/internal/auth" "github.com/kubev2v/migration-planner/internal/config" "github.com/kubev2v/migration-planner/internal/events" "github.com/kubev2v/migration-planner/internal/service" @@ -18,8 +19,9 @@ import ( ) const ( - insertAgentWithSourceStm = "INSERT INTO agents (id, source_id,associated) VALUES ('%s', '%s', TRUE);" - insertSourceStm = "INSERT INTO sources (id) VALUES ('%s');" + insertAgentWithSourceStm = "INSERT INTO agents (id, source_id,associated) VALUES ('%s', '%s', TRUE);" + insertSourceStm = "INSERT INTO sources (id) VALUES ('%s');" + insertSourceWithUsernameStm = "INSERT INTO sources (id,username, org_id) VALUES ('%s', '%s', '%s');" ) var _ = Describe("source handler", Ordered, func() { @@ -56,6 +58,27 @@ var _ = Describe("source handler", Ordered, func() { Expect(resp).To(HaveLen(2)) }) + It("successfully list all the sources -- filtered by user", func() { + tx := gormdb.Exec(fmt.Sprintf(insertSourceWithUsernameStm, uuid.NewString(), "admin", "admin")) + Expect(tx.Error).To(BeNil()) + tx = gormdb.Exec(fmt.Sprintf(insertSourceWithUsernameStm, uuid.NewString(), "cosmin", "cosmin")) + Expect(tx.Error).To(BeNil()) + + eventWriter := newTestWriter() + + user := auth.User{ + Username: "admin", + Organization: "admin", + } + ctx := auth.NewUserContext(context.TODO(), user) + + srv := service.NewServiceHandler(s, events.NewEventProducer(eventWriter)) + resp, err := srv.ListSources(ctx, server.ListSourcesRequestObject{}) + Expect(err).To(BeNil()) + Expect(reflect.TypeOf(resp)).To(Equal(reflect.TypeOf(server.ListSources200JSONResponse{}))) + Expect(resp).To(HaveLen(1)) + }) + AfterEach(func() { gormdb.Exec("DELETE FROM agents;") gormdb.Exec("DELETE FROM sources;") @@ -114,6 +137,7 @@ var _ = Describe("source handler", Ordered, func() { Expect(count).To(Equal(0)) }) + It("successfully deletes a source", func() { source := uuid.New() tx := gormdb.Exec(fmt.Sprintf(insertSourceStm, source)) @@ -142,8 +166,68 @@ var _ = Describe("source handler", Ordered, func() { myAgent, err := s.Agent().Get(context.TODO(), agent.String()) Expect(err).To(BeNil()) Expect(myAgent.DeletedAt).NotTo(BeNil()) + }) + + It("successfully deletes a source -- under user's scope", func() { + source := uuid.New() + tx := gormdb.Exec(fmt.Sprintf(insertSourceWithUsernameStm, source, "admin", "admin")) + Expect(tx.Error).To(BeNil()) + + agent := uuid.New() + tx = gormdb.Exec(fmt.Sprintf(insertAgentWithSourceStm, agent.String(), source.String())) + Expect(tx.Error).To(BeNil()) + + eventWriter := newTestWriter() + + user := auth.User{ + Username: "admin", + Organization: "admin", + } + ctx := auth.NewUserContext(context.TODO(), user) + srv := service.NewServiceHandler(s, events.NewEventProducer(eventWriter)) + _, err := srv.DeleteSource(ctx, server.DeleteSourceRequestObject{Id: source}) + Expect(err).To(BeNil()) + + count := 1 + tx = gormdb.Raw("SELECT COUNT(*) FROM SOURCES;").Scan(&count) + Expect(tx.Error).To(BeNil()) + Expect(count).To(Equal(0)) + + // we still have an agent but it's soft deleted + count = 0 + tx = gormdb.Raw("SELECT COUNT(*) FROM AGENTS;").Scan(&count) + Expect(tx.Error).To(BeNil()) + Expect(count).To(Equal(1)) + + myAgent, err := s.Agent().Get(context.TODO(), agent.String()) + Expect(err).To(BeNil()) + Expect(myAgent.DeletedAt).NotTo(BeNil()) }) + + It("fails to delete a source -- under user's scope", func() { + source := uuid.New() + tx := gormdb.Exec(fmt.Sprintf(insertSourceWithUsernameStm, source, "admin", "admin")) + Expect(tx.Error).To(BeNil()) + + agent := uuid.New() + tx = gormdb.Exec(fmt.Sprintf(insertAgentWithSourceStm, agent.String(), source.String())) + Expect(tx.Error).To(BeNil()) + + eventWriter := newTestWriter() + + user := auth.User{ + Username: "user", + Organization: "user", + } + ctx := auth.NewUserContext(context.TODO(), user) + + srv := service.NewServiceHandler(s, events.NewEventProducer(eventWriter)) + resp, err := srv.DeleteSource(ctx, server.DeleteSourceRequestObject{Id: source}) + Expect(err).To(BeNil()) + Expect(reflect.TypeOf(resp)).To(Equal(reflect.TypeOf(server.DeleteSource403JSONResponse{}))) + }) + AfterEach(func() { gormdb.Exec("DELETE FROM agents;") gormdb.Exec("DELETE FROM sources;") diff --git a/internal/store/agent.go b/internal/store/agent.go index 6c5a67fb..5c1ddb46 100644 --- a/internal/store/agent.go +++ b/internal/store/agent.go @@ -4,8 +4,6 @@ import ( "context" "errors" - api "github.com/kubev2v/migration-planner/api/v1alpha1" - apiAgent "github.com/kubev2v/migration-planner/api/v1alpha1/agent" "github.com/kubev2v/migration-planner/internal/store/model" "go.uber.org/zap" "gorm.io/gorm" @@ -22,11 +20,11 @@ const ( ) type Agent interface { - List(ctx context.Context, filter *AgentQueryFilter, opts *AgentQueryOptions) (api.AgentList, error) - Get(ctx context.Context, id string) (*api.Agent, error) - Update(ctx context.Context, agentUpdate apiAgent.AgentStatusUpdate) (*api.Agent, error) - UpdateSourceID(ctx context.Context, agentID string, sourceID string, associated bool) (*api.Agent, error) - Create(ctx context.Context, agentUpdate apiAgent.AgentStatusUpdate) (*api.Agent, error) + List(ctx context.Context, filter *AgentQueryFilter, opts *AgentQueryOptions) (model.AgentList, error) + Get(ctx context.Context, id string) (*model.Agent, error) + Update(ctx context.Context, agent model.Agent) (*model.Agent, error) + UpdateSourceID(ctx context.Context, agentID string, sourceID string, associated bool) (*model.Agent, error) + Create(ctx context.Context, agent model.Agent) (*model.Agent, error) Delete(ctx context.Context, id string, softDeletion bool) error InitialMigration(context.Context) error } @@ -46,7 +44,7 @@ func (a *AgentStore) InitialMigration(ctx context.Context) error { // List lists all the agents. // // If includeSoftDeleted is true, it lists the agents soft-deleted. -func (a *AgentStore) List(ctx context.Context, filter *AgentQueryFilter, opts *AgentQueryOptions) (api.AgentList, error) { +func (a *AgentStore) List(ctx context.Context, filter *AgentQueryFilter, opts *AgentQueryOptions) (model.AgentList, error) { var agents model.AgentList tx := a.getDB(ctx) @@ -66,26 +64,21 @@ func (a *AgentStore) List(ctx context.Context, filter *AgentQueryFilter, opts *A return nil, err } - return agents.ToApiResource(), nil + return agents, nil } // Create creates an agent from api model. -func (a *AgentStore) Create(ctx context.Context, agentUpdate apiAgent.AgentStatusUpdate) (*api.Agent, error) { - agent := model.NewAgentFromApiResource(&agentUpdate) - - if err := a.getDB(ctx).WithContext(ctx).Create(agent).Error; err != nil { +func (a *AgentStore) Create(ctx context.Context, agent model.Agent) (*model.Agent, error) { + if err := a.getDB(ctx).WithContext(ctx).Create(&agent).Error; err != nil { return nil, err } - createdResource := agent.ToApiResource() - return &createdResource, nil + return &agent, nil } // Update updates an agent from api model. -func (a *AgentStore) Update(ctx context.Context, agentUpdate apiAgent.AgentStatusUpdate) (*api.Agent, error) { - agent := model.NewAgentFromApiResource(&agentUpdate) - - if err := a.getDB(ctx).WithContext(ctx).First(&model.Agent{ID: agentUpdate.Id}).Error; err != nil { +func (a *AgentStore) Update(ctx context.Context, agent model.Agent) (*model.Agent, error) { + if err := a.getDB(ctx).WithContext(ctx).First(&model.Agent{ID: agent.ID}).Error; err != nil { return nil, err } @@ -93,16 +86,15 @@ func (a *AgentStore) Update(ctx context.Context, agentUpdate apiAgent.AgentStatu return nil, tx.Error } - updatedAgent := agent.ToApiResource() - return &updatedAgent, nil + return &agent, nil } // UpdateSourceID updates the sources id field of an agent. // The source must exists. -func (a *AgentStore) UpdateSourceID(ctx context.Context, agentID string, sourceID string, associated bool) (*api.Agent, error) { +func (a *AgentStore) UpdateSourceID(ctx context.Context, agentID string, sourceID string, associated bool) (*model.Agent, error) { agent := model.NewAgentFromID(agentID) - if err := a.getDB(ctx).WithContext(ctx).First(agent).Error; err != nil { + if err := a.getDB(ctx).WithContext(ctx).First(&agent).Error; err != nil { return nil, err } @@ -113,12 +105,11 @@ func (a *AgentStore) UpdateSourceID(ctx context.Context, agentID string, sourceI return nil, tx.Error } - updatedAgent := agent.ToApiResource() - return &updatedAgent, nil + return agent, nil } // Get returns an agent based on its id. -func (a *AgentStore) Get(ctx context.Context, id string) (*api.Agent, error) { +func (a *AgentStore) Get(ctx context.Context, id string) (*model.Agent, error) { agent := model.NewAgentFromID(id) if err := a.getDB(ctx).WithContext(ctx).Unscoped().First(&agent).Error; err != nil { @@ -128,8 +119,7 @@ func (a *AgentStore) Get(ctx context.Context, id string) (*api.Agent, error) { return nil, err } - agentSource := agent.ToApiResource() - return &agentSource, nil + return agent, nil } // Delete removes an agent. @@ -155,69 +145,3 @@ func (a *AgentStore) getDB(ctx context.Context) *gorm.DB { } return a.db } - -type BaseAgentQuerier struct { - QueryFn []func(tx *gorm.DB) *gorm.DB -} - -type AgentQueryFilter = BaseAgentQuerier - -func NewAgentQueryFilter() *AgentQueryFilter { - return &AgentQueryFilter{QueryFn: make([]func(tx *gorm.DB) *gorm.DB, 0)} -} - -func (qf *AgentQueryFilter) BySourceID(sourceID string) *AgentQueryFilter { - qf.QueryFn = append(qf.QueryFn, func(tx *gorm.DB) *gorm.DB { - return tx.Where("source_id = ?", sourceID) - }) - return qf -} - -func (qf *AgentQueryFilter) BySoftDeleted(isSoftDeleted bool) *AgentQueryFilter { - qf.QueryFn = append(qf.QueryFn, func(tx *gorm.DB) *gorm.DB { - if isSoftDeleted { - return tx.Unscoped().Where("deleted_at IS NOT NULL") - } - return tx - }) - return qf -} - -type AgentQueryOptions = BaseAgentQuerier - -func NewAgentQueryOptions() *AgentQueryOptions { - return &AgentQueryOptions{QueryFn: make([]func(tx *gorm.DB) *gorm.DB, 0)} -} - -func (o *AgentQueryOptions) WithIncludeSoftDeleted(includeSoftDeleted bool) *AgentQueryOptions { - o.QueryFn = append(o.QueryFn, func(tx *gorm.DB) *gorm.DB { - if includeSoftDeleted { - return tx.Unscoped() - } - return tx - }) - return o -} - -func (qf *AgentQueryFilter) ByID(ids []string) *AgentQueryFilter { - qf.QueryFn = append(qf.QueryFn, func(tx *gorm.DB) *gorm.DB { - return tx.Where("id IN ?", ids) - }) - return qf -} - -func (o *AgentQueryOptions) WithSortOrder(sort SortOrder) *AgentQueryOptions { - o.QueryFn = append(o.QueryFn, func(tx *gorm.DB) *gorm.DB { - switch sort { - case SortByID: - return tx.Order("id") - case SortByUpdatedTime: - return tx.Order("updated_at") - case SortByCreatedTime: - return tx.Order("created_at") - default: - return tx - } - }) - return o -} diff --git a/internal/store/agent_test.go b/internal/store/agent_test.go index 5994913f..c7fe7ffc 100644 --- a/internal/store/agent_test.go +++ b/internal/store/agent_test.go @@ -5,9 +5,9 @@ import ( "fmt" "time" - apiAgent "github.com/kubev2v/migration-planner/api/v1alpha1/agent" "github.com/kubev2v/migration-planner/internal/config" "github.com/kubev2v/migration-planner/internal/store" + "github.com/kubev2v/migration-planner/internal/store/model" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "gorm.io/gorm" @@ -15,6 +15,7 @@ import ( const ( insertAgentStm = "INSERT INTO agents (id, status, status_info, cred_url, version) VALUES ('%s', '%s', '%s', '%s', 'version_1');" + insertAgentWithUsernameStm = "INSERT INTO agents (id, status, status_info, cred_url,username, org_id, version) VALUES ('%s', '%s', '%s', '%s','%s', '%s', 'version_1');" insertAgentWithSourceStm = "INSERT INTO agents (id, status, status_info, cred_url, source_id, version) VALUES ('%s', '%s', '%s', '%s', '%s', 'version_1');" insertAgentWithUpdateAtStm = "INSERT INTO agents (id, status, status_info, cred_url, updated_at, version) VALUES ('%s', '%s', '%s', '%s', '%s', 'version_1');" insertAgentWithDeletedAtStm = "INSERT INTO agents (id, status, status_info, cred_url, deleted_at, version) VALUES ('%s', '%s', '%s', '%s', '%s', 'version_1');" @@ -51,12 +52,12 @@ var _ = Describe("agent store", Ordered, func() { Expect(err).To(BeNil()) Expect(agents).To(HaveLen(3)) - Expect(agents[0].Id).Should(BeElementOf("agent-1", "agent-2", "agent-3")) + Expect(agents[0].ID).Should(BeElementOf("agent-1", "agent-2", "agent-3")) Expect(string(agents[0].Status)).To(Equal("not-connected")) Expect(agents[0].StatusInfo).Should(BeElementOf("status-info-1", "status-info-2", "status-info-3")) - Expect(agents[0].CredentialUrl).Should(BeElementOf("cred_url-1", "cred_url-2", "cred_url-3")) - Expect(agents[1].Id).Should(BeElementOf("agent-1", "agent-2", "agent-3")) - Expect(agents[2].Id).Should(BeElementOf("agent-1", "agent-2", "agent-3")) + Expect(agents[0].CredUrl).Should(BeElementOf("cred_url-1", "cred_url-2", "cred_url-3")) + Expect(agents[1].ID).Should(BeElementOf("agent-1", "agent-2", "agent-3")) + Expect(agents[2].ID).Should(BeElementOf("agent-1", "agent-2", "agent-3")) }) It("list all the agents -- no agents to be found in the db", func() { @@ -77,7 +78,7 @@ var _ = Describe("agent store", Ordered, func() { Expect(err).To(BeNil()) Expect(agents).To(HaveLen(3)) - Expect(agents[0].Id).To(Equal("agent-1")) + Expect(agents[0].ID).To(Equal("agent-1")) }) It("successfuly list all the agents -- with options order by update_id", func() { @@ -97,7 +98,7 @@ var _ = Describe("agent store", Ordered, func() { Expect(err).To(BeNil()) Expect(agents).To(HaveLen(3)) - Expect(agents[0].Id).To(Equal("agent-2")) + Expect(agents[0].ID).To(Equal("agent-2")) }) It("successfuly list the agents -- with filter by source-id", func() { @@ -116,7 +117,7 @@ var _ = Describe("agent store", Ordered, func() { Expect(err).To(BeNil()) Expect(agents).To(HaveLen(1)) - Expect(agents[0].Id).To(Equal("agent-1")) + Expect(agents[0].ID).To(Equal("agent-1")) }) It("successfuly list the agents -- with filter by soft deletion", func() { @@ -131,7 +132,7 @@ var _ = Describe("agent store", Ordered, func() { Expect(err).To(BeNil()) Expect(agents).To(HaveLen(1)) - Expect(agents[0].Id).To(Equal("agent-1")) + Expect(agents[0].ID).To(Equal("agent-1")) }) It("successfuly list the agents -- without filter by soft deletion", func() { @@ -147,6 +148,19 @@ var _ = Describe("agent store", Ordered, func() { Expect(agents).To(HaveLen(2)) }) + It("successfuly list all the agents -- filtered by user", func() { + tx := gormdb.Exec(fmt.Sprintf(insertAgentWithUsernameStm, "agent-1", "not-connected", "status-info-1", "cred_url-1", "admin", "admin")) + Expect(tx.Error).To(BeNil()) + tx = gormdb.Exec(fmt.Sprintf(insertAgentWithUsernameStm, "agent-2", "not-connected", "status-info-1", "cred_url-1", "user", "user")) + Expect(tx.Error).To(BeNil()) + + agents, err := s.Agent().List(context.TODO(), store.NewAgentQueryFilter().ByUsername("admin"), store.NewAgentQueryOptions()) + Expect(err).To(BeNil()) + Expect(agents).To(HaveLen(1)) + + Expect(agents[0].Username).To(Equal("admin")) + }) + AfterEach(func() { gormdb.Exec("DELETE FROM agents;") gormdb.Exec("DELETE FROM sources;") @@ -155,11 +169,13 @@ var _ = Describe("agent store", Ordered, func() { Context("create", func() { It("successfuly creates an agent", func() { - agentData := apiAgent.AgentStatusUpdate{ - Id: "agent-1", - CredentialUrl: "creds-url-1", - Status: "waiting-for-credentials", - StatusInfo: "status-info-1", + agentData := model.Agent{ + ID: "agent-1", + CredUrl: "creds-url-1", + Username: "admin", + OrgID: "whatever", + Status: "waiting-for-credentials", + StatusInfo: "status-info-1", } agent, err := s.Agent().Create(context.TODO(), agentData) @@ -171,7 +187,8 @@ var _ = Describe("agent store", Ordered, func() { Expect(count).To(Equal(1)) Expect(agent).ToNot(BeNil()) - Expect(agent.Id).To(Equal("agent-1")) + Expect(agent.ID).To(Equal("agent-1")) + Expect(agent.Username).To(Equal("admin")) Expect(string(agent.Status)).To(Equal("waiting-for-credentials")) }) @@ -195,10 +212,10 @@ var _ = Describe("agent store", Ordered, func() { agent, err := s.Agent().Get(context.TODO(), "agent-1") Expect(err).To(BeNil()) - Expect(agent.Id).To(Equal("agent-1")) + Expect(agent.ID).To(Equal("agent-1")) Expect(string(agent.Status)).To(Equal("not-connected")) Expect(agent.StatusInfo).To(Equal("status-info-1")) - Expect(agent.CredentialUrl).To(Equal("cred_url-1")) + Expect(agent.CredUrl).To(Equal("cred_url-1")) }) It("successfuly return the agent connected to a source", func() { @@ -210,12 +227,12 @@ var _ = Describe("agent store", Ordered, func() { agent, err := s.Agent().Get(context.TODO(), "agent-1") Expect(err).To(BeNil()) - Expect(agent.Id).To(Equal("agent-1")) + Expect(agent.ID).To(Equal("agent-1")) Expect(string(agent.Status)).To(Equal("not-connected")) Expect(agent.StatusInfo).To(Equal("status-info-1")) - Expect(agent.CredentialUrl).To(Equal("cred_url-1")) - Expect(agent.SourceId).ToNot(BeNil()) - Expect(*agent.SourceId).To(Equal("source-1")) + Expect(agent.CredUrl).To(Equal("cred_url-1")) + Expect(agent.SourceID).ToNot(BeNil()) + Expect(*agent.SourceID).To(Equal("source-1")) }) AfterEach(func() { @@ -229,11 +246,11 @@ var _ = Describe("agent store", Ordered, func() { tx := gormdb.Exec(fmt.Sprintf(insertAgentStm, "agent-1", "not-connected", "status-info-1", "cred_url-1")) Expect(tx.Error).To(BeNil()) - agentData := apiAgent.AgentStatusUpdate{ - Id: "agent-1", - CredentialUrl: "creds-url-1", - Status: "waiting-for-credentials", - StatusInfo: "status-info-1", + agentData := model.Agent{ + ID: "agent-1", + CredUrl: "creds-url-1", + Status: "waiting-for-credentials", + StatusInfo: "status-info-1", } agent, err := s.Agent().Update(context.TODO(), agentData) @@ -250,11 +267,11 @@ var _ = Describe("agent store", Ordered, func() { tx := gormdb.Exec(fmt.Sprintf(insertAgentStm, "agent-1", "not-connected", "status-info-1", "cred_url-1")) Expect(tx.Error).To(BeNil()) - agentData := apiAgent.AgentStatusUpdate{ - Id: "agent-1", - CredentialUrl: "creds-url-1", - Status: "not-connected", - StatusInfo: "another-status-info-1", + agentData := model.Agent{ + ID: "agent-1", + CredUrl: "creds-url-1", + Status: "not-connected", + StatusInfo: "another-status-info-1", } agent, err := s.Agent().Update(context.TODO(), agentData) @@ -277,7 +294,7 @@ var _ = Describe("agent store", Ordered, func() { agent, err := s.Agent().UpdateSourceID(context.TODO(), "agent-1", "source-1", associated) Expect(err).To(BeNil()) Expect(agent).NotTo(BeNil()) - Expect(*agent.SourceId).To(Equal("source-1")) + Expect(*agent.SourceID).To(Equal("source-1")) Expect(agent.Associated).To(BeTrue()) rawSourceID := "" @@ -298,8 +315,8 @@ var _ = Describe("agent store", Ordered, func() { tx := gormdb.Exec(fmt.Sprintf(insertAgentStm, "agent-1", "not-connected", "status-info-1", "cred_url-1")) Expect(tx.Error).To(BeNil()) - agentData := apiAgent.AgentStatusUpdate{ - Id: "unknown-id", + agentData := model.Agent{ + ID: "unknown-id", } agent, err := s.Agent().Update(context.TODO(), agentData) diff --git a/internal/store/model/agent.go b/internal/store/model/agent.go index cbc154c7..b0793b31 100644 --- a/internal/store/model/agent.go +++ b/internal/store/model/agent.go @@ -3,14 +3,14 @@ package model import ( "encoding/json" - api "github.com/kubev2v/migration-planner/api/v1alpha1" - apiAgent "github.com/kubev2v/migration-planner/api/v1alpha1/agent" "gorm.io/gorm" ) type Agent struct { gorm.Model ID string `json:"id" gorm:"primaryKey"` + Username string + OrgID string Status string StatusInfo string CredUrl string @@ -30,44 +30,3 @@ func (a Agent) String() string { func NewAgentFromID(id string) *Agent { return &Agent{ID: id} } - -func NewAgentFromApiResource(resource *apiAgent.AgentStatusUpdate) *Agent { - return &Agent{ - ID: resource.Id, - Status: resource.Status, - StatusInfo: resource.StatusInfo, - CredUrl: resource.CredentialUrl, - Version: resource.Version, - } -} - -func (a *Agent) ToApiResource() api.Agent { - agent := api.Agent{ - Id: a.ID, - Status: api.StringToAgentStatus(a.Status), - StatusInfo: a.StatusInfo, - CreatedAt: a.CreatedAt, - UpdatedAt: a.UpdatedAt, - CredentialUrl: a.CredUrl, - Version: a.Version, - Associated: a.Associated, - } - - if a.DeletedAt.Valid { - agent.DeletedAt = &a.DeletedAt.Time - } - - if a.SourceID != nil { - agent.SourceId = a.SourceID - } - - return agent -} - -func (al AgentList) ToApiResource() api.AgentList { - agentList := make([]api.Agent, len(al)) - for i, agent := range al { - agentList[i] = agent.ToApiResource() - } - return agentList -} diff --git a/internal/store/model/source.go b/internal/store/model/source.go index 1cc66848..14331caf 100644 --- a/internal/store/model/source.go +++ b/internal/store/model/source.go @@ -4,7 +4,6 @@ import ( "encoding/json" "time" - "github.com/google/uuid" api "github.com/kubev2v/migration-planner/api/v1alpha1" openapi_types "github.com/oapi-codegen/runtime/types" "gorm.io/gorm" @@ -12,6 +11,8 @@ import ( type Source struct { ID openapi_types.UUID `json:"id" gorm:"primaryKey"` + Username string + OrgID string CreatedAt time.Time UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` @@ -25,42 +26,3 @@ func (s Source) String() string { val, _ := json.Marshal(s) return string(val) } - -func NewSourceFromApiCreateResource(id uuid.UUID) *Source { - return &Source{ID: id} -} - -func NewSourceFromId(id uuid.UUID) *Source { - s := Source{ID: id} - return &s -} - -func (s *Source) ToApiResource() api.Source { - source := api.Source{ - Id: s.ID, - Inventory: nil, - CreatedAt: s.CreatedAt, - UpdatedAt: s.UpdatedAt, - } - - if len(s.Agents) > 0 { - agents := make([]api.SourceAgentItem, 0, len(s.Agents)) - for _, a := range s.Agents { - agents = append(agents, api.SourceAgentItem{Id: uuid.MustParse(a.ID), Associated: a.Associated}) - } - source.Agents = &agents - } - - if s.Inventory != nil { - source.Inventory = &s.Inventory.Data - } - return source -} - -func (sl SourceList) ToApiResource() api.SourceList { - sourceList := make([]api.Source, len(sl)) - for i, source := range sl { - sourceList[i] = source.ToApiResource() - } - return sourceList -} diff --git a/internal/store/options.go b/internal/store/options.go new file mode 100644 index 00000000..bffe1cc5 --- /dev/null +++ b/internal/store/options.go @@ -0,0 +1,103 @@ +package store + +import "gorm.io/gorm" + +type BaseQuerier struct { + QueryFn []func(tx *gorm.DB) *gorm.DB +} + +type AgentQueryFilter BaseQuerier + +func NewAgentQueryFilter() *AgentQueryFilter { + return &AgentQueryFilter{QueryFn: make([]func(tx *gorm.DB) *gorm.DB, 0)} +} + +func (qf *AgentQueryFilter) BySourceID(sourceID string) *AgentQueryFilter { + qf.QueryFn = append(qf.QueryFn, func(tx *gorm.DB) *gorm.DB { + return tx.Where("source_id = ?", sourceID) + }) + return qf +} + +func (qf *AgentQueryFilter) ByUsername(username string) *AgentQueryFilter { + qf.QueryFn = append(qf.QueryFn, func(tx *gorm.DB) *gorm.DB { + return tx.Where("username = ?", username) + }) + return qf +} + +func (qf *AgentQueryFilter) ByOrgID(id string) *AgentQueryFilter { + qf.QueryFn = append(qf.QueryFn, func(tx *gorm.DB) *gorm.DB { + return tx.Where("org_id = ?", id) + }) + return qf +} + +func (qf *AgentQueryFilter) BySoftDeleted(isSoftDeleted bool) *AgentQueryFilter { + qf.QueryFn = append(qf.QueryFn, func(tx *gorm.DB) *gorm.DB { + if isSoftDeleted { + return tx.Unscoped().Where("deleted_at IS NOT NULL") + } + return tx + }) + return qf +} + +type AgentQueryOptions BaseQuerier + +func NewAgentQueryOptions() *AgentQueryOptions { + return &AgentQueryOptions{QueryFn: make([]func(tx *gorm.DB) *gorm.DB, 0)} +} + +func (o *AgentQueryOptions) WithIncludeSoftDeleted(includeSoftDeleted bool) *AgentQueryOptions { + o.QueryFn = append(o.QueryFn, func(tx *gorm.DB) *gorm.DB { + if includeSoftDeleted { + return tx.Unscoped() + } + return tx + }) + return o +} + +func (qf *AgentQueryFilter) ByID(ids []string) *AgentQueryFilter { + qf.QueryFn = append(qf.QueryFn, func(tx *gorm.DB) *gorm.DB { + return tx.Where("id IN ?", ids) + }) + return qf +} + +func (o *AgentQueryOptions) WithSortOrder(sort SortOrder) *AgentQueryOptions { + o.QueryFn = append(o.QueryFn, func(tx *gorm.DB) *gorm.DB { + switch sort { + case SortByID: + return tx.Order("id") + case SortByUpdatedTime: + return tx.Order("updated_at") + case SortByCreatedTime: + return tx.Order("created_at") + default: + return tx + } + }) + return o +} + +type SourceQueryFilter BaseQuerier + +func NewSourceQueryFilter() *SourceQueryFilter { + return &SourceQueryFilter{QueryFn: make([]func(tx *gorm.DB) *gorm.DB, 0)} +} + +func (sf *SourceQueryFilter) ByUsername(username string) *SourceQueryFilter { + sf.QueryFn = append(sf.QueryFn, func(tx *gorm.DB) *gorm.DB { + return tx.Where("username = ?", username) + }) + return sf +} + +func (sf *SourceQueryFilter) ByOrgID(id string) *SourceQueryFilter { + sf.QueryFn = append(sf.QueryFn, func(tx *gorm.DB) *gorm.DB { + return tx.Where("org_id = ?", id) + }) + return sf +} diff --git a/internal/store/source.go b/internal/store/source.go index 5445627d..41a519f5 100644 --- a/internal/store/source.go +++ b/internal/store/source.go @@ -5,7 +5,6 @@ import ( "errors" "github.com/google/uuid" - api "github.com/kubev2v/migration-planner/api/v1alpha1" "github.com/kubev2v/migration-planner/internal/store/model" "go.uber.org/zap" "gorm.io/gorm" @@ -17,12 +16,12 @@ var ( ) type Source interface { - List(ctx context.Context) (api.SourceList, error) - Create(ctx context.Context, id uuid.UUID) (*api.Source, error) + List(ctx context.Context, filter *SourceQueryFilter) (model.SourceList, error) + Create(ctx context.Context, source model.Source) (*model.Source, error) DeleteAll(ctx context.Context) error - Get(ctx context.Context, id uuid.UUID) (*api.Source, error) + Get(ctx context.Context, id uuid.UUID) (*model.Source, error) Delete(ctx context.Context, id uuid.UUID) error - Update(ctx context.Context, id uuid.UUID, inventory *api.Inventory) (*api.Source, error) + Update(ctx context.Context, source model.Source) (*model.Source, error) InitialMigration(context.Context) error } @@ -41,23 +40,29 @@ func (s *SourceStore) InitialMigration(ctx context.Context) error { return s.getDB(ctx).AutoMigrate(&model.Source{}) } -func (s *SourceStore) List(ctx context.Context) (api.SourceList, error) { +func (s *SourceStore) List(ctx context.Context, filter *SourceQueryFilter) (model.SourceList, error) { var sources model.SourceList - result := s.getDB(ctx).Model(&sources).Order("id").Preload("Agents").Find(&sources) + tx := s.getDB(ctx).Model(&sources).Order("id").Preload("Agents") + + if filter != nil { + for _, fn := range filter.QueryFn { + tx = fn(tx) + } + } + + result := tx.Find(&sources) if result.Error != nil { return nil, result.Error } - return sources.ToApiResource(), nil + return sources, nil } -func (s *SourceStore) Create(ctx context.Context, id uuid.UUID) (*api.Source, error) { - source := model.NewSourceFromApiCreateResource(id) - result := s.getDB(ctx).Create(source) +func (s *SourceStore) Create(ctx context.Context, source model.Source) (*model.Source, error) { + result := s.getDB(ctx).Create(&source) if result.Error != nil { return nil, result.Error } - createdResource := source.ToApiResource() - return &createdResource, nil + return &source, nil } func (s *SourceStore) DeleteAll(ctx context.Context) error { @@ -65,8 +70,8 @@ func (s *SourceStore) DeleteAll(ctx context.Context) error { return result.Error } -func (s *SourceStore) Get(ctx context.Context, id uuid.UUID) (*api.Source, error) { - source := model.NewSourceFromId(id) +func (s *SourceStore) Get(ctx context.Context, id uuid.UUID) (*model.Source, error) { + source := model.Source{ID: id} result := s.getDB(ctx).Preload("Agents").First(&source) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { @@ -74,12 +79,11 @@ func (s *SourceStore) Get(ctx context.Context, id uuid.UUID) (*api.Source, error } return nil, result.Error } - apiSource := source.ToApiResource() - return &apiSource, nil + return &source, nil } func (s *SourceStore) Delete(ctx context.Context, id uuid.UUID) error { - source := model.NewSourceFromId(id) + source := model.Source{ID: id} result := s.getDB(ctx).Unscoped().Delete(&source) if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) { zap.S().Named("source_store").Infof("ERROR: %v", result.Error) @@ -88,20 +92,17 @@ func (s *SourceStore) Delete(ctx context.Context, id uuid.UUID) error { return nil } -func (s *SourceStore) Update(ctx context.Context, id uuid.UUID, inventory *api.Inventory) (*api.Source, error) { - source := model.NewSourceFromId(id) +func (s *SourceStore) Update(ctx context.Context, source model.Source) (*model.Source, error) { selectFields := []string{} - if inventory != nil { - source.Inventory = model.MakeJSONField(*inventory) + if source.Inventory != nil { selectFields = append(selectFields, "inventory") } - result := s.getDB(ctx).Model(source).Clauses(clause.Returning{}).Select(selectFields).Updates(&source) + result := s.getDB(ctx).Model(&source).Clauses(clause.Returning{}).Select(selectFields).Updates(&source) if result.Error != nil { return nil, result.Error } - apiSource := source.ToApiResource() - return &apiSource, nil + return &source, nil } func (s *SourceStore) getDB(ctx context.Context) *gorm.DB { diff --git a/internal/store/source_test.go b/internal/store/source_test.go index 791fc7f8..b27fd6f2 100644 --- a/internal/store/source_test.go +++ b/internal/store/source_test.go @@ -7,13 +7,15 @@ import ( "github.com/google/uuid" "github.com/kubev2v/migration-planner/internal/config" "github.com/kubev2v/migration-planner/internal/store" + "github.com/kubev2v/migration-planner/internal/store/model" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "gorm.io/gorm" ) const ( - insertSourceStm = "INSERT INTO sources (id) VALUES ('%s');" + insertSourceStm = "INSERT INTO sources (id) VALUES ('%s');" + insertSourceWithUsernameStm = "INSERT INTO sources (id, username, org_id) VALUES ('%s', '%s', '%s');" ) var _ = Describe("source store", Ordered, func() { @@ -41,7 +43,7 @@ var _ = Describe("source store", Ordered, func() { tx = gormdb.Exec(fmt.Sprintf(insertSourceStm, uuid.NewString())) Expect(tx.Error).To(BeNil()) - sources, err := s.Source().List(context.TODO()) + sources, err := s.Source().List(context.TODO(), store.NewSourceQueryFilter()) Expect(err).To(BeNil()) Expect(sources).To(HaveLen(2)) }) @@ -56,20 +58,32 @@ var _ = Describe("source store", Ordered, func() { tx = gormdb.Exec(fmt.Sprintf("UPDATE agents set source_id = '%s';", sourceID)) Expect(tx.Error).To(BeNil()) - sources, err := s.Source().List(context.TODO()) + sources, err := s.Source().List(context.TODO(), store.NewSourceQueryFilter()) Expect(err).To(BeNil()) Expect(sources).To(HaveLen(1)) - agents := *sources[0].Agents + agents := sources[0].Agents Expect(agents).To(HaveLen(1)) - Expect(agents[0].Id.String()).To(Equal(agentID)) + Expect(agents[0].ID).To(Equal(agentID)) }) It("list all sources -- no sources", func() { - sources, err := s.Source().List(context.TODO()) + sources, err := s.Source().List(context.TODO(), store.NewSourceQueryFilter()) Expect(err).To(BeNil()) Expect(sources).To(HaveLen(0)) }) + It("successfully list the user's sources", func() { + tx := gormdb.Exec(fmt.Sprintf(insertSourceWithUsernameStm, uuid.NewString(), "admin", "admin")) + Expect(tx.Error).To(BeNil()) + tx = gormdb.Exec(fmt.Sprintf(insertSourceWithUsernameStm, uuid.NewString(), "user", "user")) + Expect(tx.Error).To(BeNil()) + + sources, err := s.Source().List(context.TODO(), store.NewSourceQueryFilter().ByUsername("admin")) + Expect(err).To(BeNil()) + Expect(sources).To(HaveLen(1)) + Expect(sources[0].Username).To(Equal("admin")) + }) + AfterEach(func() { gormdb.Exec("DELETE from agents;") gormdb.Exec("DELETE from sources;") @@ -109,7 +123,12 @@ var _ = Describe("source store", Ordered, func() { Context("create", func() { It("successfully creates one source", func() { sourceID := uuid.New() - source, err := s.Source().Create(context.TODO(), sourceID) + m := model.Source{ + ID: sourceID, + Username: "admin", + OrgID: "org", + } + source, err := s.Source().Create(context.TODO(), m) Expect(err).To(BeNil()) Expect(source).NotTo(BeNil()) @@ -121,7 +140,12 @@ var _ = Describe("source store", Ordered, func() { It("successfully creates one source without sshkey", func() { sourceID := uuid.New() - source, err := s.Source().Create(context.TODO(), sourceID) + m := model.Source{ + ID: sourceID, + Username: "admin", + OrgID: "org", + } + source, err := s.Source().Create(context.TODO(), m) Expect(err).To(BeNil()) Expect(source).NotTo(BeNil()) diff --git a/internal/store/store_test.go b/internal/store/store_test.go index 04743d3d..c1868003 100644 --- a/internal/store/store_test.go +++ b/internal/store/store_test.go @@ -6,6 +6,7 @@ import ( "github.com/google/uuid" "github.com/kubev2v/migration-planner/internal/config" st "github.com/kubev2v/migration-planner/internal/store" + "github.com/kubev2v/migration-planner/internal/store/model" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "gorm.io/gorm" @@ -36,7 +37,13 @@ var _ = Describe("Store", Ordered, func() { ctx, err := store.NewTransactionContext(context.TODO()) Expect(err).To(BeNil()) - source, err := store.Source().Create(ctx, uuid.New()) + sourceID := uuid.New() + m := model.Source{ + ID: sourceID, + Username: "admin", + OrgID: "org", + } + source, err := store.Source().Create(ctx, m) Expect(source).ToNot(BeNil()) Expect(err).To(BeNil()) @@ -54,12 +61,17 @@ var _ = Describe("Store", Ordered, func() { ctx, err := store.NewTransactionContext(context.TODO()) Expect(err).To(BeNil()) - source, err := store.Source().Create(ctx, uuid.New()) + m := model.Source{ + ID: uuid.New(), + Username: "admin", + OrgID: "org", + } + source, err := store.Source().Create(ctx, m) Expect(source).ToNot(BeNil()) Expect(err).To(BeNil()) // count in the same transaction - sources, err := store.Source().List(ctx) + sources, err := store.Source().List(ctx, st.NewSourceQueryFilter()) Expect(err).To(BeNil()) Expect(sources).NotTo(BeNil()) Expect(sources).To(HaveLen(1))