From ceee0b9c12e17545cb981bb81719f9a352ee0bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 30 Jun 2025 17:23:11 +0200 Subject: [PATCH] UserAdmin: Inform about enterprise user features (#106632) * UserAdmin: Inform about enterprise user features * Update * Update * Update * Update * Update * New design * Update * Update * Update * Update * Update link * fix import * refactor to brand badge to component * Update * Update * Update * Update * Update links --- e2e/pa11yci.conf.js | 2 +- go.work.sum | 8 ++ pkg/services/user/model.go | 1 + pkg/services/user/userimpl/store.go | 5 +- .../admin/EnterpriseAuthFeaturesCard.tsx | 117 ++++++++++++++++++ .../app/features/admin/UserListAdminPage.tsx | 2 + .../auth-config/components/ProviderCard.tsx | 4 +- public/app/features/teams/TeamList.test.tsx | 3 + public/app/features/teams/TeamList.tsx | 2 + public/locales/en-US/grafana.json | 6 + 10 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 public/app/features/admin/EnterpriseAuthFeaturesCard.tsx diff --git a/e2e/pa11yci.conf.js b/e2e/pa11yci.conf.js index 63e274e16bf..b4ed68a5412 100644 --- a/e2e/pa11yci.conf.js +++ b/e2e/pa11yci.conf.js @@ -91,7 +91,7 @@ const config = { }, { url: '${HOST}/org/teams', - threshold: 0, + threshold: 1, }, { url: '${HOST}/plugins', diff --git a/go.work.sum b/go.work.sum index 9bbec9aacc2..cf90c70f3b1 100644 --- a/go.work.sum +++ b/go.work.sum @@ -754,6 +754,7 @@ github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 h github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30/go.mod h1:RNuWDIiGjq5nndL2PyQrndUy9nMLwheA3uWaAV7fe4U= +github.com/apache/arrow-go/v18 v18.2.0/go.mod h1:Ic/01WSwGJWRrdAZcxjBZ5hbApNJ28K96jGYaxzzGUc= github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 h1:q4dksr6ICHXqG5hm0ZW5IHyeEJXoIJSOZeBLmWPNeIQ= github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs= github.com/apache/arrow/go/v10 v10.0.1 h1:n9dERvixoC/1JjDmBcs9FPaEryoANa2sCgVFo6ez9cI= @@ -885,6 +886,7 @@ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpV github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chromedp/cdproto v0.0.0-20220208224320-6efb837e6bc2/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U= +github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/chromedp v0.9.2 h1:dKtNz4kApb06KuSXoTQIyUC2TrA0fhGDwNZf3bcgfKw= github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= @@ -1335,6 +1337,7 @@ github.com/grafana/grafana-plugin-sdk-go v0.263.0/go.mod h1:U43Cnrj/9DNYyvFcNdeU github.com/grafana/grafana-plugin-sdk-go v0.267.0/go.mod h1:OuwS4c/JYgn0rr/w5zhJBpLo4gKm/vw15RsfpYAvK9Q= github.com/grafana/grafana-plugin-sdk-go v0.269.1/go.mod h1:yv2KbO4mlr9WuDK2f+2gHAMTwwLmLuqaEnrPXTRU+OI= github.com/grafana/grafana-plugin-sdk-go v0.275.0/go.mod h1:mO9LJqdXDh5JpO/xIdPAeg5LdThgQ06Y/SLpXDWKw2c= +github.com/grafana/grafana-plugin-sdk-go v0.277.0/go.mod h1:mAUWg68w5+1f5TLDqagIr8sWr1RT9h7ufJl5NMcWJAU= github.com/grafana/grafana/apps/advisor v0.0.0-20250123151950-b066a6313173/go.mod h1:goSDiy3jtC2cp8wjpPZdUHRENcoSUHae1/Px/MDfddA= github.com/grafana/grafana/apps/advisor v0.0.0-20250220154326-6e5de80ef295/go.mod h1:9I1dKV3Dqr0NPR9Af0WJGxOytp5/6W3JLiNChOz8r+c= github.com/grafana/grafana/apps/advisor v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:q+h3HbmqU/PposW6lq8cMle1v8vuyX1LCMrGzbabHxc= @@ -1366,6 +1369,8 @@ github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250121113133-e7 github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:xrKQcxQxz+IUF90ybtfENFeEXtlj9nAsX/3Fw0KEIeQ= github.com/grafana/nanogit v0.0.0-20250616082354-5e94194d02ed h1:59JF1WhHLT+lNX89Tm1OzOEySMVMASAhaPbsRjtp8Kc= github.com/grafana/nanogit v0.0.0-20250616082354-5e94194d02ed/go.mod h1:OIAAKNgG5fpuJQRNO1lUSj9nc18Xl3O7M8fjIlBO1cI= +github.com/grafana/nanogit v0.0.0-20250619160700-ebf70d342aa5 h1:MAQ2B0cu0V1S91ZjVa7NomNZFjaR2SmdtvdwhqBtyhU= +github.com/grafana/nanogit v0.0.0-20250619160700-ebf70d342aa5/go.mod h1:tN93IZUaAmnSWgL0IgnKdLv6DNeIhTJGvl1wvQMrWco= github.com/grafana/prometheus-alertmanager v0.25.1-0.20240930132144-b5e64e81e8d3 h1:6D2gGAwyQBElSrp3E+9lSr7k8gLuP3Aiy20rweLWeBw= github.com/grafana/prometheus-alertmanager v0.25.1-0.20240930132144-b5e64e81e8d3/go.mod h1:YeND+6FDA7OuFgDzYODN8kfPhXLCehcpxe4T9mdnpCY= github.com/grafana/prometheus-alertmanager v0.25.1-0.20250331083058-4563aec7a975 h1:4/BZkGObFWZf4cLbE2Vqg/1VTz67Q0AJ7LHspWLKJoQ= @@ -1380,6 +1385,7 @@ github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJr github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= @@ -2329,6 +2335,7 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.5 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.58.0/go.mod h1:uosvgpqTcTXtcPQORTbEkZNDQTCDOgTz1fe6aLSyqrQ= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0/go.mod h1:54CaSNqYEXvpzDh8KPjiMVoWm60t5R0dZRt0leEPgAs= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= @@ -2341,6 +2348,7 @@ go.opentelemetry.io/contrib/propagators/b3 v1.27.0 h1:IjgxbomVrV9za6bRi8fWCNXENs go.opentelemetry.io/contrib/propagators/b3 v1.27.0/go.mod h1:Dv9obQz25lCisDvvs4dy28UPh974CxkahRDUPsY7y9E= go.opentelemetry.io/contrib/propagators/b3 v1.35.0 h1:DpwKW04LkdFRFCIgM3sqwTJA/QREHMeMHYPWP1WeaPQ= go.opentelemetry.io/contrib/propagators/b3 v1.35.0/go.mod h1:9+SNxwqvCWo1qQwUpACBY5YKNVxFJn5mlbXg/4+uKBg= +go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU= go.opentelemetry.io/contrib/samplers/jaegerremote v0.28.0/go.mod h1:iWS+NvC948FyfnJbVfPN9h/8+vr8CR2FPn6XsLRkvH8= go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0/go.mod h1:XAJmM2MWhiIoTO4LCLBVeE8w009TmsYk6hq1UNdXs5A= go.opentelemetry.io/contrib/zpages v0.60.0 h1:wOM9ie1Hz4H88L9KE6GrGbKJhfm+8F1NfW/Y3q9Xt+8= diff --git a/pkg/services/user/model.go b/pkg/services/user/model.go index 6e2377052a9..b60bd4ac306 100644 --- a/pkg/services/user/model.go +++ b/pkg/services/user/model.go @@ -15,6 +15,7 @@ func (f *HelpFlags1) AddFlag(flag HelpFlags1) { *f |= flag } const ( HelpFlagGettingStartedPanelDismissed HelpFlags1 = 1 << iota HelpFlagDashboardHelp1 + HelpFlagEnterpriseAuth1 ) type UpdateEmailActionType string diff --git a/pkg/services/user/userimpl/store.go b/pkg/services/user/userimpl/store.go index 40517d9f29e..ccbef1138f8 100644 --- a/pkg/services/user/userimpl/store.go +++ b/pkg/services/user/userimpl/store.go @@ -274,7 +274,10 @@ func (ss *sqlStore) Update(ctx context.Context, cmd *user.UpdateUserCommand) err q = q.UseBool("is_admin") usr.IsAdmin = v }) - setOptional(cmd.HelpFlags1, func(v user.HelpFlags1) { usr.HelpFlags1 = *cmd.HelpFlags1 }) + setOptional(cmd.HelpFlags1, func(v user.HelpFlags1) { + q = q.MustCols("help_flags1") + usr.HelpFlags1 = *cmd.HelpFlags1 + }) if _, err := q.Update(&usr); err != nil { return err diff --git a/public/app/features/admin/EnterpriseAuthFeaturesCard.tsx b/public/app/features/admin/EnterpriseAuthFeaturesCard.tsx new file mode 100644 index 00000000000..f52e2726b3c --- /dev/null +++ b/public/app/features/admin/EnterpriseAuthFeaturesCard.tsx @@ -0,0 +1,117 @@ +import { css } from '@emotion/css'; +import { useState } from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { GrafanaEdition } from '@grafana/data/internal'; +import { t, Trans } from '@grafana/i18n'; +import { config } from '@grafana/runtime'; +import { Text, Stack, useStyles2, Button, LinkButton } from '@grafana/ui'; +import { CloudEnterpriseBadge } from 'app/core/components/Branding/CloudEnterpriseBadge'; +import { contextSrv } from 'app/core/core'; +import { backendSrv } from 'app/core/services/backend_srv'; + +export interface Props { + page?: 'teams' | 'users'; +} + +export function EnterpriseAuthFeaturesCard({ page }: Props) { + const styles = useStyles2(getStyles); + const helpFlags = contextSrv.user.helpFlags1; + const HELP_FLAG_ENTERPRISE_AUTH = 0x0004; + const [isDismissed, setDismissed] = useState(Boolean(helpFlags & HELP_FLAG_ENTERPRISE_AUTH)); + + const onDismiss = () => { + backendSrv + .put(`/api/user/helpflags/${HELP_FLAG_ENTERPRISE_AUTH}`, undefined, { showSuccessAlert: false }) + .then((res) => { + contextSrv.user.helpFlags1 = res.helpFlags1; + setDismissed(true); + }); + }; + + // This card is only visible in oss + if (isDismissed || !isOpenSourceBuildOrUnlicenced()) { + return null; + } + + return ( +
+ + +
+ ); +} + +function getStyles(theme: GrafanaTheme2) { + return { + cloudBadge: css({ + display: 'flex', + alignItems: 'center', + background: theme.colors.gradients.brandHorizontal, + color: theme.colors.primary.contrastText, + padding: theme.spacing(0.5, 1), + borderRadius: theme.shape.radius.pill, + fontSize: theme.typography.bodySmall.fontSize, + gap: theme.spacing(1), + }), + box: css({ + padding: theme.spacing(3), + border: `1px solid ${theme.colors.border.weak}`, + backgroundColor: theme.colors.background.secondary, + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(1.5), + borderRadius: theme.shape.radius.default, + marginTop: theme.spacing(3), + strong: { + color: theme.colors.text.primary, + }, + }), + icon: css({ + position: 'relative', + top: -1, + }), + }; +} + +export function isOpenSourceBuildOrUnlicenced() { + if (config.buildInfo.edition === GrafanaEdition.OpenSource) { + return true; + } + + if (config.licenseInfo.stateInfo !== 'Licensed') { + return true; + } + + return false; +} diff --git a/public/app/features/admin/UserListAdminPage.tsx b/public/app/features/admin/UserListAdminPage.tsx index d14b2d02f09..d4b485c3ada 100644 --- a/public/app/features/admin/UserListAdminPage.tsx +++ b/public/app/features/admin/UserListAdminPage.tsx @@ -11,6 +11,7 @@ import { contextSrv } from 'app/core/core'; import { AccessControlAction, StoreState, UserFilter } from '../../types'; +import { EnterpriseAuthFeaturesCard } from './EnterpriseAuthFeaturesCard'; import { UsersTable } from './Users/UsersTable'; import { changeFilter, changePage, changeQuery, changeSort, fetchUsers } from './state/actions'; @@ -118,6 +119,7 @@ const UserListAdminPageUnConnected = ({ fetchData={changeSort} /> )} + ); }; diff --git a/public/app/features/auth-config/components/ProviderCard.tsx b/public/app/features/auth-config/components/ProviderCard.tsx index cb83ca8f28b..b7263c342dd 100644 --- a/public/app/features/auth-config/components/ProviderCard.tsx +++ b/public/app/features/auth-config/components/ProviderCard.tsx @@ -51,7 +51,7 @@ export function ProviderSAMLCard() { external variant="bodySmall" color="secondary" - href="https://grafana.com/auth/sign-up/create-user?cloud-auth=&redirectPath=cloud-auth&utm_source=oss-authorization-admin" + href="https://grafana.com/auth/sign-up/create-user?cloud-auth=&redirectPath=cloud-auth&utm_source=oss-grafana&cnt=admin-authorization-saml" > {t('auth-config.provider-card.saml-learn-more', 'Single sign-on (SSO) with SAML.')} @@ -77,7 +77,7 @@ export function ProviderSCIMCard() { external variant="bodySmall" color="secondary" - href="https://grafana.com/auth/sign-up/create-user?cloud-auth=&redirectPath=cloud-auth&utm_source=oss-authorization-admin" + href="https://grafana.com/auth/sign-up/create-user?cloud-auth=&redirectPath=cloud-auth&utm_source=oss-grafana&cnt=admin-authorization-scim" > {t('auth-config.provider-card.scim-learn-more', ' Sync users and teams with SCIM.')} diff --git a/public/app/features/teams/TeamList.test.tsx b/public/app/features/teams/TeamList.test.tsx index abbfff37583..7629076e3c2 100644 --- a/public/app/features/teams/TeamList.test.tsx +++ b/public/app/features/teams/TeamList.test.tsx @@ -13,6 +13,9 @@ jest.mock('app/core/core', () => ({ contextSrv: { hasPermission: (action: string) => true, licensedAccessControlEnabled: () => false, + user: { + helpFlags1: 0, + }, }, })); diff --git a/public/app/features/teams/TeamList.tsx b/public/app/features/teams/TeamList.tsx index 369285604ae..dd93bd941e7 100644 --- a/public/app/features/teams/TeamList.tsx +++ b/public/app/features/teams/TeamList.tsx @@ -27,6 +27,7 @@ import { contextSrv } from 'app/core/services/context_srv'; import { AccessControlAction, Role, StoreState, TeamWithRoles } from 'app/types'; import { TeamRolePicker } from '../../core/components/RolePicker/TeamRolePicker'; +import { EnterpriseAuthFeaturesCard } from '../admin/EnterpriseAuthFeaturesCard'; import { deleteTeam, loadTeams, changePage, changeQuery, changeSort } from './state/actions'; @@ -288,6 +289,7 @@ export const TeamList = ({ )} )} + {!query && } ); diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 9e8789046a4..d117d5ed8f2 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -110,6 +110,12 @@ "update-button": "Update", "users-heading": "Organization users" }, + "enterprise-auth-features-card": { + "dismiss": "Dismiss", + "heading": "Enterprise authentication", + "learn-more-link": "Learn more", + "text": "Manage users, teams, and permissions automatically with <1>SAML, <3>SCIM, <6>LDAP, and <8>RBAC — available in Grafana Cloud and Enterprise." + }, "feature-listing": { "title-auditing": "Auditing", "title-custom-branding": "Custom branding",