mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CloudMigrations: Introduce RBAC role for migration assistant (#98588)
* CloudMigrations: delete unused code * CloudMigrations: add access control and protect API + navtree with action * CloudMigrations: register access control roles * CloudMigrations: gate frontend based with access control * CloudMigrations: fix api tests * CloudMigrations: add docs on new actions and roles * CloudMigrations: dont interpolate vars to make it more greppable * CloudMigrations: run prettier
This commit is contained in:
parent
79d8201b49
commit
3958fb9e0a
@ -114,6 +114,7 @@ The following list contains role-based access control actions.
|
||||
| `licensing:delete` | None | Delete the license token. |
|
||||
| `licensing:read` | None | Read licensing information. |
|
||||
| `licensing:write` | None | Update the license token. |
|
||||
| `migrationassistant:migrate` | None | Execute on-prem to cloud migrations through the Migration Assistant. |
|
||||
| `org.users:write` | <ul><li>`users:*`</li><li>`users:id:*`</li></ul> | Update the organization role (`None`, `Viewer`, `Editor`, or `Admin`) of a user. |
|
||||
| `org.users:add` | <ul><li>`users:*`</li><li>`users:id:*`</li></ul> | Add a user to an organization or invite a new user to an organization. |
|
||||
| `org.users:read` | <ul><li>`users:*`</li><li>`users:id:*`</li></ul> | Get user profiles within an organization. |
|
||||
|
@ -56,7 +56,7 @@ The following tables list permissions associated with basic and fixed roles.
|
||||
|
||||
| Basic role | UID | Associated fixed roles | Description |
|
||||
| ------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Grafana Admin | `basic_grafana_admin` | `fixed:roles:reader`<br>`fixed:roles:writer`<br>`fixed:users:reader`<br>`fixed:users:writer`<br>`fixed:org.users:reader`<br>`fixed:org.users:writer`<br>`fixed:ldap:reader`<br>`fixed:ldap:writer`<br>`fixed:stats:reader`<br>`fixed:settings:reader`<br>`fixed:settings:writer`<br>`fixed:provisioning:writer`<br>`fixed:organization:reader`<br>`fixed:organization:maintainer`<br>`fixed:licensing:reader`<br>`fixed:licensing:writer`<br>`fixed:datasources.caching:reader`<br>`fixed:datasources.caching:writer`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:plugins:maintainer`<br>`fixed:authentication.config:writer`<br>`fixed:library.panels:creator`<br>`fixed:library.panels:reader`<br>`fixed:library.panels:general.reader`<br>`fixed:library.panels:writer`<br>`fixed:library.panels:general.writer`<br>`fixed:groupsync:writer` | Default [Grafana server administrator](/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/#grafana-server-administrators) assignments. |
|
||||
| Grafana Admin | `basic_grafana_admin` | `fixed:roles:reader`<br>`fixed:roles:writer`<br>`fixed:users:reader`<br>`fixed:users:writer`<br>`fixed:org.users:reader`<br>`fixed:org.users:writer`<br>`fixed:ldap:reader`<br>`fixed:ldap:writer`<br>`fixed:stats:reader`<br>`fixed:settings:reader`<br>`fixed:settings:writer`<br>`fixed:provisioning:writer`<br>`fixed:organization:reader`<br>`fixed:organization:maintainer`<br>`fixed:licensing:reader`<br>`fixed:licensing:writer`<br>`fixed:datasources.caching:reader`<br>`fixed:datasources.caching:writer`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:plugins:maintainer`<br>`fixed:authentication.config:writer`<br>`fixed:library.panels:creator`<br>`fixed:library.panels:reader`<br>`fixed:library.panels:general.reader`<br>`fixed:library.panels:writer`<br>`fixed:library.panels:general.writer`<br>`fixed:groupsync:writer`<br>`fixed:migrationassistant:migrator` | Default [Grafana server administrator](/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/#grafana-server-administrators) assignments. |
|
||||
| Admin | `basic_admin` | `fixed:reports:reader`<br>`fixed:reports:writer`<br>`fixed:datasources:reader`<br>`fixed:datasources:writer`<br>`fixed:organization:writer`<br>`fixed:datasources.permissions:reader`<br>`fixed:datasources.permissions:writer`<br>`fixed:teams:writer`<br>`fixed:dashboards:reader`<br>`fixed:dashboards:writer`<br>`fixed:dashboards.permissions:reader`<br>`fixed:dashboards.permissions:writer`<br>`fixed:dashboards.public:writer`<br>`fixed:folders:reader`<br>`fixed:folders:writer`<br>`fixed:folders.permissions:reader`<br>`fixed:folders.permissions:writer`<br>`fixed:alerting:writer`<br>`fixed:apikeys:reader`<br>`fixed:apikeys:writer`<br>`fixed:alerting.provisioning.secrets:reader`<br>`fixed:alerting.provisioning:writer`<br>`fixed:datasources.caching:reader`<br>`fixed:datasources.caching:writer`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:plugins:writer`<br>`fixed:library.panels:creator`<br>`fixed:library.panels:reader`<br>`fixed:library.panels:general.reader`<br>`fixed:library.panels:writer`<br>`fixed:library.panels:general.writer`<br>`fixed:alerting.provisioning.status:writer`<br>`fixed:groupsync:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. |
|
||||
| Editor | `basic_editor` | `fixed:datasources:explorer`<br>`fixed:dashboards:creator`<br>`fixed:folders:creator`<br>`fixed:annotations:writer`<br>`fixed:teams:creator` if the `editors_can_admin` configuration flag is enabled<br>`fixed:alerting:writer`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:library.panels:creator`<br>`fixed:library.panels:general.reader`<br>`fixed:library.panels:general.writer`<br>`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. |
|
||||
| Viewer | `basic_viewer` | `fixed:datasources.id:reader`<br>`fixed:organization:reader`<br>`fixed:annotations:reader`<br>`fixed:annotations.dashboard:writer`<br>`fixed:alerting:reader`<br>`fixed:plugins.app:reader`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:library.panels:general.reader`<br>`fixed:datasources:explorer` if the `viewers_can_edit` configuration flag is enabled | Default [Viewer](ref:rbac-basic-roles) assignments. |
|
||||
@ -125,6 +125,7 @@ To learn how to use the roles API to determine the role UUIDs, refer to [Manage
|
||||
| `fixed:library.panels:writer` | `fixed_JTljAr21LWLTXCkgfBC4H0lhBC8` | All permissions from `fixed:library.panels:reader` plus<br>`library.panels:create`<br>`library.panels:delete`<br>`library.panels:write` | Create, read, write or delete all library panels and their permissions. |
|
||||
| `fixed:licensing:reader` | `fixed_OADpuXvNEylO2Kelu3GIuBXEAYE` | `licensing:read`<br>`licensing.reports:read` | Read licensing information and licensing reports. |
|
||||
| `fixed:licensing:writer` | `fixed_gzbz3rJpQMdaKHt-E4q0PVaKMoE` | All permissions from `fixed:licensing:viewer` and <br>`licensing:write`<br>`licensing:delete` | Read licensing information and licensing reports, update and delete the license token. |
|
||||
| `fixed:migrationassistant:migrator` | `fixed_LLk2p7TRuBztOAksTQb1Klc8YTk` | `migrationassistant:migrate` | Execute on-prem to cloud migrations through the Migration Assistant. |
|
||||
| `fixed:org.users:reader` | `fixed_oCqNwlVHLOpw7-jAlwp4HzYqwGY` | `org.users:read` | Read users within a single organization. |
|
||||
| `fixed:org.users:writer` | `fixed_VERj5nayasjgf_Yh0sWqqCkxWlw` | All permissions from `fixed:org.users:reader` and <br>`org.users:add`<br>`org.users:remove`<br>`org.users:write` | Within a single organization, add a user, invite a new user, read information about a user and their role, remove a user from that organization, or change the role of a user. |
|
||||
| `fixed:organization:maintainer` | `fixed_CMm-uuBaPUBf4r8XG3jIvxo55bg` | All permissions from `fixed:organization:reader` and <br> `orgs:write`<br>`orgs:create`<br>`orgs:delete`<br>`orgs.quotas:write` | Create, read, write, or delete an organization. Read or write its quotas. This role needs to be assigned globally. |
|
||||
|
@ -42,6 +42,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/ssoutils"
|
||||
"github.com/grafana/grafana/pkg/services/apikey"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/correlations"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
@ -117,7 +118,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
r.Get("/admin/stats", authorize(ac.EvalPermission(ac.ActionServerStatsRead)), hs.Index)
|
||||
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagOnPremToCloudMigrations) {
|
||||
r.Get("/admin/migrate-to-cloud", reqOrgAdmin, hs.Index)
|
||||
r.Get("/admin/migrate-to-cloud", authorize(cloudmigration.MigrationAssistantAccess), hs.Index)
|
||||
}
|
||||
|
||||
// feature toggle admin page
|
||||
|
31
pkg/services/cloudmigration/accesscontrol.go
Normal file
31
pkg/services/cloudmigration/accesscontrol.go
Normal file
@ -0,0 +1,31 @@
|
||||
package cloudmigration
|
||||
|
||||
import "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
|
||||
const (
|
||||
ActionMigrate = "migrationassistant:migrate"
|
||||
)
|
||||
|
||||
var (
|
||||
// MigrationAssistantAccess is used to protect the "Migrate to Grafana Cloud" page.
|
||||
MigrationAssistantAccess = accesscontrol.EvalPermission(ActionMigrate)
|
||||
)
|
||||
|
||||
func RegisterAccessControlRoles(service accesscontrol.Service) error {
|
||||
migrator := accesscontrol.RoleRegistration{
|
||||
Role: accesscontrol.RoleDTO{
|
||||
Name: "fixed:migrationassistant:migrator",
|
||||
DisplayName: "Organization resource migrator",
|
||||
Description: "Migrate organization resources.",
|
||||
Group: "Migration Assistant",
|
||||
Permissions: []accesscontrol.Permission{
|
||||
{
|
||||
Action: ActionMigrate,
|
||||
},
|
||||
},
|
||||
},
|
||||
Grants: []string{string(accesscontrol.RoleGrafanaAdmin)},
|
||||
}
|
||||
|
||||
return service.DeclareFixedRoles(migrator)
|
||||
}
|
@ -8,7 +8,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@ -28,6 +28,7 @@ func RegisterApi(
|
||||
rr routing.RouteRegister,
|
||||
cms cloudmigration.Service,
|
||||
tracer tracing.Tracer,
|
||||
acHandler accesscontrol.AccessControl,
|
||||
) *CloudMigrationAPI {
|
||||
api := &CloudMigrationAPI{
|
||||
log: log.New("cloudmigrations.api"),
|
||||
@ -35,12 +36,14 @@ func RegisterApi(
|
||||
cloudMigrationService: cms,
|
||||
tracer: tracer,
|
||||
}
|
||||
api.registerEndpoints()
|
||||
api.registerEndpoints(acHandler)
|
||||
return api
|
||||
}
|
||||
|
||||
// registerEndpoints Registers Endpoints on Grafana Router
|
||||
func (cma *CloudMigrationAPI) registerEndpoints() {
|
||||
func (cma *CloudMigrationAPI) registerEndpoints(acHandler accesscontrol.AccessControl) {
|
||||
authorize := accesscontrol.Middleware(acHandler)
|
||||
|
||||
cma.routeRegister.Group("/api/cloudmigration", func(cloudMigrationRoute routing.RouteRegister) {
|
||||
// destination instance endpoints for token management
|
||||
cloudMigrationRoute.Get("/token", routing.Wrap(cma.GetToken))
|
||||
@ -59,7 +62,7 @@ func (cma *CloudMigrationAPI) registerEndpoints() {
|
||||
cloudMigrationRoute.Get("/migration/:uid/snapshots", routing.Wrap(cma.GetSnapshotList))
|
||||
cloudMigrationRoute.Post("/migration/:uid/snapshot/:snapshotUid/upload", routing.Wrap(cma.UploadSnapshot))
|
||||
cloudMigrationRoute.Post("/migration/:uid/snapshot/:snapshotUid/cancel", routing.Wrap(cma.CancelSnapshot))
|
||||
}, middleware.ReqOrgAdmin)
|
||||
}, authorize(cloudmigration.MigrationAssistantAccess))
|
||||
}
|
||||
|
||||
// swagger:route GET /cloudmigration/token migrations getCloudMigrationToken
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
||||
"github.com/grafana/grafana/pkg/services/cloudmigration/cloudmigrationimpl/fake"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
@ -20,36 +22,55 @@ type TestCase struct {
|
||||
requestHttpMethod string
|
||||
requestUrl string
|
||||
requestBody string
|
||||
basicRole org.RoleType
|
||||
user *user.SignedInUser
|
||||
// if the CloudMigrationService should return an error
|
||||
serviceReturnError bool
|
||||
expectedHttpResult int
|
||||
expectedBody string
|
||||
}
|
||||
|
||||
var (
|
||||
orgID int64 = 1
|
||||
|
||||
userWithPermissions = &user.SignedInUser{
|
||||
OrgID: orgID,
|
||||
OrgRole: org.RoleEditor,
|
||||
Permissions: map[int64]map[string][]string{
|
||||
orgID: {cloudmigration.ActionMigrate: nil},
|
||||
},
|
||||
}
|
||||
|
||||
userWithoutPermissions = &user.SignedInUser{
|
||||
OrgID: orgID,
|
||||
OrgRole: org.RoleAdmin,
|
||||
IsGrafanaAdmin: true,
|
||||
Permissions: map[int64]map[string][]string{},
|
||||
}
|
||||
)
|
||||
|
||||
func TestCloudMigrationAPI_GetToken(t *testing.T) {
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "should return 200 if everything is ok",
|
||||
desc: "returns 200 if the user has the right permissions",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/token",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusOK,
|
||||
expectedBody: `{"id":"mock_id","displayName":"mock_name","expiresAt":"","firstUsedAt":"","lastUsedAt":"","createdAt":""}`,
|
||||
},
|
||||
{
|
||||
desc: "should return 403 if no used is not admin",
|
||||
desc: "returns 403 if the user does not have the right permissions",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/token",
|
||||
basicRole: org.RoleEditor,
|
||||
user: userWithoutPermissions,
|
||||
expectedHttpResult: http.StatusForbidden,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 500 if service returns an error",
|
||||
desc: "returns 500 if service returns an error",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/token",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusInternalServerError,
|
||||
expectedBody: "",
|
||||
@ -64,26 +85,26 @@ func TestCloudMigrationAPI_GetToken(t *testing.T) {
|
||||
func TestCloudMigrationAPI_CreateToken(t *testing.T) {
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "should return 200 if everything is ok",
|
||||
desc: "returns 200 if the user has the right permissions",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/token",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusOK,
|
||||
expectedBody: `{"token":"mock_token"}`,
|
||||
},
|
||||
{
|
||||
desc: "should return 403 if no used is not admin",
|
||||
desc: "returns 403 if the user does not have the right permissions",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/token",
|
||||
basicRole: org.RoleEditor,
|
||||
user: userWithoutPermissions,
|
||||
expectedHttpResult: http.StatusForbidden,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 500 if service returns an error",
|
||||
desc: "returns 500 if service returns an error",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/token",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusInternalServerError,
|
||||
expectedBody: "",
|
||||
@ -98,32 +119,32 @@ func TestCloudMigrationAPI_CreateToken(t *testing.T) {
|
||||
func TestCloudMigrationAPI_DeleteToken(t *testing.T) {
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "should return 200 if everything is ok",
|
||||
desc: "returns 200 if the user has the right permissions",
|
||||
requestHttpMethod: http.MethodDelete,
|
||||
requestUrl: "/api/cloudmigration/token/1234",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusNoContent,
|
||||
},
|
||||
{
|
||||
desc: "should return 403 if no used is not admin",
|
||||
desc: "returns 403 if the user does not have the right permissions",
|
||||
requestHttpMethod: http.MethodDelete,
|
||||
requestUrl: "/api/cloudmigration/token/1234",
|
||||
basicRole: org.RoleEditor,
|
||||
user: userWithoutPermissions,
|
||||
expectedHttpResult: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
desc: "should return 500 if service returns an error",
|
||||
desc: "returns 500 if service returns an error",
|
||||
requestHttpMethod: http.MethodDelete,
|
||||
requestUrl: "/api/cloudmigration/token/1234",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusInternalServerError,
|
||||
},
|
||||
{
|
||||
desc: "should return 400 if uid is invalid",
|
||||
desc: "returns 400 if uid is invalid",
|
||||
requestHttpMethod: http.MethodDelete,
|
||||
requestUrl: "/api/cloudmigration/token/***",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusBadRequest,
|
||||
},
|
||||
@ -137,32 +158,32 @@ func TestCloudMigrationAPI_DeleteToken(t *testing.T) {
|
||||
func TestCloudMigrationAPI_GetMigration(t *testing.T) {
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "should return 200 if everything is ok",
|
||||
desc: "returns 200 if the user has the right permissions",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/1234",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "should return 403 if no used is not admin",
|
||||
desc: "returns 403 if the user does not have the right permissions",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/1234",
|
||||
basicRole: org.RoleEditor,
|
||||
user: userWithoutPermissions,
|
||||
expectedHttpResult: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
desc: "should return 500 if service returns an error",
|
||||
desc: "returns 500 if service returns an error",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/1234",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
desc: "should return 400 if uid is invalid",
|
||||
desc: "returns 400 if uid is invalid",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/****",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusBadRequest,
|
||||
},
|
||||
@ -176,26 +197,26 @@ func TestCloudMigrationAPI_GetMigration(t *testing.T) {
|
||||
func TestCloudMigrationAPI_GetMigrationList(t *testing.T) {
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "should return 200 if everything is ok",
|
||||
desc: "returns 200 if the user has the right permissions",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusOK,
|
||||
expectedBody: `{"sessions":[{"uid":"mock_uid_1","slug":"mock_stack_1","created":"2024-06-05T17:30:40Z","updated":"2024-06-05T17:30:40Z"},{"uid":"mock_uid_2","slug":"mock_stack_2","created":"2024-06-05T17:30:40Z","updated":"2024-06-05T17:30:40Z"}]}`,
|
||||
},
|
||||
{
|
||||
desc: "should return 403 if no used is not admin",
|
||||
desc: "returns 403 if the user does not have the right permissions",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration",
|
||||
basicRole: org.RoleEditor,
|
||||
user: userWithoutPermissions,
|
||||
expectedHttpResult: http.StatusForbidden,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 500 if service returns an error",
|
||||
desc: "returns 500 if service returns an error",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusInternalServerError,
|
||||
expectedBody: "",
|
||||
@ -210,38 +231,38 @@ func TestCloudMigrationAPI_GetMigrationList(t *testing.T) {
|
||||
func TestCloudMigrationAPI_CreateMigration(t *testing.T) {
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "should return 200 if everything is ok",
|
||||
desc: "returns 200 if the user has the right permissions",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration",
|
||||
requestBody: `{"auth_token":"asdf"}`,
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusOK,
|
||||
expectedBody: `{"uid":"fake_uid","slug":"fake_stack","created":"2024-06-05T17:30:40Z","updated":"2024-06-05T17:30:40Z"}`,
|
||||
},
|
||||
{
|
||||
desc: "should return 403 if no used is not admin",
|
||||
desc: "returns 403 if the user does not have the right permissions",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration",
|
||||
basicRole: org.RoleEditor,
|
||||
user: userWithoutPermissions,
|
||||
expectedHttpResult: http.StatusForbidden,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 500 if service returns an error",
|
||||
desc: "returns 500 if service returns an error",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration",
|
||||
requestBody: `{"authToken":"asdf"}`,
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusInternalServerError,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 400 if body is not a valid json",
|
||||
desc: "returns 400 if body is not a valid json",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration",
|
||||
requestBody: "asdf",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: false,
|
||||
expectedHttpResult: http.StatusBadRequest,
|
||||
expectedBody: "",
|
||||
@ -256,35 +277,35 @@ func TestCloudMigrationAPI_CreateMigration(t *testing.T) {
|
||||
func TestCloudMigrationAPI_DeleteMigration(t *testing.T) {
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "should return 200 if everything is ok",
|
||||
desc: "returns 200 if the user has the right permissions",
|
||||
requestHttpMethod: http.MethodDelete,
|
||||
requestUrl: "/api/cloudmigration/migration/1234",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusOK,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 403 if no used is not admin",
|
||||
desc: "returns 403 if the user does not have the right permissions",
|
||||
requestHttpMethod: http.MethodDelete,
|
||||
requestUrl: "/api/cloudmigration/migration/1234",
|
||||
basicRole: org.RoleEditor,
|
||||
user: userWithoutPermissions,
|
||||
expectedHttpResult: http.StatusForbidden,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 500 if service returns an error",
|
||||
desc: "returns 500 if service returns an error",
|
||||
requestHttpMethod: http.MethodDelete,
|
||||
requestUrl: "/api/cloudmigration/migration/1234",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusInternalServerError,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 400 if uid is invalid",
|
||||
desc: "returns 400 if uid is invalid",
|
||||
requestHttpMethod: http.MethodDelete,
|
||||
requestUrl: "/api/cloudmigration/migration/****",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusBadRequest,
|
||||
},
|
||||
@ -298,35 +319,35 @@ func TestCloudMigrationAPI_DeleteMigration(t *testing.T) {
|
||||
func TestCloudMigrationAPI_CreateSnapshot(t *testing.T) {
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "should return 200 if everything is ok",
|
||||
desc: "returns 200 if the user has the right permissions",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusOK,
|
||||
expectedBody: `{"uid":"fake_uid"}`,
|
||||
},
|
||||
{
|
||||
desc: "should return 403 if no used is not admin",
|
||||
desc: "returns 403 if the user does not have the right permissions",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot",
|
||||
basicRole: org.RoleEditor,
|
||||
user: userWithoutPermissions,
|
||||
expectedHttpResult: http.StatusForbidden,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 500 if service returns an error",
|
||||
desc: "returns 500 if service returns an error",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusInternalServerError,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 400 if uid is invalid",
|
||||
desc: "returns 400 if uid is invalid",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/***/snapshot",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusBadRequest,
|
||||
},
|
||||
@ -340,43 +361,43 @@ func TestCloudMigrationAPI_CreateSnapshot(t *testing.T) {
|
||||
func TestCloudMigrationAPI_GetSnapshot(t *testing.T) {
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "should return 200 if everything is ok",
|
||||
desc: "returns 200 if the user has the right permissions",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot/1",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusOK,
|
||||
expectedBody: `{"uid":"fake_uid","status":"CREATING","sessionUid":"1234","created":"0001-01-01T00:00:00Z","finished":"0001-01-01T00:00:00Z","results":[{"name":"dashboard name","parentName":"dashboard parent name","type":"DASHBOARD","refId":"123","status":"PENDING"},{"name":"datasource name","parentName":"dashboard parent name","type":"DATASOURCE","refId":"456","status":"OK"}],"stats":{"types":{},"statuses":{},"total":0}}`,
|
||||
},
|
||||
{
|
||||
desc: "should return 403 if no used is not admin",
|
||||
desc: "returns 403 if the user does not have the right permissions",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot/1",
|
||||
basicRole: org.RoleEditor,
|
||||
user: userWithoutPermissions,
|
||||
expectedHttpResult: http.StatusForbidden,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 500 if service returns an error",
|
||||
desc: "returns 500 if service returns an error",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot/1",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusInternalServerError,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 400 if uid is invalid",
|
||||
desc: "returns 400 if uid is invalid",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/***/snapshot/1",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "should return 400 if snapshot_uid is invalid",
|
||||
desc: "returns 400 if snapshot_uid is invalid",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot/***",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusBadRequest,
|
||||
},
|
||||
@ -390,51 +411,51 @@ func TestCloudMigrationAPI_GetSnapshot(t *testing.T) {
|
||||
func TestCloudMigrationAPI_GetSnapshotList(t *testing.T) {
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "should return 200 if everything is ok",
|
||||
desc: "returns 200 if the user has the right permissions",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshots",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusOK,
|
||||
expectedBody: `{"snapshots":[{"uid":"fake_uid","status":"CREATING","sessionUid":"1234","created":"2024-06-05T17:30:40Z","finished":"0001-01-01T00:00:00Z"},{"uid":"fake_uid","status":"CREATING","sessionUid":"1234","created":"2024-06-05T18:30:40Z","finished":"0001-01-01T00:00:00Z"}]}`,
|
||||
},
|
||||
{
|
||||
desc: "with limit query param should return 200 if everything is ok",
|
||||
desc: "with limit query param returns 200 if everything is ok",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshots?limit=1",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusOK,
|
||||
expectedBody: `{"snapshots":[{"uid":"fake_uid","status":"CREATING","sessionUid":"1234","created":"2024-06-05T17:30:40Z","finished":"0001-01-01T00:00:00Z"}]}`,
|
||||
},
|
||||
{
|
||||
desc: "with sort query param should return 200 if everything is ok",
|
||||
desc: "with sort query param returns 200 if everything is ok",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshots?sort=latest",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusOK,
|
||||
expectedBody: `{"snapshots":[{"uid":"fake_uid","status":"CREATING","sessionUid":"1234","created":"2024-06-05T18:30:40Z","finished":"0001-01-01T00:00:00Z"},{"uid":"fake_uid","status":"CREATING","sessionUid":"1234","created":"2024-06-05T17:30:40Z","finished":"0001-01-01T00:00:00Z"}]}`,
|
||||
},
|
||||
{
|
||||
desc: "should return 403 if no used is not admin",
|
||||
desc: "returns 403 if the user does not have the right permissions",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshots",
|
||||
basicRole: org.RoleEditor,
|
||||
user: userWithoutPermissions,
|
||||
expectedHttpResult: http.StatusForbidden,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 500 if service returns an error",
|
||||
desc: "returns 500 if service returns an error",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshots",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusInternalServerError,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 400 if uid is invalid",
|
||||
desc: "returns 400 if uid is invalid",
|
||||
requestHttpMethod: http.MethodGet,
|
||||
requestUrl: "/api/cloudmigration/migration/***/snapshots",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusBadRequest,
|
||||
},
|
||||
@ -448,43 +469,43 @@ func TestCloudMigrationAPI_GetSnapshotList(t *testing.T) {
|
||||
func TestCloudMigrationAPI_UploadSnapshot(t *testing.T) {
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "should return 200 if everything is ok",
|
||||
desc: "returns 200 if the user has the right permissions",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot/1/upload",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusOK,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 403 if no used is not admin",
|
||||
desc: "returns 403 if the user does not have the right permissions",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot/1/upload",
|
||||
basicRole: org.RoleEditor,
|
||||
user: userWithoutPermissions,
|
||||
expectedHttpResult: http.StatusForbidden,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 500 if service returns an error",
|
||||
desc: "returns 500 if service returns an error",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot/1/upload",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusInternalServerError,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 400 if uid is invalid",
|
||||
desc: "returns 400 if uid is invalid",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/***/snapshot/1/upload",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "should return 400 if snapshot_uid is invalid",
|
||||
desc: "returns 400 if snapshot_uid is invalid",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot/***/upload",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusBadRequest,
|
||||
},
|
||||
@ -498,43 +519,43 @@ func TestCloudMigrationAPI_UploadSnapshot(t *testing.T) {
|
||||
func TestCloudMigrationAPI_CancelSnapshot(t *testing.T) {
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "should return 200 if everything is ok",
|
||||
desc: "returns 200 if the user has the right permissions",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot/1/cancel",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
expectedHttpResult: http.StatusOK,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 403 if no used is not admin",
|
||||
desc: "returns 403 if the user does not have the right permissions",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot/1/cancel",
|
||||
basicRole: org.RoleEditor,
|
||||
user: userWithoutPermissions,
|
||||
expectedHttpResult: http.StatusForbidden,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 500 if service returns an error",
|
||||
desc: "returns 500 if service returns an error",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot/1/cancel",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusInternalServerError,
|
||||
expectedBody: "",
|
||||
},
|
||||
{
|
||||
desc: "should return 400 if uid is invalid",
|
||||
desc: "returns 400 if uid is invalid",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/***/snapshot/1/cancel",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "should return 400 if snapshot_uid is invalid",
|
||||
desc: "returns 400 if snapshot_uid is invalid",
|
||||
requestHttpMethod: http.MethodPost,
|
||||
requestUrl: "/api/cloudmigration/migration/1234/snapshot/***/cancel",
|
||||
basicRole: org.RoleAdmin,
|
||||
user: userWithPermissions,
|
||||
serviceReturnError: true,
|
||||
expectedHttpResult: http.StatusBadRequest,
|
||||
},
|
||||
@ -548,7 +569,13 @@ func TestCloudMigrationAPI_CancelSnapshot(t *testing.T) {
|
||||
func runSimpleApiTest(tt TestCase) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
// setup server
|
||||
api := RegisterApi(routing.NewRouteRegister(), fake.FakeServiceImpl{ReturnError: tt.serviceReturnError}, tracing.InitializeTracerForTest())
|
||||
api := RegisterApi(
|
||||
routing.NewRouteRegister(),
|
||||
fake.FakeServiceImpl{ReturnError: tt.serviceReturnError},
|
||||
tracing.InitializeTracerForTest(),
|
||||
acimpl.ProvideAccessControlTest(),
|
||||
)
|
||||
|
||||
server := webtest.NewServer(t, api.routeRegister)
|
||||
|
||||
var body io.Reader = nil
|
||||
@ -559,12 +586,10 @@ func runSimpleApiTest(tt TestCase) func(t *testing.T) {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// create test request
|
||||
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
OrgRole: tt.basicRole,
|
||||
})
|
||||
webtest.RequestWithSignedInUser(req, tt.user)
|
||||
res, err := server.Send(req)
|
||||
defer func() { require.NoError(t, res.Body.Close()) }()
|
||||
t.Cleanup(func() { require.NoError(t, res.Body.Close()) })
|
||||
|
||||
// validations
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.expectedHttpResult, res.StatusCode)
|
||||
|
@ -111,6 +111,7 @@ func ProvideService(
|
||||
pluginStore pluginstore.Store,
|
||||
pluginSettingsService pluginsettings.Service,
|
||||
accessControl accesscontrol.AccessControl,
|
||||
acService accesscontrol.Service,
|
||||
kvStore kvstore.KVStore,
|
||||
libraryElementsService libraryelements.Service,
|
||||
ngAlert *ngalert.AlertNG,
|
||||
@ -119,6 +120,10 @@ func ProvideService(
|
||||
return &NoopServiceImpl{}, nil
|
||||
}
|
||||
|
||||
if err := cloudmigration.RegisterAccessControlRoles(acService); err != nil {
|
||||
return nil, fmt.Errorf("registering access control roles: %w", err)
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
store: &sqlStore{db: db, secretsStore: secretsStore, secretsService: secretsService},
|
||||
log: log.New(LogPrefix),
|
||||
@ -137,7 +142,7 @@ func ProvideService(
|
||||
libraryElementsService: libraryElementsService,
|
||||
ngAlert: ngAlert,
|
||||
}
|
||||
s.api = api.RegisterApi(routeRegister, s, tracer)
|
||||
s.api = api.RegisterApi(routeRegister, s, tracer, accessControl)
|
||||
|
||||
httpClientS3, err := httpClientProvider.New()
|
||||
if err != nil {
|
||||
|
@ -929,6 +929,7 @@ func setUpServiceTest(t *testing.T, withDashboardMock bool, cfgOverrides ...conf
|
||||
&pluginstore.FakePluginStore{},
|
||||
&pluginsettings.FakePluginSettings{},
|
||||
actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||
fakeAccessControlService,
|
||||
kvstore.ProvideService(sqlStore),
|
||||
&libraryelementsfake.LibraryElementService{},
|
||||
ng,
|
||||
|
@ -1,11 +0,0 @@
|
||||
package slicesext
|
||||
|
||||
func Map[T any, U any](xs []T, f func(T) U) []U {
|
||||
out := make([]U, 0, len(xs))
|
||||
|
||||
for _, x := range xs {
|
||||
out = append(out, f(x))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package slicesext_test
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/cloudmigration/slicesext"
|
||||
)
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("mapping a nil slice does nothing and returns an empty slice", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Empty(t, slicesext.Map[any, any](nil, nil))
|
||||
})
|
||||
|
||||
t.Run("mapping a non-nil slice with a nil function panics", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Panics(t, func() { slicesext.Map[int, any]([]int{1, 2, 3}, nil) })
|
||||
})
|
||||
|
||||
t.Run("mapping a non-nil slice with a non-nil function returns the mapped slice", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
original := []int{1, 2, 3}
|
||||
expected := []string{"1", "2", "3"}
|
||||
fn := func(i int) string { return strconv.Itoa(i) }
|
||||
|
||||
require.ElementsMatch(t, expected, slicesext.Map(original, fn))
|
||||
})
|
||||
}
|
@ -4,11 +4,11 @@ import (
|
||||
"github.com/grafana/grafana/pkg/login/social"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/ssoutils"
|
||||
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/correlations"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/navtree"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginaccesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@ -61,7 +61,7 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
|
||||
Url: s.cfg.AppSubURL + "/admin/storage",
|
||||
})
|
||||
}
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagOnPremToCloudMigrations) && c.SignedInUser.HasRole(org.RoleAdmin) {
|
||||
if hasAccess(cloudmigration.MigrationAssistantAccess) && s.features.IsEnabled(ctx, featuremgmt.FlagOnPremToCloudMigrations) {
|
||||
generalNodeLinks = append(generalNodeLinks, &navtree.NavLink{
|
||||
Text: "Migrate to Grafana Cloud",
|
||||
Id: "migrate-to-cloud",
|
||||
|
@ -377,7 +377,7 @@ export function getAppRoutes(): RouteDescriptor[] {
|
||||
},
|
||||
config.featureToggles.onPremToCloudMigrations && {
|
||||
path: '/admin/migrate-to-cloud',
|
||||
roles: () => ['Admin'],
|
||||
roles: () => contextSrv.evaluatePermission([AccessControlAction.MigrationAssistantMigrate]),
|
||||
component: SafeDynamicImport(
|
||||
() => import(/* webpackChunkName: "MigrateToCloud" */ 'app/features/migrate-to-cloud/MigrateToCloud')
|
||||
),
|
||||
|
@ -164,6 +164,9 @@ export enum AccessControlAction {
|
||||
// GroupSync
|
||||
GroupSyncMappingsRead = 'groupsync.mappings:read',
|
||||
GroupSyncMappingsWrite = 'groupsync.mappings:write',
|
||||
|
||||
// Migration Assistant
|
||||
MigrationAssistantMigrate = 'migrationassistant:migrate',
|
||||
}
|
||||
|
||||
export interface Role {
|
||||
|
Loading…
Reference in New Issue
Block a user