web/admin: revamped rbac and user settings tabs (#8299)

* web/admin: fix duplicate RBAC preview banner on permission modal

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* switch non-embedded permission page to use vertical tabs

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix some leftover html?

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* move stuff into vertical subtab

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* show all of users permission tabs on one main tab

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* rework role page to match user page

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* use separate tabs

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* rename role permission tables to match user tables

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* rename to credentials and tokens

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add country icon to session list

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add oauth access token list

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add helper to get relative time

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* use pfdivider

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* replace plain hr with pf-c-divider

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* use new logic for showing relative time in charts

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* use consistent relative time for event display

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove more leftovers

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix some alignment issues on the admin dashboard

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update storybook map

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add sanity check to event app lookup

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* make api drawer header fixed

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix table padding for toggle

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix notification drawer for user interface

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* enable system task search

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix formatting, exclude generated script from formatting

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* web: minor fixes

There's a renderer (it's not a component, not yet) for producing definition lists without
the risk of missing a class or tag.

Breaking conditionally rendered components out to make their use easier to identify.

* fix prettier

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix outpost form

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix more flaky tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* re-create locale

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add some description for different permission views

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix system task search

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update docs

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Ken Sternberg <ken@goauthentik.io>
This commit is contained in:
Jens L
2024-01-26 18:01:03 +01:00
committed by GitHub
parent 85a8768424
commit 11ca358242
48 changed files with 838 additions and 456 deletions

View File

@ -86,7 +86,6 @@ class NotificationTransportViewSet(UsedByMixin, ModelViewSet):
event = Event.new( event = Event.new(
action="notification_test", action="notification_test",
user=get_user(request.user), user=get_user(request.user),
app=self.__class__.__module__,
context={"foo": "bar"}, context={"foo": "bar"},
) )
event.save() event.save()

View File

@ -209,8 +209,9 @@ class Event(SerializerModel, ExpiringModel):
app = parent.f_globals["__name__"] app = parent.f_globals["__name__"]
# Attempt to match the calling module to the django app it belongs to # Attempt to match the calling module to the django app it belongs to
# if we can't find a match, keep the module name # if we can't find a match, keep the module name
django_apps = get_close_matches(app, django_app_names(), n=1) django_apps: list[str] = get_close_matches(app, django_app_names(), n=1)
if len(django_apps) > 0: # Also ensure that closest django app has the correct prefix
if len(django_apps) > 0 and django_apps[0].startswith(app):
app = django_apps[0] app = django_apps[0]
cleaned_kwargs = cleanse_dict(sanitize_dict(kwargs)) cleaned_kwargs = cleanse_dict(sanitize_dict(kwargs))
event = Event(action=action, app=app, context=cleaned_kwargs) event = Event(action=action, app=app, context=cleaned_kwargs)

View File

@ -70,7 +70,7 @@ class TestRecovery(TenantAPITestCase):
body = loads(response.content.decode()) body = loads(response.content.decode())
token = Token.objects.get(intent=TokenIntents.INTENT_RECOVERY, user=self.user) token = Token.objects.get(intent=TokenIntents.INTENT_RECOVERY, user=self.user)
self.assertIn(token.key, body["url"]) self.assertIn(token.key, body["url"])
self.assertEqual(len(Token.objects.all()), 1) self.assertEqual(len(Token.objects.filter(intent=TokenIntents.INTENT_RECOVERY)), 1)
@CONFIG.patch("outposts.disable_embedded_outpost", True) @CONFIG.patch("outposts.disable_embedded_outpost", True)
@CONFIG.patch("tenants.enabled", True) @CONFIG.patch("tenants.enabled", True)
@ -86,4 +86,4 @@ class TestRecovery(TenantAPITestCase):
headers=HEADERS, headers=HEADERS,
) )
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
self.assertEqual(len(Token.objects.all()), 0) self.assertEqual(len(Token.objects.filter(intent=TokenIntents.INTENT_RECOVERY)), 0)

View File

@ -11,3 +11,4 @@ src/locales/
storybook-static/ storybook-static/
# Prettier breaks the tsconfig file # Prettier breaks the tsconfig file
tsconfig.json tsconfig.json
.storybook/css-import-maps*

View File

@ -27,6 +27,7 @@ const rawCssImportMaps = [
'import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css";', 'import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css";',
'import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";', 'import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";',
'import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";', 'import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";',
'import PFDivider from "@patternfly/patternfly/components/Divider/divider.css";',
'import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";', 'import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";',
'import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";', 'import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";',
'import PFDualListSelector from "@patternfly/patternfly/components/DualListSelector/dual-list-selector.css";', 'import PFDualListSelector from "@patternfly/patternfly/components/DualListSelector/dual-list-selector.css";',
@ -70,10 +71,8 @@ const rawCssImportMaps = [
'import styles from "./LibraryPageImpl.css";', 'import styles from "./LibraryPageImpl.css";',
]; ];
const cssImportMaps = rawCssImportMaps.reduce( const cssImportMaps = rawCssImportMaps.reduce((acc, line) => (
(acc, line) => ({ ...acc, [line]: line.replace(/\.css/, ".css?inline") }), {...acc, [line]: line.replace(/\.css/, ".css?inline")}), {});
{},
);
export { cssImportMaps }; export { cssImportMaps };
export default cssImportMaps; export default cssImportMaps;

View File

@ -63,8 +63,7 @@ function getTheImportLines(importPaths: string[]) {
const importPaths = getTheSourceFiles(); const importPaths = getTheSourceFiles();
const importLines = getTheImportLines(importPaths); const importLines = getTheImportLines(importPaths);
const outputFile = ` const outputFile = `// THIS IS A GENERATED FILE. DO NOT EDIT BY HAND.
// THIS IS A GENERATED FILE. DO NOT EDIT BY HAND.
// //
// This file is generated by the build-storybook-import-maps script in the UI's base directory. // This file is generated by the build-storybook-import-maps script in the UI's base directory.
// This is a *hack* to work around an inconsistency in the way rollup, vite, and storybook // This is a *hack* to work around an inconsistency in the way rollup, vite, and storybook

View File

@ -19,6 +19,7 @@ import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, state } from "lit/decorators.js"; import { customElement, state } from "lit/decorators.js";
import PFContent from "@patternfly/patternfly/components/Content/content.css"; import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFDivider from "@patternfly/patternfly/components/Divider/divider.css";
import PFList from "@patternfly/patternfly/components/List/list.css"; import PFList from "@patternfly/patternfly/components/List/list.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
@ -41,15 +42,12 @@ export class AdminOverviewPage extends AKElement {
PFPage, PFPage,
PFContent, PFContent,
PFList, PFList,
PFDivider,
css` css`
.row-divider { .pf-l-grid__item {
margin-top: -4px; height: 100%;
margin-bottom: -4px;
} }
.graph-container { .pf-l-grid__item.big-graph-container {
height: 20em;
}
.big-graph-container {
height: 35em; height: 35em;
} }
.card-container { .card-container {
@ -82,9 +80,7 @@ export class AdminOverviewPage extends AKElement {
<div class="pf-l-grid pf-m-gutter"> <div class="pf-l-grid pf-m-gutter">
<!-- row 1 --> <!-- row 1 -->
<div class="pf-l-grid__item pf-m-6-col pf-l-grid pf-m-gutter"> <div class="pf-l-grid__item pf-m-6-col pf-l-grid pf-m-gutter">
<div <div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-4-col-on-2xl">
class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-4-col-on-2xl graph-container"
>
<ak-aggregate-card <ak-aggregate-card
icon="fa fa-share" icon="fa fa-share"
header=${msg("Quick actions")} header=${msg("Quick actions")}
@ -136,9 +132,7 @@ export class AdminOverviewPage extends AKElement {
</ul> </ul>
</ak-aggregate-card> </ak-aggregate-card>
</div> </div>
<div <div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-4-col-on-2xl">
class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-4-col-on-2xl graph-container"
>
<ak-aggregate-card <ak-aggregate-card
icon="pf-icon pf-icon-zone" icon="pf-icon pf-icon-zone"
header=${msg("Outpost status")} header=${msg("Outpost status")}
@ -148,14 +142,14 @@ export class AdminOverviewPage extends AKElement {
</ak-aggregate-card> </ak-aggregate-card>
</div> </div>
<div <div
class="pf-l-grid__item pf-m-12-col pf-m-12-col-on-xl pf-m-4-col-on-2xl graph-container" class="pf-l-grid__item pf-m-12-col pf-m-12-col-on-xl pf-m-4-col-on-2xl"
> >
<ak-aggregate-card icon="fa fa-sync-alt" header=${msg("Sync status")}> <ak-aggregate-card icon="fa fa-sync-alt" header=${msg("Sync status")}>
<ak-admin-status-chart-sync></ak-admin-status-chart-sync> <ak-admin-status-chart-sync></ak-admin-status-chart-sync>
</ak-aggregate-card> </ak-aggregate-card>
</div> </div>
<div class="pf-l-grid__item pf-m-12-col row-divider"> <div class="pf-l-grid__item pf-m-12-col">
<hr /> <hr class="pf-c-divider" />
</div> </div>
<div <div
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-md pf-m-4-col-on-xl card-container" class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-md pf-m-4-col-on-xl card-container"
@ -176,8 +170,8 @@ export class AdminOverviewPage extends AKElement {
<div class="pf-l-grid__item pf-m-6-col"> <div class="pf-l-grid__item pf-m-6-col">
<ak-recent-events pageSize="6"></ak-recent-events> <ak-recent-events pageSize="6"></ak-recent-events>
</div> </div>
<div class="pf-l-grid__item pf-m-12-col row-divider"> <div class="pf-l-grid__item pf-m-12-col">
<hr /> <hr class="pf-c-divider" />
</div> </div>
<!-- row 3 --> <!-- row 3 -->
<div <div

View File

@ -8,6 +8,7 @@ import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement } from "lit/decorators.js"; import { customElement } from "lit/decorators.js";
import PFContent from "@patternfly/patternfly/components/Content/content.css"; import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFDivider from "@patternfly/patternfly/components/Divider/divider.css";
import PFList from "@patternfly/patternfly/components/List/list.css"; import PFList from "@patternfly/patternfly/components/List/list.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
@ -22,14 +23,8 @@ export class DashboardUserPage extends AKElement {
PFPage, PFPage,
PFContent, PFContent,
PFList, PFList,
PFDivider,
css` css`
.row-divider {
margin-top: -4px;
margin-bottom: -4px;
}
.graph-container {
height: 20em;
}
.big-graph-container { .big-graph-container {
height: 35em; height: 35em;
} }
@ -59,8 +54,8 @@ export class DashboardUserPage extends AKElement {
</ak-charts-admin-model-per-day> </ak-charts-admin-model-per-day>
</ak-aggregate-card> </ak-aggregate-card>
</div> </div>
<div class="pf-l-grid__item pf-m-12-col row-divider"> <div class="pf-l-grid__item pf-m-12-col">
<hr /> <hr class="pf-c-divider" />
</div> </div>
<!-- row 2 --> <!-- row 2 -->
<div <div

View File

@ -1,4 +1,5 @@
import { EventGeo, EventUser } from "@goauthentik/admin/events/utils"; import { EventGeo, EventUser } from "@goauthentik/admin/events/utils";
import { getRelativeTime } from "@goauthentik/app/common/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events"; import { EventWithContext } from "@goauthentik/common/events";
import { actionToLabel } from "@goauthentik/common/labels"; import { actionToLabel } from "@goauthentik/common/labels";
@ -71,7 +72,8 @@ export class RecentEventsCard extends Table<Event> {
html`<div><a href="${`#/events/log/${item.pk}`}">${actionToLabel(item.action)}</a></div> html`<div><a href="${`#/events/log/${item.pk}`}">${actionToLabel(item.action)}</a></div>
<small>${item.app}</small>`, <small>${item.app}</small>`,
EventUser(item), EventUser(item),
html`<span>${item.created?.toLocaleString()}</span>`, html`<div>${getRelativeTime(item.created)}</div>
<small>${item.created.toLocaleString()}</small>`,
html` <div>${item.clientIp || msg("-")}</div> html` <div>${item.clientIp || msg("-")}</div>
<small>${EventGeo(item)}</small>`, <small>${EventGeo(item)}</small>`,
html`<span>${item.brand?.name || msg("-")}</span>`, html`<span>${item.brand?.name || msg("-")}</span>`,

View File

@ -1,8 +1,8 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { AKChart, RGBAColor } from "@goauthentik/elements/charts/Chart"; import { AKChart, RGBAColor } from "@goauthentik/elements/charts/Chart";
import { ChartData, Tick } from "chart.js"; import { ChartData } from "chart.js";
import { msg, str } from "@lit/localize"; import { msg } from "@lit/localize";
import { customElement } from "lit/decorators.js"; import { customElement } from "lit/decorators.js";
import { AdminApi, LoginMetrics } from "@goauthentik/api"; import { AdminApi, LoginMetrics } from "@goauthentik/api";
@ -13,13 +13,6 @@ export class AdminLoginAuthorizeChart extends AKChart<LoginMetrics> {
return new AdminApi(DEFAULT_CONFIG).adminMetricsRetrieve(); return new AdminApi(DEFAULT_CONFIG).adminMetricsRetrieve();
} }
timeTickCallback(tickValue: string | number, index: number, ticks: Tick[]): string {
const valueStamp = ticks[index];
const delta = Date.now() - valueStamp.value;
const ago = Math.round(delta / 1000 / 3600 / 24);
return msg(str`${ago} day(s) ago`);
}
getChartData(data: LoginMetrics): ChartData { getChartData(data: LoginMetrics): ChartData {
return { return {
datasets: [ datasets: [

View File

@ -1,5 +1,6 @@
import "@goauthentik/admin/events/EventVolumeChart"; import "@goauthentik/admin/events/EventVolumeChart";
import { EventGeo, EventUser } from "@goauthentik/admin/events/utils"; import { EventGeo, EventUser } from "@goauthentik/admin/events/utils";
import { getRelativeTime } from "@goauthentik/app/common/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events"; import { EventWithContext } from "@goauthentik/common/events";
import { actionToLabel } from "@goauthentik/common/labels"; import { actionToLabel } from "@goauthentik/common/labels";
@ -82,9 +83,9 @@ export class EventListPage extends TablePage<Event> {
html`<div>${actionToLabel(item.action)}</div> html`<div>${actionToLabel(item.action)}</div>
<small>${item.app}</small>`, <small>${item.app}</small>`,
EventUser(item), EventUser(item),
html`<span>${item.created?.toLocaleString()}</span>`, html`<div>${getRelativeTime(item.created)}</div>
<small>${item.created.toLocaleString()}</small>`,
html`<div>${item.clientIp || msg("-")}</div> html`<div>${item.clientIp || msg("-")}</div>
<small>${EventGeo(item)}</small>`, <small>${EventGeo(item)}</small>`,
html`<span>${item.brand?.name || msg("-")}</span>`, html`<span>${item.brand?.name || msg("-")}</span>`,
html`<a href="#/events/log/${item.pk}"> html`<a href="#/events/log/${item.pk}">

View File

@ -1,4 +1,5 @@
import { EventGeo, EventUser } from "@goauthentik/admin/events/utils"; import { EventGeo, EventUser } from "@goauthentik/admin/events/utils";
import { getRelativeTime } from "@goauthentik/app/common/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events"; import { EventWithContext } from "@goauthentik/common/events";
import { actionToLabel } from "@goauthentik/common/labels"; import { actionToLabel } from "@goauthentik/common/labels";
@ -99,7 +100,8 @@ export class EventViewPage extends AKElement {
</dt> </dt>
<dd class="pf-c-description-list__description"> <dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text"> <div class="pf-c-description-list__text">
${this.event.created?.toLocaleString()} <div>${getRelativeTime(this.event.created)}</div>
<small>${this.event.created.toLocaleString()}</small>
</div> </div>
</dd> </dd>
</div> </div>

View File

@ -230,25 +230,29 @@ export class OutpostForm extends ModelForm<Outpost, string> {
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-group aria-label="Advanced settings"> <ak-form-group aria-label="Advanced settings">
<span slot="header"> ${msg("Advanced settings")} </span> <span slot="header"> ${msg("Advanced settings")} </span>
<ak-form-element-horizontal label=${msg("Configuration")} name="config"> <div slot="body" class="pf-c-form">
<ak-codemirror <ak-form-element-horizontal label=${msg("Configuration")} name="config">
mode=${CodeMirrorMode.YAML} <ak-codemirror
value="${YAML.stringify( mode=${CodeMirrorMode.YAML}
this.instance ? this.instance.config : this.defaultConfig?.config, value="${YAML.stringify(
)}" this.instance ? this.instance.config : this.defaultConfig?.config,
></ak-codemirror> )}"
<p class="pf-c-form__helper-text"> ></ak-codemirror>
${msg("Set custom attributes using YAML or JSON.")} <p class="pf-c-form__helper-text">
</p> ${msg("Set custom attributes using YAML or JSON.")}
<p class="pf-c-form__helper-text"> </p>
${msg("See more here:")}&nbsp; <p class="pf-c-form__helper-text">
<a ${msg("See more here:")}&nbsp;
target="_blank" <a
href="${docLink("/docs/outposts?utm_source=authentik#configuration")}" target="_blank"
>${msg("Documentation")}</a href="${docLink(
> "/docs/outposts?utm_source=authentik#configuration",
</p> )}"
</ak-form-element-horizontal> >${msg("Documentation")}</a
>
</p>
</ak-form-element-horizontal>
</div>
</ak-form-group>`; </ak-form-group>`;
} }
} }

View File

@ -22,6 +22,7 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css"; import PFCard from "@patternfly/patternfly/components/Card/card.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css"; import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
import PFDivider from "@patternfly/patternfly/components/Divider/divider.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css"; import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFPage from "@patternfly/patternfly/components/Page/page.css";
@ -70,6 +71,7 @@ export class OAuth2ProviderViewPage extends AKElement {
PFForm, PFForm,
PFFormControl, PFFormControl,
PFBanner, PFBanner,
PFDivider,
]; ];
} }
@ -258,7 +260,7 @@ export class OAuth2ProviderViewPage extends AKElement {
value="${this.providerUrls?.issuer || msg("-")}" value="${this.providerUrls?.issuer || msg("-")}"
/> />
</div> </div>
<hr /> <hr class="pf-c-divider" />
<div class="pf-c-form__group"> <div class="pf-c-form__group">
<label class="pf-c-form__label"> <label class="pf-c-form__label">
<span class="pf-c-form__label-text" <span class="pf-c-form__label-text"

View File

@ -11,8 +11,8 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { Permission, RbacApi } from "@goauthentik/api"; import { Permission, RbacApi } from "@goauthentik/api";
@customElement("ak-role-permissions-global-table") @customElement("ak-role-assigned-global-permissions-table")
export class RolePermissionGlobalTable extends Table<Permission> { export class RoleAssignedGlobalPermissionsTable extends Table<Permission> {
@property() @property()
roleUuid?: string; roleUuid?: string;

View File

@ -10,8 +10,8 @@ import { customElement, property } from "lit/decorators.js";
import { ExtraRoleObjectPermission, ModelEnum, RbacApi } from "@goauthentik/api"; import { ExtraRoleObjectPermission, ModelEnum, RbacApi } from "@goauthentik/api";
@customElement("ak-role-permissions-object-table") @customElement("ak-role-assigned-object-permissions-table")
export class RolePermissionObjectTable extends Table<ExtraRoleObjectPermission> { export class RoleAssignedObjectPermissionTable extends Table<ExtraRoleObjectPermission> {
@property() @property()
roleUuid?: string; roleUuid?: string;

View File

@ -1,21 +1,20 @@
import "@goauthentik/admin/groups/RelatedGroupList"; import "@goauthentik/admin/groups/RelatedGroupList";
import "@goauthentik/app/admin/roles/RolePermissionGlobalTable"; import "@goauthentik/admin/roles/RoleForm";
import "@goauthentik/app/admin/roles/RolePermissionObjectTable";
import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import "@goauthentik/app/elements/rbac/ObjectPermissionsPage";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { renderDescriptionList } from "@goauthentik/components/DescriptionList";
import "@goauthentik/components/events/ObjectChangelog"; import "@goauthentik/components/events/ObjectChangelog";
import "@goauthentik/components/events/UserEvents"; import "@goauthentik/components/events/UserEvents";
import { AKElement } from "@goauthentik/elements/Base"; import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/PageHeader"; import "@goauthentik/elements/PageHeader";
import "@goauthentik/elements/Tabs"; import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/forms/ModalForm";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit"; import { css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators.js"; import { customElement, property, state } from "lit/decorators.js";
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css"; import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css"; import PFCard from "@patternfly/patternfly/components/Card/card.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css"; import PFContent from "@patternfly/patternfly/components/Content/content.css";
@ -43,7 +42,7 @@ export class RoleViewPage extends AKElement {
@state() @state()
_role?: Role; _role?: Role;
static get styles(): CSSResult[] { static get styles() {
return [ return [
PFBase, PFBase,
PFPage, PFPage,
@ -53,7 +52,6 @@ export class RoleViewPage extends AKElement {
PFContent, PFContent,
PFCard, PFCard,
PFDescriptionList, PFDescriptionList,
PFBanner,
css` css`
.pf-c-description-list__description ak-action-button { .pf-c-description-list__description ak-action-button {
margin-right: 6px; margin-right: 6px;
@ -74,7 +72,7 @@ export class RoleViewPage extends AKElement {
}); });
} }
render(): TemplateResult { render() {
return html`<ak-page-header return html`<ak-page-header
icon="fa fa-lock" icon="fa fa-lock"
header=${msg(str`Role ${this._role?.name || ""}`)} header=${msg(str`Role ${this._role?.name || ""}`)}
@ -83,73 +81,61 @@ export class RoleViewPage extends AKElement {
${this.renderBody()}`; ${this.renderBody()}`;
} }
renderBody(): TemplateResult { renderUpdateControl(role: Role) {
return html` <div class="pf-c-description-list__text">
<ak-forms-modal>
<span slot="submit"> ${msg("Update")} </span>
<span slot="header"> ${msg("Update Role")} </span>
<ak-role-form slot="form" .instancePk=${role.pk}> </ak-role-form>
<button slot="trigger" class="pf-c-button pf-m-primary">${msg("Edit")}</button>
</ak-forms-modal>
</div>`;
}
renderBody() {
if (!this._role) { if (!this._role) {
return html``; return nothing;
} }
return html`<div class="pf-c-banner pf-m-info">
${msg("RBAC is in preview.")} return html` <ak-tabs>
<a href="mailto:hello@goauthentik.io">${msg("Send us feedback!")}</a> <section
</div> slot="page-overview"
<ak-tabs> data-tab-title="${msg("Overview")}"
<section class="pf-c-page__main-section pf-m-no-padding-mobile"
slot="page-overview" >
data-tab-title="${msg("Overview")}" <div class="pf-l-grid pf-m-gutter">
class="pf-c-page__main-section pf-m-no-padding-mobile" <div
> class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-3-col-on-xl pf-m-3-col-on-2xl"
<div class="pf-l-grid pf-m-gutter"> >
<div <div class="pf-c-card__title">${msg("Role Info")}</div>
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-3-col-on-xl pf-m-3-col-on-2xl" <div class="pf-c-card__body">
> ${renderDescriptionList([
<div class="pf-c-card__title">${msg("Role Info")}</div> [msg("Name"), this._role.name],
<div class="pf-c-card__body"> [msg("Edit"), this.renderUpdateControl(this._role)],
<dl class="pf-c-description-list"> ])}
<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text"
>${msg("Name")}</span
>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
${this._role.name}
</div>
</dd>
</div>
</dl>
</div>
</div>
<div
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-9-col-on-xl pf-m-9-col-on-2xl"
>
<div class="pf-c-card__title">
${msg("Assigned global permissions")}
</div>
<div class="pf-c-card__body">
<ak-role-permissions-global-table
roleUuid=${this._role.pk}
></ak-role-permissions-global-table>
</div>
</div>
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
<div class="pf-c-card__title">
${msg("Assigned object permissions")}
</div>
<div class="pf-c-card__body">
<ak-role-permissions-object-table
roleUuid=${this._role.pk}
></ak-role-permissions-object-table>
</div>
</div> </div>
</div> </div>
</section> <div
<ak-rbac-object-permission-page class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-9-col-on-xl pf-m-9-col-on-2xl"
slot="page-permissions" >
data-tab-title="${msg("Permissions")}" <div class="pf-c-card__title">${msg("Changelog")}</div>
model=${RbacPermissionsAssignedByUsersListModelEnum.RbacRole} <div class="pf-c-card__body">
objectPk=${this._role.pk} <ak-object-changelog
.showBanner=${false} targetModelPk=${this.roleId}
></ak-rbac-object-permission-page> targetModelApp="authentik_rbac"
</ak-tabs>`; targetModelName="role"
>
</ak-object-changelog>
</div>
</div>
</div>
</section>
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.RbacRole}
objectPk=${this._role.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;
} }
} }

View File

@ -31,6 +31,10 @@ export class SystemTaskListPage extends TablePage<SystemTask> {
expandable = true; expandable = true;
searchEnabled(): boolean {
return true;
}
@property() @property()
order = "name"; order = "name";

View File

@ -3,13 +3,13 @@ import "@goauthentik/admin/users/UserActiveForm";
import "@goauthentik/admin/users/UserChart"; import "@goauthentik/admin/users/UserChart";
import "@goauthentik/admin/users/UserForm"; import "@goauthentik/admin/users/UserForm";
import "@goauthentik/admin/users/UserPasswordForm"; import "@goauthentik/admin/users/UserPasswordForm";
import "@goauthentik/app/admin/users/UserAssignedGlobalPermissionsTable";
import "@goauthentik/app/admin/users/UserAssignedObjectPermissionsTable";
import { import {
renderRecoveryEmailRequest, renderRecoveryEmailRequest,
requestRecoveryLink, requestRecoveryLink,
} from "@goauthentik/app/admin/users/UserListPage"; } from "@goauthentik/app/admin/users/UserListPage";
import { me } from "@goauthentik/app/common/users"; import { me } from "@goauthentik/app/common/users";
import "@goauthentik/app/elements/oauth/UserAccessTokenList";
import "@goauthentik/app/elements/oauth/UserRefreshTokenList";
import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import "@goauthentik/app/elements/rbac/ObjectPermissionsPage";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { EVENT_REFRESH } from "@goauthentik/common/constants";
@ -31,12 +31,11 @@ import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/ActionButton"; import "@goauthentik/elements/buttons/ActionButton";
import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/buttons/SpinnerButton";
import "@goauthentik/elements/forms/ModalForm"; import "@goauthentik/elements/forms/ModalForm";
import "@goauthentik/elements/oauth/UserRefreshList";
import "@goauthentik/elements/user/SessionList"; import "@goauthentik/elements/user/SessionList";
import "@goauthentik/elements/user/UserConsentList"; import "@goauthentik/elements/user/UserConsentList";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { css, html, nothing } from "lit"; import { TemplateResult, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators.js"; import { customElement, property, state } from "lit/decorators.js";
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css"; import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
@ -227,45 +226,94 @@ export class UserViewPage extends WithCapabilitiesConfig(AKElement) {
renderRecoveryButtons(user: User) { renderRecoveryButtons(user: User) {
return html`<div class="ak-button-collection"> return html`<div class="ak-button-collection">
<ak-forms-modal size=${PFSize.Medium} id="update-password-request"> <ak-forms-modal size=${PFSize.Medium} id="update-password-request">
<span slot="submit">${msg("Update password")}</span> <span slot="submit">${msg("Update password")}</span>
<span slot="header">${msg("Update password")}</span> <span slot="header">${msg("Update password")}</span>
<ak-user-password-form <ak-user-password-form slot="form" .instancePk=${user.pk}></ak-user-password-form>
slot="form" <button slot="trigger" class="pf-c-button pf-m-secondary pf-m-block">
.instancePk=${user.pk} <pf-tooltip position="top" content=${msg("Enter a new password for this user")}>
></ak-user-password-form> ${msg("Set password")}
<button </pf-tooltip>
slot="trigger" </button>
class="pf-c-button pf-m-secondary pf-m-block" </ak-forms-modal>
> <ak-action-button
<pf-tooltip id="reset-password-button"
position="top" class="pf-m-secondary pf-m-block"
content=${msg("Enter a new password for this user")} .apiRequest=${() => requestRecoveryLink(user)}
> >
${msg("Set password")} <pf-tooltip
</pf-tooltip> position="top"
</button> content=${msg("Create a link for this user to reset their password")}
</ak-forms-modal> >
<ak-action-button ${msg("Create Recovery Link")}
id="reset-password-button" </pf-tooltip>
class="pf-m-secondary pf-m-block" </ak-action-button>
.apiRequest=${() => requestRecoveryLink(user)} ${user.email ? renderRecoveryEmailRequest(user) : nothing}
> </div> `;
<pf-tooltip }
position="top"
content=${msg( renderTabCredentialsToken(user: User): TemplateResult {
"Create a link for this user to reset their password", return html`
)} <ak-tabs pageIdentifier="userCredentialsTokens" ?vertical=${true}>
> <section
${msg("Create Recovery Link")} slot="page-sessions"
</pf-tooltip> data-tab-title="${msg("Sessions")}"
</ak-action-button> class="pf-c-page__main-section pf-m-no-padding-mobile"
${user.email ? renderRecoveryEmailRequest(user) : nothing} >
</div> <div class="pf-c-card">
</dd> <div class="pf-c-card__body">
<ak-user-session-list targetUser=${user.username}>
</ak-user-session-list>
</div>
</div> </div>
</dl> </section>
</div> <section
slot="page-consent"
data-tab-title="${msg("Explicit Consent")}"
class="pf-c-page__main-section pf-m-no-padding-mobile"
>
<div class="pf-c-card">
<div class="pf-c-card__body">
<ak-user-consent-list userId=${user.pk}> </ak-user-consent-list>
</div>
</div>
</section>
<section
slot="page-oauth-access"
data-tab-title="${msg("OAuth Access Tokens")}"
class="pf-c-page__main-section pf-m-no-padding-mobile"
>
<div class="pf-c-card">
<div class="pf-c-card__body">
<ak-user-oauth-access-token-list userId=${user.pk}>
</ak-user-oauth-access-token-list>
</div>
</div>
</section>
<section
slot="page-oauth-refresh"
data-tab-title="${msg("OAuth Refresh Tokens")}"
class="pf-c-page__main-section pf-m-no-padding-mobile"
>
<div class="pf-c-card">
<div class="pf-c-card__body">
<ak-user-oauth-refresh-token-list userId=${user.pk}>
</ak-user-oauth-refresh-token-list>
</div>
</div>
</section>
<section
slot="page-mfa-authenticators"
data-tab-title="${msg("MFA Authenticators")}"
class="pf-c-page__main-section pf-m-no-padding-mobile"
>
<div class="pf-c-card">
<div class="pf-c-card__body">
<ak-user-device-table userId=${user.pk}> </ak-user-device-table>
</div>
</div>
</section>
</ak-tabs>
`; `;
} }
@ -326,18 +374,6 @@ export class UserViewPage extends WithCapabilitiesConfig(AKElement) {
</div> </div>
</div> </div>
</section> </section>
<section
slot="page-sessions"
data-tab-title="${msg("Sessions")}"
class="pf-c-page__main-section pf-m-no-padding-mobile"
>
<div class="pf-c-card">
<div class="pf-c-card__body">
<ak-user-session-list targetUser=${this.user.username}>
</ak-user-session-list>
</div>
</div>
</section>
<section <section
slot="page-groups" slot="page-groups"
data-tab-title="${msg("Groups")}" data-tab-title="${msg("Groups")}"
@ -360,78 +396,16 @@ export class UserViewPage extends WithCapabilitiesConfig(AKElement) {
</div> </div>
</div> </div>
</section> </section>
<section <section slot="page-credentials" data-tab-title="${msg("Credentials / Tokens")}">
slot="page-consent" ${this.renderTabCredentialsToken(this.user)}
data-tab-title="${msg("Explicit Consent")}"
class="pf-c-page__main-section pf-m-no-padding-mobile"
>
<div class="pf-c-card">
<div class="pf-c-card__body">
<ak-user-consent-list userId=${this.user.pk}> </ak-user-consent-list>
</div>
</div>
</section>
<section
slot="page-oauth-refresh"
data-tab-title="${msg("OAuth Refresh Tokens")}"
class="pf-c-page__main-section pf-m-no-padding-mobile"
>
<div class="pf-c-card">
<div class="pf-c-card__body">
<ak-user-oauth-refresh-list userId=${this.user.pk}>
</ak-user-oauth-refresh-list>
</div>
</div>
</section>
<section
slot="page-mfa-authenticators"
data-tab-title="${msg("MFA Authenticators")}"
class="pf-c-page__main-section pf-m-no-padding-mobile"
>
<div class="pf-c-card">
<div class="pf-c-card__body">
<ak-user-device-table userId=${this.user.pk}> </ak-user-device-table>
</div>
</div>
</section> </section>
<ak-rbac-object-permission-page <ak-rbac-object-permission-page
slot="page-permissions" slot="page-permissions"
data-tab-title="${msg("Permissions")}" data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.CoreUser} model=${RbacPermissionsAssignedByUsersListModelEnum.CoreUser}
objectPk=${this.user.pk} objectPk=${this.user.pk}
></ak-rbac-object-permission-page>
<div
slot="page-mfa-assigned-permissions"
data-tab-title="${msg("Assigned permissions")}"
class=""
> >
<div class="pf-c-banner pf-m-info"> </ak-rbac-object-permission-page>
${msg("RBAC is in preview.")}
<a href="mailto:hello@goauthentik.io">${msg("Send us feedback!")}</a>
</div>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-l-grid pf-m-gutter">
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
<div class="pf-c-card__title">
${msg("Assigned global permissions")}
</div>
<div class="pf-c-card__body">
<ak-user-assigned-global-permissions-table userId=${this.user.pk}>
</ak-user-assigned-global-permissions-table>
</div>
</div>
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
<div class="pf-c-card__title">
${msg("Assigned object permissions")}
</div>
<div class="pf-c-card__body">
<ak-user-assigned-object-permissions-table userId=${this.user.pk}>
</ak-user-assigned-object-permissions-table>
</div>
</div>
</div>
</section>
</div>
</ak-tabs>`; </ak-tabs>`;
} }
} }

View File

@ -149,3 +149,25 @@ export function adaptCSS(sheet: AdaptableStylesheet[]): CSSStyleSheet[];
export function adaptCSS(sheet: AdaptableStylesheet | AdaptableStylesheet[]): AdaptedStylesheets { export function adaptCSS(sheet: AdaptableStylesheet | AdaptableStylesheet[]): AdaptedStylesheets {
return Array.isArray(sheet) ? sheet.map(_adaptCSS) : _adaptCSS(sheet); return Array.isArray(sheet) ? sheet.map(_adaptCSS) : _adaptCSS(sheet);
} }
const _timeUnits = new Map<Intl.RelativeTimeFormatUnit, number>([
["year", 24 * 60 * 60 * 1000 * 365],
["month", (24 * 60 * 60 * 1000 * 365) / 12],
["day", 24 * 60 * 60 * 1000],
["hour", 60 * 60 * 1000],
["minute", 60 * 1000],
["second", 1000],
]);
export function getRelativeTime(d1: Date, d2: Date = new Date()): string {
const rtf = new Intl.RelativeTimeFormat("default", { numeric: "auto" });
const elapsed = d1.getTime() - d2.getTime();
// "Math.abs" accounts for both "past" & "future" scenarios
for (const [key, value] of _timeUnits) {
if (Math.abs(elapsed) > value || key == "second") {
return rtf.format(Math.round(elapsed / value), key);
}
}
return rtf.format(Math.round(elapsed / 1000), "second");
}

View File

@ -1,5 +1,6 @@
import { EventGeo, EventUser } from "@goauthentik/app/admin/events/utils"; import { EventGeo, EventUser } from "@goauthentik/app/admin/events/utils";
import { actionToLabel } from "@goauthentik/app/common/labels"; import { actionToLabel } from "@goauthentik/app/common/labels";
import { getRelativeTime } from "@goauthentik/app/common/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events"; import { EventWithContext } from "@goauthentik/common/events";
import { uiConfig } from "@goauthentik/common/ui/config"; import { uiConfig } from "@goauthentik/common/ui/config";
@ -46,7 +47,7 @@ export class ObjectChangelog extends Table<Event> {
let modelName = this._targetModelName; let modelName = this._targetModelName;
let appName = this.targetModelApp; let appName = this.targetModelApp;
if (this._targetModelName.indexOf(".") !== -1) { if (this._targetModelName.indexOf(".") !== -1) {
const parts = this._targetModelName.split("."); const parts = this._targetModelName.split(".", 1);
appName = parts[0]; appName = parts[0];
modelName = parts[1]; modelName = parts[1];
} }
@ -77,7 +78,8 @@ export class ObjectChangelog extends Table<Event> {
return [ return [
html`${actionToLabel(item.action)}`, html`${actionToLabel(item.action)}`,
EventUser(item), EventUser(item),
html`<span>${item.created?.toLocaleString()}</span>`, html`<div>${getRelativeTime(item.created)}</div>
<small>${item.created.toLocaleString()}</small>`,
html`<div>${item.clientIp || msg("-")}</div> html`<div>${item.clientIp || msg("-")}</div>
<small>${EventGeo(item)}</small>`, <small>${EventGeo(item)}</small>`,

View File

@ -1,4 +1,5 @@
import { EventUser } from "@goauthentik/app/admin/events/utils"; import { EventUser } from "@goauthentik/app/admin/events/utils";
import { getRelativeTime } from "@goauthentik/app/common/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events"; import { EventWithContext } from "@goauthentik/common/events";
import { actionToLabel } from "@goauthentik/common/labels"; import { actionToLabel } from "@goauthentik/common/labels";
@ -48,7 +49,8 @@ export class UserEvents extends Table<Event> {
return [ return [
html`${actionToLabel(item.action)}`, html`${actionToLabel(item.action)}`,
EventUser(item), EventUser(item),
html`<span>${item.created?.toLocaleString()}</span>`, html`<div>${getRelativeTime(item.created)}</div>
<small>${item.created.toLocaleString()}</small>`,
html`<span>${item.clientIp || msg("-")}</span>`, html`<span>${item.clientIp || msg("-")}</span>`,
]; ];
} }

View File

@ -84,6 +84,7 @@ export class AggregateCard extends AKElement {
${this.renderInner()} ${this.renderInner()}
${this.subtext ? html`<p class="subtext">${this.subtext}</p>` : html``} ${this.subtext ? html`<p class="subtext">${this.subtext}</p>` : html``}
</div> </div>
<div class="pf-c-card__footer">&nbsp;</div>
</div>`; </div>`;
} }
} }

View File

@ -1,3 +1,4 @@
import { getRelativeTime } from "@goauthentik/app/common/utils";
import { EVENT_REFRESH, EVENT_THEME_CHANGE } from "@goauthentik/common/constants"; import { EVENT_REFRESH, EVENT_THEME_CHANGE } from "@goauthentik/common/constants";
import { AKElement } from "@goauthentik/elements/Base"; import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/EmptyState"; import "@goauthentik/elements/EmptyState";
@ -18,7 +19,7 @@ import { ArcElement, BarElement } from "chart.js";
import { LinearScale, TimeScale } from "chart.js"; import { LinearScale, TimeScale } from "chart.js";
import "chartjs-adapter-moment"; import "chartjs-adapter-moment";
import { msg, str } from "@lit/localize"; import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit"; import { CSSResult, TemplateResult, css, html } from "lit";
import { property, state } from "lit/decorators.js"; import { property, state } from "lit/decorators.js";
@ -161,9 +162,7 @@ export abstract class AKChart<T> extends AKElement {
timeTickCallback(tickValue: string | number, index: number, ticks: Tick[]): string { timeTickCallback(tickValue: string | number, index: number, ticks: Tick[]): string {
const valueStamp = ticks[index]; const valueStamp = ticks[index];
const delta = Date.now() - valueStamp.value; return getRelativeTime(new Date(valueStamp.value));
const ago = Math.round(delta / 1000 / 3600);
return msg(str`${ago} hour(s) ago`);
} }
getOptions(): ChartOptions { getOptions(): ChartOptions {

View File

@ -25,8 +25,11 @@ export class APIDrawer extends AKElement {
PFContent, PFContent,
PFDropdown, PFDropdown,
css` css`
:host {
--header-height: 114px;
}
.pf-c-notification-drawer__header { .pf-c-notification-drawer__header {
height: 114px; height: var(--header-height);
align-items: center; align-items: center;
} }
.pf-c-notification-drawer__header-action, .pf-c-notification-drawer__header-action,
@ -41,6 +44,9 @@ export class APIDrawer extends AKElement {
.pf-c-notification-drawer__body { .pf-c-notification-drawer__body {
overflow-x: hidden; overflow-x: hidden;
} }
.pf-c-notification-drawer__list {
max-height: calc(100vh - var(--header-height));
}
`, `,
]; ];
} }

View File

@ -0,0 +1,93 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { uiConfig } from "@goauthentik/common/ui/config";
import "@goauthentik/components/ak-status-label";
import "@goauthentik/elements/forms/DeleteBulkForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { Table, TableColumn } from "@goauthentik/elements/table/Table";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";
import { ExpiringBaseGrantModel, Oauth2Api, TokenModel } from "@goauthentik/api";
@customElement("ak-user-oauth-access-token-list")
export class UserOAuthAccessTokenList extends Table<TokenModel> {
expandable = true;
@property({ type: Number })
userId?: number;
static get styles(): CSSResult[] {
return super.styles.concat(PFFlex);
}
async apiEndpoint(page: number): Promise<PaginatedResponse<TokenModel>> {
return new Oauth2Api(DEFAULT_CONFIG).oauth2AccessTokensList({
user: this.userId,
ordering: "expires",
page: page,
pageSize: (await uiConfig()).pagination.perPage,
});
}
checkbox = true;
order = "-expires";
columns(): TableColumn[] {
return [
new TableColumn(msg("Provider"), "provider"),
new TableColumn(msg("Revoked?"), "revoked"),
new TableColumn(msg("Expires"), "expires"),
new TableColumn(msg("Scopes"), "scope"),
];
}
renderExpanded(item: TokenModel): TemplateResult {
return html` <td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
<div class="pf-l-flex">
<div class="pf-l-flex__item">
<h3>${msg("ID Token")}</h3>
<pre>${item.idToken}</pre>
</div>
</div>
</div>
</td>
<td></td>
<td></td>`;
}
renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Refresh Tokens(s)")}
.objects=${this.selectedElements}
.usedBy=${(item: ExpiringBaseGrantModel) => {
return new Oauth2Api(DEFAULT_CONFIG).oauth2RefreshTokensUsedByList({
id: item.pk,
});
}}
.delete=${(item: ExpiringBaseGrantModel) => {
return new Oauth2Api(DEFAULT_CONFIG).oauth2RefreshTokensDestroy({
id: item.pk,
});
}}
>
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
${msg("Delete")}
</button>
</ak-forms-delete-bulk>`;
}
row(item: TokenModel): TemplateResult[] {
return [
html`<a href="#/core/providers/${item.provider?.pk}"> ${item.provider?.name} </a>`,
html`<ak-status-label type="warning" ?good=${item.revoked}></ak-status-label>`,
html`${item.expires?.toLocaleString()}`,
html`${item.scope.join(", ")}`,
];
}
}

View File

@ -13,8 +13,8 @@ import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";
import { ExpiringBaseGrantModel, Oauth2Api, TokenModel } from "@goauthentik/api"; import { ExpiringBaseGrantModel, Oauth2Api, TokenModel } from "@goauthentik/api";
@customElement("ak-user-oauth-refresh-list") @customElement("ak-user-oauth-refresh-token-list")
export class UserOAuthRefreshList extends Table<TokenModel> { export class UserOAuthRefreshTokenList extends Table<TokenModel> {
expandable = true; expandable = true;
@property({ type: Number }) @property({ type: Number })

View File

@ -38,6 +38,7 @@ export class ObjectPermissionsPageForm extends ModelForm<unknown, string> {
.model=${this.model} .model=${this.model}
.objectPk=${this.objectPk} .objectPk=${this.objectPk}
slot="form" slot="form"
.embedded=${true}
> >
</ak-rbac-object-permission-page>`; </ak-rbac-object-permission-page>`;
} }

View File

@ -1,10 +1,14 @@
import "@goauthentik/app/admin/roles/RoleAssignedGlobalPermissionsTable";
import "@goauthentik/app/admin/roles/RoleAssignedObjectPermissionTable";
import "@goauthentik/app/admin/users/UserAssignedGlobalPermissionsTable";
import "@goauthentik/app/admin/users/UserAssignedObjectPermissionsTable";
import { AKElement } from "@goauthentik/app/elements/Base"; import { AKElement } from "@goauthentik/app/elements/Base";
import "@goauthentik/app/elements/rbac/RoleObjectPermissionTable"; import "@goauthentik/app/elements/rbac/RoleObjectPermissionTable";
import "@goauthentik/app/elements/rbac/UserObjectPermissionTable"; import "@goauthentik/app/elements/rbac/UserObjectPermissionTable";
import "@goauthentik/elements/Tabs"; import "@goauthentik/elements/Tabs";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit"; import { html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property } from "lit/decorators.js";
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css"; import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
@ -24,20 +28,26 @@ export class ObjectPermissionPage extends AKElement {
objectPk?: string | number; objectPk?: string | number;
@property({ type: Boolean }) @property({ type: Boolean })
showBanner = true; embedded = false;
static get styles(): CSSResult[] { static get styles() {
return [PFBase, PFGrid, PFPage, PFCard, PFBanner]; return [PFBase, PFGrid, PFPage, PFCard, PFBanner];
} }
render(): TemplateResult { render() {
return html`${this.showBanner return html`${!this.embedded
? html`<div class="pf-c-banner pf-m-info"> ? html`<div class="pf-c-banner pf-m-info">
${msg("RBAC is in preview.")} ${msg("RBAC is in preview.")}
<a href="mailto:hello@goauthentik.io">${msg("Send us feedback!")}</a> <a href="mailto:hello@goauthentik.io">${msg("Send us feedback!")}</a>
</div>` </div>`
: html``} : nothing}
<ak-tabs pageIdentifier="permissionPage"> <ak-tabs pageIdentifier="permissionPage" ?vertical=${!this.embedded}>
${this.model === RbacPermissionsAssignedByUsersListModelEnum.CoreUser
? this.renderCoreUser()
: nothing}
${this.model === RbacPermissionsAssignedByUsersListModelEnum.RbacRole
? this.renderRbacRole()
: nothing}
<section <section
slot="page-object-user" slot="page-object-user"
data-tab-title="${msg("User Object Permissions")}" data-tab-title="${msg("User Object Permissions")}"
@ -45,7 +55,10 @@ export class ObjectPermissionPage extends AKElement {
> >
<div class="pf-l-grid pf-m-gutter"> <div class="pf-l-grid pf-m-gutter">
<div class="pf-c-card pf-l-grid__item pf-m-12-col"> <div class="pf-c-card pf-l-grid__item pf-m-12-col">
<div class="pf-c-card__title">User Object Permissions</div> <div class="pf-c-card__title">${msg("User Object Permissions")}</div>
<div class="pf-c-card__body">
${msg("Permissions set on users which affect this object.")}
</div>
<div class="pf-c-card__body"> <div class="pf-c-card__body">
<ak-rbac-user-object-permission-table <ak-rbac-user-object-permission-table
.model=${this.model} .model=${this.model}
@ -63,7 +76,10 @@ export class ObjectPermissionPage extends AKElement {
> >
<div class="pf-l-grid pf-m-gutter"> <div class="pf-l-grid pf-m-gutter">
<div class="pf-c-card pf-l-grid__item pf-m-12-col"> <div class="pf-c-card pf-l-grid__item pf-m-12-col">
<div class="pf-c-card__title">Role Object Permissions</div> <div class="pf-c-card__title">${msg("Role Object Permissions")}</div>
<div class="pf-c-card__body">
${msg("Permissions set on roles which affect this object.")}
</div>
<div class="pf-c-card__body"> <div class="pf-c-card__body">
<ak-rbac-role-object-permission-table <ak-rbac-role-object-permission-table
.model=${this.model} .model=${this.model}
@ -76,4 +92,98 @@ export class ObjectPermissionPage extends AKElement {
</section> </section>
</ak-tabs>`; </ak-tabs>`;
} }
renderCoreUser() {
return html`
<div
slot="page-assigned-global-permissions"
data-tab-title="${msg("Assigned global permissions")}"
>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
<div class="pf-c-card__title">${msg("Assigned global permissions")}</div>
<div class="pf-c-card__body">
${msg(
"Permissions assigned to this user which affect all object instances of a given type.",
)}
</div>
<div class="pf-c-card__body">
<ak-user-assigned-global-permissions-table
userId=${this.objectPk as number}
>
</ak-user-assigned-global-permissions-table>
</div>
</div>
</section>
</div>
<div
slot="page-assigned-object-permissions"
data-tab-title="${msg("Assigned object permissions")}"
>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
<div class="pf-c-card__title">${msg("Assigned object permissions")}</div>
<div class="pf-c-card__body">
${msg(
"Permissions assigned to this user affecting specific object instances.",
)}
</div>
<div class="pf-c-card__body">
<ak-user-assigned-object-permissions-table
userId=${this.objectPk as number}
>
</ak-user-assigned-object-permissions-table>
</div>
</div>
</section>
</div>
`;
}
renderRbacRole() {
return html`
<div
slot="page-assigned-global-permissions"
data-tab-title="${msg("Assigned global permissions")}"
>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
<div class="pf-c-card__title">${msg("Assigned global permissions")}</div>
<div class="pf-c-card__body">
${msg(
"Permissions assigned to this role which affect all object instances of a given type.",
)}
</div>
<div class="pf-c-card__body">
<ak-role-assigned-global-permissions-table
roleUuid=${this.objectPk as string}
>
</ak-role-assigned-global-permissions-table>
</div>
</div>
</section>
</div>
<div
slot="page-assigned-object-permissions"
data-tab-title="${msg("Assigned object permissions")}"
>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
<div class="pf-c-card__title">${msg("Assigned object permissions")}</div>
<div class="pf-c-card__body">
${msg(
"Permissions assigned to this user affecting specific object instances.",
)}
</div>
<div class="pf-c-card__body">
<ak-role-assigned-object-permissions-table
roleUuid=${this.objectPk as string}
>
</ak-role-assigned-object-permissions-table>
</div>
</div>
</section>
</div>
`;
}
} }

View File

@ -180,6 +180,12 @@ export abstract class Table<T> extends AKElement implements TableLike {
.pf-c-table tbody .pf-c-table__check input { .pf-c-table tbody .pf-c-table__check input {
margin-top: calc(var(--pf-c-table__check--input--MarginTop) + 1px); margin-top: calc(var(--pf-c-table__check--input--MarginTop) + 1px);
} }
.pf-c-toolbar__content {
row-gap: var(--pf-global--spacer--sm);
}
.pf-c-toolbar__item .pf-c-input-group {
padding: 0 var(--pf-global--spacer--sm);
}
`, `,
]; ];
} }

View File

@ -1,8 +1,10 @@
import { getRelativeTime } from "@goauthentik/app/common/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { uiConfig } from "@goauthentik/common/ui/config"; import { uiConfig } from "@goauthentik/common/ui/config";
import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/DeleteBulkForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { Table, TableColumn } from "@goauthentik/elements/table/Table"; import { Table, TableColumn } from "@goauthentik/elements/table/Table";
import getUnicodeFlagIcon from "country-flag-icons/unicode";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -31,6 +33,7 @@ export class AuthenticatedSessionList extends Table<AuthenticatedSession> {
columns(): TableColumn[] { columns(): TableColumn[] {
return [ return [
new TableColumn(msg("Last IP"), "last_ip"), new TableColumn(msg("Last IP"), "last_ip"),
new TableColumn(msg("Last used"), "last_used"),
new TableColumn(msg("Expires"), "expires"), new TableColumn(msg("Expires"), "expires"),
]; ];
} }
@ -66,10 +69,17 @@ export class AuthenticatedSessionList extends Table<AuthenticatedSession> {
row(item: AuthenticatedSession): TemplateResult[] { row(item: AuthenticatedSession): TemplateResult[] {
return [ return [
html`<div> html`<div>
${item.current ? html`${msg("(Current session)")}&nbsp;` : html``}${item.lastIp} ${item.current ? html`${msg("(Current session)")}&nbsp;` : html``}
${item.lastIp}
${item.geoIp?.country
? html`&nbsp;${getUnicodeFlagIcon(item.geoIp.country)} `
: html``}
</div> </div>
<small>${item.userAgent.userAgent?.family}, ${item.userAgent.os?.family}</small>`, <small>${item.userAgent.userAgent?.family}, ${item.userAgent.os?.family}</small>`,
html`${item.expires?.toLocaleString()}`, html`<div>${getRelativeTime(item.lastUsed)}</div>
<small>${item.lastUsed?.toLocaleString()}</small>`,
html`<div>${getRelativeTime(item.expires || new Date())}</div>
<small>${item.expires?.toLocaleString()}</small>`,
]; ];
} }
} }

View File

@ -10,6 +10,7 @@ import { TemplateResult, html } from "lit";
import { customElement, state } from "lit/decorators.js"; import { customElement, state } from "lit/decorators.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css"; import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFDivider from "@patternfly/patternfly/components/Divider/divider.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css"; import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFLogin from "@patternfly/patternfly/components/Login/login.css"; import PFLogin from "@patternfly/patternfly/components/Login/login.css";
@ -32,7 +33,7 @@ export class PlexLoginInit extends BaseStage<
authUrl?: string; authUrl?: string;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return [PFBase, PFLogin, PFForm, PFFormControl, PFButton, PFTitle]; return [PFBase, PFLogin, PFForm, PFFormControl, PFButton, PFTitle, PFDivider];
} }
async firstUpdated(): Promise<void> { async firstUpdated(): Promise<void> {
@ -76,7 +77,7 @@ export class PlexLoginInit extends BaseStage<
header=${msg("Waiting for authentication...")} header=${msg("Waiting for authentication...")}
> >
</ak-empty-state> </ak-empty-state>
<hr /> <hr class="pf-c-divider" />
<p>${msg("If no Plex popup opens, click the button below.")}</p> <p>${msg("If no Plex popup opens, click the button below.")}</p>
<button <button
class="pf-c-button pf-m-block pf-m-primary" class="pf-c-button pf-m-block pf-m-primary"

View File

@ -8,6 +8,7 @@ import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js"; import { ifDefined } from "lit/directives/if-defined.js";
import PFDivider from "@patternfly/patternfly/components/Divider/divider.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css"; import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFList from "@patternfly/patternfly/components/List/list.css"; import PFList from "@patternfly/patternfly/components/List/list.css";
@ -26,6 +27,7 @@ export class AccessDeniedIcon extends AKElement {
return [ return [
PFBase, PFBase,
PFTitle, PFTitle,
PFDivider,
css` css`
.big-icon { .big-icon {
display: flex; display: flex;
@ -51,7 +53,7 @@ export class AccessDeniedIcon extends AKElement {
</p> </p>
<h3 class="pf-c-title pf-m-3xl reason">${msg("Request has been denied.")}</h3> <h3 class="pf-c-title pf-m-3xl reason">${msg("Request has been denied.")}</h3>
${this.errorMessage ${this.errorMessage
? html`<hr /> ? html` <hr class="pf-c-divider" />
<p>${this.errorMessage}</p>` <p>${this.errorMessage}</p>`
: html``} : html``}
</div>`; </div>`;

View File

@ -121,6 +121,9 @@ export class UserInterface extends Interface {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.pf-c-drawer__main {
max-height: calc(100vh - 76px);
}
`, `,
]; ];
} }

View File

@ -427,12 +427,6 @@
<source>No workers connected. Background tasks will not run.</source> <source>No workers connected. Background tasks will not run.</source>
<target>Keine Worker verbunden. Hintergrundaufgaben werden nicht ausgeführt.</target> <target>Keine Worker verbunden. Hintergrundaufgaben werden nicht ausgeführt.</target>
</trans-unit> </trans-unit>
<trans-unit id="s2ed8eb02525a920a">
<source><x id="0" equiv-text="${ago}"/> hour(s) ago</source>
</trans-unit>
<trans-unit id="s1f1c857c0c4250e4">
<source><x id="0" equiv-text="${ago}"/> day(s) ago</source>
</trans-unit>
<trans-unit id="s11bc220e8fa9d797"> <trans-unit id="s11bc220e8fa9d797">
<source>Authorizations</source> <source>Authorizations</source>
<target>Berechtigungen</target> <target>Berechtigungen</target>
@ -5834,9 +5828,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s1455753daa00f1bc"> <trans-unit id="s1455753daa00f1bc">
<source>User doesn't have view permission so description cannot be retrieved.</source> <source>User doesn't have view permission so description cannot be retrieved.</source>
</trans-unit> </trans-unit>
<trans-unit id="sa3a3e09b88ed9791">
<source>Assigned permissions</source>
</trans-unit>
<trans-unit id="s9cc631505c17b028"> <trans-unit id="s9cc631505c17b028">
<source>Assigned global permissions</source> <source>Assigned global permissions</source>
</trans-unit> </trans-unit>
@ -6372,6 +6363,30 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sb2275335377069aa"> <trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source> <source>This feature requires an enterprise license.</source>
</trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="saada50e84c7c646d">
<source>Permissions set on users which affect this object.</source>
</trans-unit>
<trans-unit id="s37d1b74df205b528">
<source>Permissions set on roles which affect this object.</source>
</trans-unit>
<trans-unit id="sc443bfff8a5dd106">
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
</trans-unit>
<trans-unit id="s3eba09a350b7d6c9">
<source>Permissions assigned to this user affecting specific object instances.</source>
</trans-unit>
<trans-unit id="s17032e57ba222d2f">
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -433,16 +433,6 @@
<source>No workers connected. Background tasks will not run.</source> <source>No workers connected. Background tasks will not run.</source>
<target>No workers connected. Background tasks will not run.</target> <target>No workers connected. Background tasks will not run.</target>
</trans-unit> </trans-unit>
<trans-unit id="s2ed8eb02525a920a">
<source><x id="0" equiv-text="${ago}"/> hour(s) ago</source>
<target>
<x id="0" equiv-text="${ago}"/>hour(s) ago</target>
</trans-unit>
<trans-unit id="s1f1c857c0c4250e4">
<source><x id="0" equiv-text="${ago}"/> day(s) ago</source>
<target>
<x id="0" equiv-text="${ago}"/>day(s) ago</target>
</trans-unit>
<trans-unit id="s11bc220e8fa9d797"> <trans-unit id="s11bc220e8fa9d797">
<source>Authorizations</source> <source>Authorizations</source>
<target>Authorizations</target> <target>Authorizations</target>
@ -6109,9 +6099,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s1455753daa00f1bc"> <trans-unit id="s1455753daa00f1bc">
<source>User doesn't have view permission so description cannot be retrieved.</source> <source>User doesn't have view permission so description cannot be retrieved.</source>
</trans-unit> </trans-unit>
<trans-unit id="sa3a3e09b88ed9791">
<source>Assigned permissions</source>
</trans-unit>
<trans-unit id="s9cc631505c17b028"> <trans-unit id="s9cc631505c17b028">
<source>Assigned global permissions</source> <source>Assigned global permissions</source>
</trans-unit> </trans-unit>
@ -6647,6 +6634,30 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sb2275335377069aa"> <trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source> <source>This feature requires an enterprise license.</source>
</trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="saada50e84c7c646d">
<source>Permissions set on users which affect this object.</source>
</trans-unit>
<trans-unit id="s37d1b74df205b528">
<source>Permissions set on roles which affect this object.</source>
</trans-unit>
<trans-unit id="sc443bfff8a5dd106">
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
</trans-unit>
<trans-unit id="s3eba09a350b7d6c9">
<source>Permissions assigned to this user affecting specific object instances.</source>
</trans-unit>
<trans-unit id="s17032e57ba222d2f">
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -420,12 +420,6 @@
<source>No workers connected. Background tasks will not run.</source> <source>No workers connected. Background tasks will not run.</source>
<target>No hay trabajadores conectados. No se ejecutarán tareas en segundo plano.</target> <target>No hay trabajadores conectados. No se ejecutarán tareas en segundo plano.</target>
</trans-unit> </trans-unit>
<trans-unit id="s2ed8eb02525a920a">
<source><x id="0" equiv-text="${ago}"/> hour(s) ago</source>
</trans-unit>
<trans-unit id="s1f1c857c0c4250e4">
<source><x id="0" equiv-text="${ago}"/> day(s) ago</source>
</trans-unit>
<trans-unit id="s11bc220e8fa9d797"> <trans-unit id="s11bc220e8fa9d797">
<source>Authorizations</source> <source>Authorizations</source>
<target>Autorizaciones</target> <target>Autorizaciones</target>
@ -5750,9 +5744,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s1455753daa00f1bc"> <trans-unit id="s1455753daa00f1bc">
<source>User doesn't have view permission so description cannot be retrieved.</source> <source>User doesn't have view permission so description cannot be retrieved.</source>
</trans-unit> </trans-unit>
<trans-unit id="sa3a3e09b88ed9791">
<source>Assigned permissions</source>
</trans-unit>
<trans-unit id="s9cc631505c17b028"> <trans-unit id="s9cc631505c17b028">
<source>Assigned global permissions</source> <source>Assigned global permissions</source>
</trans-unit> </trans-unit>
@ -6288,6 +6279,30 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sb2275335377069aa"> <trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source> <source>This feature requires an enterprise license.</source>
</trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="saada50e84c7c646d">
<source>Permissions set on users which affect this object.</source>
</trans-unit>
<trans-unit id="s37d1b74df205b528">
<source>Permissions set on roles which affect this object.</source>
</trans-unit>
<trans-unit id="sc443bfff8a5dd106">
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
</trans-unit>
<trans-unit id="s3eba09a350b7d6c9">
<source>Permissions assigned to this user affecting specific object instances.</source>
</trans-unit>
<trans-unit id="s17032e57ba222d2f">
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -537,18 +537,6 @@
<source>No workers connected. Background tasks will not run.</source> <source>No workers connected. Background tasks will not run.</source>
<target>Aucun worker connecté. Les tâches de fond ne tourneront pas.</target> <target>Aucun worker connecté. Les tâches de fond ne tourneront pas.</target>
</trans-unit>
<trans-unit id="s2ed8eb02525a920a">
<source><x id="0" equiv-text="${ago}"/> hour(s) ago</source>
<target>
Il y a <x id="0" equiv-text="${ago}"/> heure(s)</target>
</trans-unit>
<trans-unit id="s1f1c857c0c4250e4">
<source><x id="0" equiv-text="${ago}"/> day(s) ago</source>
<target>
Il y a <x id="0" equiv-text="${ago}"/> jour(s)</target>
</trans-unit> </trans-unit>
<trans-unit id="s11bc220e8fa9d797"> <trans-unit id="s11bc220e8fa9d797">
<source>Authorizations</source> <source>Authorizations</source>
@ -7666,10 +7654,6 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<source>User doesn't have view permission so description cannot be retrieved.</source> <source>User doesn't have view permission so description cannot be retrieved.</source>
<target>L'utilisateur n'a pas les permissions de lecture, la description ne peut donc pas être récupérée.</target> <target>L'utilisateur n'a pas les permissions de lecture, la description ne peut donc pas être récupérée.</target>
</trans-unit> </trans-unit>
<trans-unit id="sa3a3e09b88ed9791">
<source>Assigned permissions</source>
<target>Permissions assignées</target>
</trans-unit>
<trans-unit id="s9cc631505c17b028"> <trans-unit id="s9cc631505c17b028">
<source>Assigned global permissions</source> <source>Assigned global permissions</source>
<target>Permissions globales assignées</target> <target>Permissions globales assignées</target>
@ -8375,6 +8359,30 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
</trans-unit> </trans-unit>
<trans-unit id="sb2275335377069aa"> <trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source> <source>This feature requires an enterprise license.</source>
</trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="saada50e84c7c646d">
<source>Permissions set on users which affect this object.</source>
</trans-unit>
<trans-unit id="s37d1b74df205b528">
<source>Permissions set on roles which affect this object.</source>
</trans-unit>
<trans-unit id="sc443bfff8a5dd106">
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
</trans-unit>
<trans-unit id="s3eba09a350b7d6c9">
<source>Permissions assigned to this user affecting specific object instances.</source>
</trans-unit>
<trans-unit id="s17032e57ba222d2f">
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -536,18 +536,6 @@
<source>No workers connected. Background tasks will not run.</source> <source>No workers connected. Background tasks will not run.</source>
<target>연결된 워커가 없습니다. 백그라운드 작업이 실행되지 않습니다.</target> <target>연결된 워커가 없습니다. 백그라운드 작업이 실행되지 않습니다.</target>
</trans-unit>
<trans-unit id="s2ed8eb02525a920a">
<source><x id="0" equiv-text="${ago}"/> hour(s) ago</source>
<target>
<x id="0" equiv-text="${ago}"/>시간 전</target>
</trans-unit>
<trans-unit id="s1f1c857c0c4250e4">
<source><x id="0" equiv-text="${ago}"/> day(s) ago</source>
<target>
<x id="0" equiv-text="${ago}"/> 일 전</target>
</trans-unit> </trans-unit>
<trans-unit id="s11bc220e8fa9d797"> <trans-unit id="s11bc220e8fa9d797">
<source>Authorizations</source> <source>Authorizations</source>
@ -7629,10 +7617,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>User doesn't have view permission so description cannot be retrieved.</source> <source>User doesn't have view permission so description cannot be retrieved.</source>
<target>사용자에게 보기 권한이 없으므로 설명을 검색할 수 없습니다.</target> <target>사용자에게 보기 권한이 없으므로 설명을 검색할 수 없습니다.</target>
</trans-unit> </trans-unit>
<trans-unit id="sa3a3e09b88ed9791">
<source>Assigned permissions</source>
<target>할당된 권한</target>
</trans-unit>
<trans-unit id="s9cc631505c17b028"> <trans-unit id="s9cc631505c17b028">
<source>Assigned global permissions</source> <source>Assigned global permissions</source>
<target>전역 권한 할당</target> <target>전역 권한 할당</target>
@ -8245,6 +8229,30 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sb2275335377069aa"> <trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source> <source>This feature requires an enterprise license.</source>
</trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="saada50e84c7c646d">
<source>Permissions set on users which affect this object.</source>
</trans-unit>
<trans-unit id="s37d1b74df205b528">
<source>Permissions set on roles which affect this object.</source>
</trans-unit>
<trans-unit id="sc443bfff8a5dd106">
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
</trans-unit>
<trans-unit id="s3eba09a350b7d6c9">
<source>Permissions assigned to this user affecting specific object instances.</source>
</trans-unit>
<trans-unit id="s17032e57ba222d2f">
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -530,16 +530,6 @@
<source>No workers connected. Background tasks will not run.</source> <source>No workers connected. Background tasks will not run.</source>
<target>Geen medewerkers verbonden. Achtergrondtaken worden niet uitgevoerd.</target> <target>Geen medewerkers verbonden. Achtergrondtaken worden niet uitgevoerd.</target>
</trans-unit>
<trans-unit id="s2ed8eb02525a920a">
<source><x id="0" equiv-text="${ago}"/> hour(s) ago</source>
<target><x id="0" equiv-text="${ago}"/> uur geleden</target>
</trans-unit>
<trans-unit id="s1f1c857c0c4250e4">
<source><x id="0" equiv-text="${ago}"/> day(s) ago</source>
<target><x id="0" equiv-text="${ago}"/> dag(en) geleden</target>
</trans-unit> </trans-unit>
<trans-unit id="s11bc220e8fa9d797"> <trans-unit id="s11bc220e8fa9d797">
<source>Authorizations</source> <source>Authorizations</source>
@ -7769,9 +7759,6 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
<trans-unit id="se5c795faf2c07514"> <trans-unit id="se5c795faf2c07514">
<source>Create Recovery Link</source> <source>Create Recovery Link</source>
</trans-unit> </trans-unit>
<trans-unit id="sa3a3e09b88ed9791">
<source>Assigned permissions</source>
</trans-unit>
<trans-unit id="s9cc631505c17b028"> <trans-unit id="s9cc631505c17b028">
<source>Assigned global permissions</source> <source>Assigned global permissions</source>
</trans-unit> </trans-unit>
@ -8085,6 +8072,30 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
</trans-unit> </trans-unit>
<trans-unit id="sb2275335377069aa"> <trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source> <source>This feature requires an enterprise license.</source>
</trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="saada50e84c7c646d">
<source>Permissions set on users which affect this object.</source>
</trans-unit>
<trans-unit id="s37d1b74df205b528">
<source>Permissions set on roles which affect this object.</source>
</trans-unit>
<trans-unit id="sc443bfff8a5dd106">
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
</trans-unit>
<trans-unit id="s3eba09a350b7d6c9">
<source>Permissions assigned to this user affecting specific object instances.</source>
</trans-unit>
<trans-unit id="s17032e57ba222d2f">
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -429,12 +429,6 @@
<source>No workers connected. Background tasks will not run.</source> <source>No workers connected. Background tasks will not run.</source>
<target>Brak połączonych workerów. Zadania w tle nie będą działać.</target> <target>Brak połączonych workerów. Zadania w tle nie będą działać.</target>
</trans-unit> </trans-unit>
<trans-unit id="s2ed8eb02525a920a">
<source><x id="0" equiv-text="${ago}"/> hour(s) ago</source>
</trans-unit>
<trans-unit id="s1f1c857c0c4250e4">
<source><x id="0" equiv-text="${ago}"/> day(s) ago</source>
</trans-unit>
<trans-unit id="s11bc220e8fa9d797"> <trans-unit id="s11bc220e8fa9d797">
<source>Authorizations</source> <source>Authorizations</source>
<target>Autoryzacje</target> <target>Autoryzacje</target>
@ -5957,9 +5951,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s1455753daa00f1bc"> <trans-unit id="s1455753daa00f1bc">
<source>User doesn't have view permission so description cannot be retrieved.</source> <source>User doesn't have view permission so description cannot be retrieved.</source>
</trans-unit> </trans-unit>
<trans-unit id="sa3a3e09b88ed9791">
<source>Assigned permissions</source>
</trans-unit>
<trans-unit id="s9cc631505c17b028"> <trans-unit id="s9cc631505c17b028">
<source>Assigned global permissions</source> <source>Assigned global permissions</source>
</trans-unit> </trans-unit>
@ -6495,6 +6486,30 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sb2275335377069aa"> <trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source> <source>This feature requires an enterprise license.</source>
</trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="saada50e84c7c646d">
<source>Permissions set on users which affect this object.</source>
</trans-unit>
<trans-unit id="s37d1b74df205b528">
<source>Permissions set on roles which affect this object.</source>
</trans-unit>
<trans-unit id="sc443bfff8a5dd106">
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
</trans-unit>
<trans-unit id="s3eba09a350b7d6c9">
<source>Permissions assigned to this user affecting specific object instances.</source>
</trans-unit>
<trans-unit id="s17032e57ba222d2f">
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -531,16 +531,6 @@
<source>No workers connected. Background tasks will not run.</source> <source>No workers connected. Background tasks will not run.</source>
<target>Ńō ŵōŕķēŕś ćōńńēćţēď. ßàćķĝŕōũńď ţàśķś ŵĩĺĺ ńōţ ŕũń.</target> <target>Ńō ŵōŕķēŕś ćōńńēćţēď. ßàćķĝŕōũńď ţàśķś ŵĩĺĺ ńōţ ŕũń.</target>
</trans-unit>
<trans-unit id="s2ed8eb02525a920a">
<source><x id="0" equiv-text="${ago}"/> hour(s) ago</source>
<target><x id="0" equiv-text="${ago}"/> ĥōũŕ(ś) àĝō</target>
</trans-unit>
<trans-unit id="s1f1c857c0c4250e4">
<source><x id="0" equiv-text="${ago}"/> day(s) ago</source>
<target><x id="0" equiv-text="${ago}"/> ďàŷ(ś) àĝō</target>
</trans-unit> </trans-unit>
<trans-unit id="s11bc220e8fa9d797"> <trans-unit id="s11bc220e8fa9d797">
<source>Authorizations</source> <source>Authorizations</source>
@ -7609,10 +7599,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>User doesn't have view permission so description cannot be retrieved.</source> <source>User doesn't have view permission so description cannot be retrieved.</source>
<target>Ũśēŕ ďōēśń'ţ ĥàvē vĩēŵ ƥēŕmĩśśĩōń śō ďēśćŕĩƥţĩōń ćàńńōţ ƀē ŕēţŕĩēvēď.</target> <target>Ũśēŕ ďōēśń'ţ ĥàvē vĩēŵ ƥēŕmĩśśĩōń śō ďēśćŕĩƥţĩōń ćàńńōţ ƀē ŕēţŕĩēvēď.</target>
</trans-unit> </trans-unit>
<trans-unit id="sa3a3e09b88ed9791">
<source>Assigned permissions</source>
<target>Àśśĩĝńēď ƥēŕmĩśśĩōńś</target>
</trans-unit>
<trans-unit id="s9cc631505c17b028"> <trans-unit id="s9cc631505c17b028">
<source>Assigned global permissions</source> <source>Assigned global permissions</source>
<target>Àśśĩĝńēď ĝĺōƀàĺ ƥēŕmĩśśĩōńś</target> <target>Àśśĩĝńēď ĝĺōƀàĺ ƥēŕmĩśśĩōńś</target>
@ -8220,4 +8206,28 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sb2275335377069aa"> <trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source> <source>This feature requires an enterprise license.</source>
</trans-unit> </trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="saada50e84c7c646d">
<source>Permissions set on users which affect this object.</source>
</trans-unit>
<trans-unit id="s37d1b74df205b528">
<source>Permissions set on roles which affect this object.</source>
</trans-unit>
<trans-unit id="sc443bfff8a5dd106">
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
</trans-unit>
<trans-unit id="s3eba09a350b7d6c9">
<source>Permissions assigned to this user affecting specific object instances.</source>
</trans-unit>
<trans-unit id="s17032e57ba222d2f">
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
</trans-unit>
</body></file></xliff> </body></file></xliff>

View File

@ -420,12 +420,6 @@
<source>No workers connected. Background tasks will not run.</source> <source>No workers connected. Background tasks will not run.</source>
<target>İşçi bağlantısı yok. Arka plan görevleri çalışmaz.</target> <target>İşçi bağlantısı yok. Arka plan görevleri çalışmaz.</target>
</trans-unit> </trans-unit>
<trans-unit id="s2ed8eb02525a920a">
<source><x id="0" equiv-text="${ago}"/> hour(s) ago</source>
</trans-unit>
<trans-unit id="s1f1c857c0c4250e4">
<source><x id="0" equiv-text="${ago}"/> day(s) ago</source>
</trans-unit>
<trans-unit id="s11bc220e8fa9d797"> <trans-unit id="s11bc220e8fa9d797">
<source>Authorizations</source> <source>Authorizations</source>
<target>Yetkilendirmeler</target> <target>Yetkilendirmeler</target>
@ -5743,9 +5737,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s1455753daa00f1bc"> <trans-unit id="s1455753daa00f1bc">
<source>User doesn't have view permission so description cannot be retrieved.</source> <source>User doesn't have view permission so description cannot be retrieved.</source>
</trans-unit> </trans-unit>
<trans-unit id="sa3a3e09b88ed9791">
<source>Assigned permissions</source>
</trans-unit>
<trans-unit id="s9cc631505c17b028"> <trans-unit id="s9cc631505c17b028">
<source>Assigned global permissions</source> <source>Assigned global permissions</source>
</trans-unit> </trans-unit>
@ -6281,6 +6272,30 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sb2275335377069aa"> <trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source> <source>This feature requires an enterprise license.</source>
</trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="saada50e84c7c646d">
<source>Permissions set on users which affect this object.</source>
</trans-unit>
<trans-unit id="s37d1b74df205b528">
<source>Permissions set on roles which affect this object.</source>
</trans-unit>
<trans-unit id="sc443bfff8a5dd106">
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
</trans-unit>
<trans-unit id="s3eba09a350b7d6c9">
<source>Permissions assigned to this user affecting specific object instances.</source>
</trans-unit>
<trans-unit id="s17032e57ba222d2f">
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -344,15 +344,9 @@
<trans-unit id="s341ab68d4130de20"> <trans-unit id="s341ab68d4130de20">
<source>No workers connected. Background tasks will not run.</source> <source>No workers connected. Background tasks will not run.</source>
</trans-unit> </trans-unit>
<trans-unit id="s2ed8eb02525a920a">
<source><x id="0" equiv-text="${ago}"/> hour(s) ago</source>
</trans-unit>
<trans-unit id="s98327528f00365a7"> <trans-unit id="s98327528f00365a7">
<source>Failed to fetch data.</source> <source>Failed to fetch data.</source>
</trans-unit> </trans-unit>
<trans-unit id="s1f1c857c0c4250e4">
<source><x id="0" equiv-text="${ago}"/> day(s) ago</source>
</trans-unit>
<trans-unit id="s11bc220e8fa9d797"> <trans-unit id="s11bc220e8fa9d797">
<source>Authorizations</source> <source>Authorizations</source>
</trans-unit> </trans-unit>
@ -3169,9 +3163,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s28b3de1561da72b3"> <trans-unit id="s28b3de1561da72b3">
<source>MFA Authenticators</source> <source>MFA Authenticators</source>
</trans-unit> </trans-unit>
<trans-unit id="sa3a3e09b88ed9791">
<source>Assigned permissions</source>
</trans-unit>
<trans-unit id="s9cc631505c17b028"> <trans-unit id="s9cc631505c17b028">
<source>Assigned global permissions</source> <source>Assigned global permissions</source>
</trans-unit> </trans-unit>
@ -5191,6 +5182,30 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sb2275335377069aa"> <trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source> <source>This feature requires an enterprise license.</source>
</trans-unit> </trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="saada50e84c7c646d">
<source>Permissions set on users which affect this object.</source>
</trans-unit>
<trans-unit id="s37d1b74df205b528">
<source>Permissions set on roles which affect this object.</source>
</trans-unit>
<trans-unit id="sc443bfff8a5dd106">
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
</trans-unit>
<trans-unit id="s3eba09a350b7d6c9">
<source>Permissions assigned to this user affecting specific object instances.</source>
</trans-unit>
<trans-unit id="s17032e57ba222d2f">
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@ -537,18 +537,6 @@
<source>No workers connected. Background tasks will not run.</source> <source>No workers connected. Background tasks will not run.</source>
<target>没有 Workers 连接,后台任务将无法运行。</target> <target>没有 Workers 连接,后台任务将无法运行。</target>
</trans-unit>
<trans-unit id="s2ed8eb02525a920a">
<source><x id="0" equiv-text="${ago}"/> hour(s) ago</source>
<target>
<x id="0" equiv-text="${ago}"/>小时前</target>
</trans-unit>
<trans-unit id="s1f1c857c0c4250e4">
<source><x id="0" equiv-text="${ago}"/> day(s) ago</source>
<target>
<x id="0" equiv-text="${ago}"/>天前</target>
</trans-unit> </trans-unit>
<trans-unit id="s11bc220e8fa9d797"> <trans-unit id="s11bc220e8fa9d797">
<source>Authorizations</source> <source>Authorizations</source>
@ -7668,10 +7656,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>User doesn't have view permission so description cannot be retrieved.</source> <source>User doesn't have view permission so description cannot be retrieved.</source>
<target>用户不具有查看权限,所以无法获取描述。</target> <target>用户不具有查看权限,所以无法获取描述。</target>
</trans-unit> </trans-unit>
<trans-unit id="sa3a3e09b88ed9791">
<source>Assigned permissions</source>
<target>分配的权限</target>
</trans-unit>
<trans-unit id="s9cc631505c17b028"> <trans-unit id="s9cc631505c17b028">
<source>Assigned global permissions</source> <source>Assigned global permissions</source>
<target>分配的全局权限</target> <target>分配的全局权限</target>
@ -8388,6 +8372,42 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sb2275335377069aa"> <trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source> <source>This feature requires an enterprise license.</source>
</trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source>
</trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="saada50e84c7c646d">
<source>Permissions set on users which affect this object.</source>
</trans-unit>
<trans-unit id="s37d1b74df205b528">
<source>Permissions set on roles which affect this object.</source>
</trans-unit>
<trans-unit id="sc443bfff8a5dd106">
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
</trans-unit>
<trans-unit id="s3eba09a350b7d6c9">
<source>Permissions assigned to this user affecting specific object instances.</source>
</trans-unit>
<trans-unit id="s17032e57ba222d2f">
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -427,12 +427,6 @@
<source>No workers connected. Background tasks will not run.</source> <source>No workers connected. Background tasks will not run.</source>
<target>没有 workers 连接。后台任务将无法运行。</target> <target>没有 workers 连接。后台任务将无法运行。</target>
</trans-unit> </trans-unit>
<trans-unit id="s2ed8eb02525a920a">
<source><x id="0" equiv-text="${ago}"/> hour(s) ago</source>
</trans-unit>
<trans-unit id="s1f1c857c0c4250e4">
<source><x id="0" equiv-text="${ago}"/> day(s) ago</source>
</trans-unit>
<trans-unit id="s11bc220e8fa9d797"> <trans-unit id="s11bc220e8fa9d797">
<source>Authorizations</source> <source>Authorizations</source>
<target>授权</target> <target>授权</target>
@ -5791,9 +5785,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s1455753daa00f1bc"> <trans-unit id="s1455753daa00f1bc">
<source>User doesn't have view permission so description cannot be retrieved.</source> <source>User doesn't have view permission so description cannot be retrieved.</source>
</trans-unit> </trans-unit>
<trans-unit id="sa3a3e09b88ed9791">
<source>Assigned permissions</source>
</trans-unit>
<trans-unit id="s9cc631505c17b028"> <trans-unit id="s9cc631505c17b028">
<source>Assigned global permissions</source> <source>Assigned global permissions</source>
</trans-unit> </trans-unit>
@ -6329,6 +6320,30 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sb2275335377069aa"> <trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source> <source>This feature requires an enterprise license.</source>
</trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="saada50e84c7c646d">
<source>Permissions set on users which affect this object.</source>
</trans-unit>
<trans-unit id="s37d1b74df205b528">
<source>Permissions set on roles which affect this object.</source>
</trans-unit>
<trans-unit id="sc443bfff8a5dd106">
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
</trans-unit>
<trans-unit id="s3eba09a350b7d6c9">
<source>Permissions assigned to this user affecting specific object instances.</source>
</trans-unit>
<trans-unit id="s17032e57ba222d2f">
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -530,16 +530,6 @@
<source>No workers connected. Background tasks will not run.</source> <source>No workers connected. Background tasks will not run.</source>
<target>沒有已連線的 workers無法執行背景工作。</target> <target>沒有已連線的 workers無法執行背景工作。</target>
</trans-unit>
<trans-unit id="s2ed8eb02525a920a">
<source><x id="0" equiv-text="${ago}"/> hour(s) ago</source>
<target><x id="0" equiv-text="${ago}"/> 小時以前</target>
</trans-unit>
<trans-unit id="s1f1c857c0c4250e4">
<source><x id="0" equiv-text="${ago}"/> day(s) ago</source>
<target><x id="0" equiv-text="${ago}"/> 天以前</target>
</trans-unit> </trans-unit>
<trans-unit id="s11bc220e8fa9d797"> <trans-unit id="s11bc220e8fa9d797">
<source>Authorizations</source> <source>Authorizations</source>
@ -7602,10 +7592,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>User doesn't have view permission so description cannot be retrieved.</source> <source>User doesn't have view permission so description cannot be retrieved.</source>
<target>使用者沒有讀取權限,所以無法取得描述。</target> <target>使用者沒有讀取權限,所以無法取得描述。</target>
</trans-unit> </trans-unit>
<trans-unit id="sa3a3e09b88ed9791">
<source>Assigned permissions</source>
<target>已分配的權限</target>
</trans-unit>
<trans-unit id="s9cc631505c17b028"> <trans-unit id="s9cc631505c17b028">
<source>Assigned global permissions</source> <source>Assigned global permissions</source>
<target>已分配的全域權限</target> <target>已分配的全域權限</target>
@ -8204,6 +8190,30 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sb2275335377069aa"> <trans-unit id="sb2275335377069aa">
<source>This feature requires an enterprise license.</source> <source>This feature requires an enterprise license.</source>
</trans-unit>
<trans-unit id="s6d3f81dc4bcacbda">
<source>Last used</source>
</trans-unit>
<trans-unit id="s96b2703420bfbd0c">
<source>OAuth Access Tokens</source>
</trans-unit>
<trans-unit id="sd99e668c54f78f6e">
<source>Credentials / Tokens</source>
</trans-unit>
<trans-unit id="saada50e84c7c646d">
<source>Permissions set on users which affect this object.</source>
</trans-unit>
<trans-unit id="s37d1b74df205b528">
<source>Permissions set on roles which affect this object.</source>
</trans-unit>
<trans-unit id="sc443bfff8a5dd106">
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
</trans-unit>
<trans-unit id="s3eba09a350b7d6c9">
<source>Permissions assigned to this user affecting specific object instances.</source>
</trans-unit>
<trans-unit id="s17032e57ba222d2f">
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -9,22 +9,20 @@ Refer to the following topics for instructions to view and manage permissions.
You can view all permissions that are assigned to a user, group, role, flow, or stage. You can view all permissions that are assigned to a user, group, role, flow, or stage.
### View user, group, and role permissions ### View user and role permissions
To view _object_ permissions for a specific user, role, or group: To view _object_ permissions for a specific user or role:
1. Go to the Admin interface and navigate to **Directory**. 1. Go to the Admin interface and navigate to **Directory**.
2. Select either **Users**, **Groups**, or **Roles** 2. Select either **Users** or **Roles**
3. Select a specific user/group/role by clicking on the name (this opens the details page). 3. Select a specific user/role by clicking on the name (this opens the details page).
4. Click the **Assigned Permissions** tab at the top of the page (to the right of the **Permissions** tab). 4. Click the **Permissions** tab at the top of the page
5. Scroll down to see both the global and object-level permissions. 5. Select the **Assigned global permissions** sub-tab to see global permissions and the **Assigned object permissions** sub-tab to see the object-level permissions.
:::info
Note that groups do not have global permissions.
:::
### View flow permissions ### View flow permissions
\_These instructions apply to all objects that have a detail page, which can be accessed by clicking on the name in the list page.\_\_
1. Go to the Admin interface and navigate to **Flows and Stages -> Flows**. 1. Go to the Admin interface and navigate to **Flows and Stages -> Flows**.
2. Click the name of the flow (this opens the details page). 2. Click the name of the flow (this opens the details page).
3. Click the **Permissions** tab at the top of the page. 3. Click the **Permissions** tab at the top of the page.
@ -32,6 +30,8 @@ Note that groups do not have global permissions.
### View stage permissions ### View stage permissions
\_These instructions apply to all objects that **do not** have a detail page.\_\_
1. Go to the Admin interface and navigate to **Flows and Stages -> Stagess**. 1. Go to the Admin interface and navigate to **Flows and Stages -> Stagess**.
2. On the row for the specific stage whose permissions you want to view, click the lock icon. 2. On the row for the specific stage whose permissions you want to view, click the lock icon.
3. On the **Update Permissions** tab, you can view the assigned permissions using the **User Object Permissions** and the **Role Object Permissions** tabs. 3. On the **Update Permissions** tab, you can view the assigned permissions using the **User Object Permissions** and the **Role Object Permissions** tabs.