();
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/Page/PageContent.module.css:
--------------------------------------------------------------------------------
1 | .pageContent {
2 | --component-page_content-vertical-padding: 2rem;
3 | background-color: white;
4 | padding-top: var(--component-page_content-vertical-padding);
5 | padding-bottom: var(--component-page_content-vertical-padding);
6 | box-sizing: inherit;
7 | }
8 |
9 | @media only screen and (max-width: 768px) {
10 | .pageContent {
11 | --component-page-margin-horizontal: 0.5rem;
12 | padding-right: var(--component-page-margin-horizontal);
13 | padding-left: var(--component-page-margin-horizontal);
14 | }
15 | }
16 |
17 | @media only screen and (min-width: 769px) {
18 | .pageContent {
19 | --component-page-margin-horizontal: 3rem;
20 | padding-right: var(--component-page-margin-horizontal);
21 | padding-left: var(--component-page-margin-horizontal);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/features/amUI/consent/ActiveConsentsPage/ActiveConsentsPage.module.css:
--------------------------------------------------------------------------------
1 | .consentsHeader {
2 | margin-top: 15px;
3 | padding-bottom: 1rem;
4 | }
5 |
6 | .activeConsentsSubHeading {
7 | display: flex;
8 | align-items: center;
9 | justify-content: space-between;
10 | margin-bottom: var(--ds-size-4);
11 | margin-top: var(--ds-size-12);
12 | }
13 |
14 | .consentLogLink {
15 | font-size: var(--ds-font-size-4);
16 | display: flex;
17 | gap: var(--ds-size-1);
18 | align-items: flex-end;
19 | }
20 |
21 | .expandedListItem {
22 | padding: var(--ds-size-8) var(--ds-size-14);
23 | }
24 | .consentDialog {
25 | max-width: 50rem;
26 | }
27 |
28 | @media only screen and (max-width: 769px) {
29 | .expandedListItem {
30 | padding: var(--ds-size-2);
31 | }
32 |
33 | .consentBadge {
34 | display: none;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/features/apiDelegation/components/OverviewPageContent/OrgDelegationActionBar/OrgDelegationActionBar.module.css:
--------------------------------------------------------------------------------
1 | @media only screen and (max-width: 768px) {
2 | .actionBarContent {
3 | padding-left: 0.7rem;
4 | }
5 |
6 | .orgDelegationActionBarTitle {
7 | font-size: 14px;
8 | }
9 | }
10 |
11 | @media only screen and (min-width: 769px) {
12 | .actionBarContent {
13 | padding-left: 50px;
14 | }
15 | }
16 |
17 | .actionBarContent {
18 | padding: 10px 10px 10px 20px;
19 | }
20 |
21 | .actionBarText__softDelete {
22 | text-decoration: line-through;
23 | opacity: 70%;
24 | }
25 |
26 | .actionBarSubtitle__softDelete {
27 | text-decoration: line-through;
28 | opacity: 70%;
29 | }
30 |
31 | .accordionHeaderRightText {
32 | margin-right: 15px;
33 | }
34 |
35 | .additionalText {
36 | margin-right: 10px;
37 | }
38 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Tests/Data/ExpectedResults/SystemUser/regnskapsforerCustomers.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "b4c3ba12-7e87-4ae1-84f8-426b1decfacf",
4 | "name": "VIKESÅ OG ÅLVUNDFJORD",
5 | "orgNo": "910510789"
6 | },
7 | {
8 | "id": "60fb3d5b-99c2-4df0-aa77-f3fca3bc5199",
9 | "name": "RAKRYGGET UNG TIGER AS",
10 | "orgNo": "313523497"
11 | },
12 | {
13 | "id": "3d8b34c3-df0d-4dcc-be12-e788ce414744",
14 | "name": "Digitaliseringsdirektoratet",
15 | "orgNo": "991825827"
16 | },
17 | {
18 | "id": "82cc64c5-60ff-4184-8c07-964c3a1e6fc7",
19 | "name": "Kan ikke legges til",
20 | "orgNo": "881825827"
21 | },
22 | {
23 | "id": "56d8ef08-d1c3-4dac-8b62-461c18ce612b",
24 | "name": "Kan ikke fjernes",
25 | "orgNo": "313131317"
26 | }
27 | ]
28 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Core/Models/Delegation/IdValuePair.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | namespace Altinn.AccessManagement.UI.Core.Models
4 | {
5 | ///
6 | /// This model describes a pair of AttributeId and AttributeValue for use in matching in XACML policies, for instance a resource, a user, a party or an action.
7 | ///
8 | public class IdValuePair
9 | {
10 | ///
11 | /// Gets or sets the attribute id for the match
12 | ///
13 | [Required]
14 | public string Id { get; set; }
15 |
16 | ///
17 | /// Gets or sets the attribute value for the match
18 | ///
19 | [Required]
20 | public string Value { get; set; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
14 |
15 |
16 |
17 | Altinn
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/features/amUI/common/NotAvailableForUserTypeAlert/NotAvailableForUserTypeAlert.tsx:
--------------------------------------------------------------------------------
1 | import { DsHeading, DsLink, DsParagraph } from '@altinn/altinn-components';
2 | import { useTranslation } from 'react-i18next';
3 | import classes from './NotAvailableForUserTypeAlert.module.css';
4 | import { getHostUrl } from '@/resources/utils/pathUtils';
5 |
6 | export const NotAvailableForUserTypeAlert = () => {
7 | const { t } = useTranslation();
8 | return (
9 |
10 |
14 | {t('page_not_available.title')}
15 |
16 | {t('page_not_available.text')}
17 | {t('page_not_available.link_text')}
18 |
19 | );
20 | };
21 |
--------------------------------------------------------------------------------
/src/features/singleRight/delegate/ChooseRightsPage/ChooseRightsPage.module.css:
--------------------------------------------------------------------------------
1 | .popoverButtonContainer {
2 | margin-top: 20px;
3 | display: flex;
4 | justify-content: center;
5 | }
6 |
7 | .serviceResources {
8 | margin-top: var(--ds-spacing-2);
9 | display: flex;
10 | flex-direction: column;
11 | gap: var(--ds-spacing-2);
12 | }
13 |
14 | .navigationContainer {
15 | margin-top: 40px;
16 | display: flex;
17 | flex-direction: row;
18 | justify-content: flex-start;
19 | gap: 20px;
20 | }
21 |
22 | .secondaryText {
23 | margin-top: 20px;
24 | margin-bottom: 40px;
25 | }
26 |
27 | @media screen and (max-width: 768px) {
28 | .navigationContainer {
29 | display: flex;
30 | flex-direction: column;
31 | gap: 20px;
32 | }
33 | .navigationContainer > button {
34 | flex-grow: 1;
35 | width: 100%;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Core/Models/Connections/AddConnectionResponse.cs:
--------------------------------------------------------------------------------
1 | namespace Altinn.AccessManagement.UI.Core.Models.Connections
2 | {
3 | ///
4 | /// The response model for adding a connection
5 | ///
6 | public class AddConnectionResponse
7 | {
8 | ///
9 | /// Users uuid.
10 | ///
11 | public Guid Id { get; set; }
12 |
13 | ///
14 | /// New role id.
15 | ///
16 | public Guid RoleId { get; set; }
17 |
18 | ///
19 | /// From id.
20 | ///
21 | public Guid FromId { get; set; }
22 |
23 | ///
24 | /// To id.
25 | ///
26 | public Guid ToId { get; set; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/resources/hooks/useProviderLogoUrl.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { useGetOrgDataQuery } from '@/rtk/features/altinnCdnApi';
3 |
4 | /**
5 | * Hook that returns a resolver for provider/org logo URLs based on org code.
6 | * Falls back to emblem, then logo. Returns undefined while loading or if not found.
7 | */
8 | export const useProviderLogoUrl = () => {
9 | const { data: orgData, isLoading } = useGetOrgDataQuery();
10 |
11 | const getProviderLogoUrl = React.useCallback(
12 | (orgCode: string | null | undefined): string | undefined => {
13 | if (!orgData || isLoading) return undefined;
14 | const org = orgCode ? orgData[orgCode] : undefined;
15 | return org?.emblem ?? org?.logo ?? undefined;
16 | },
17 | [orgData, isLoading],
18 | );
19 |
20 | return { getProviderLogoUrl, isLoading } as const;
21 | };
22 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Core/Models/Consent/Frontend/ConsentRightFE.cs:
--------------------------------------------------------------------------------
1 | namespace Altinn.AccessManagement.UI.Core.Models.Consent.Frontend
2 | {
3 | ///
4 | /// Consent rights where metadata values are replaced
5 | ///
6 | public class ConsentRightFE
7 | {
8 | ///
9 | /// Resource identifier
10 | ///
11 | public required string Identifier { get; set; }
12 |
13 | ///
14 | /// Resource title
15 | ///
16 | public required Dictionary Title { get; set; }
17 |
18 | ///
19 | /// Resource consentText with metadata values replaced
20 | ///
21 | public required Dictionary ConsentTextHtml { get; set; }
22 | }
23 | }
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Core/Models/Profile/ProfileGroup.cs:
--------------------------------------------------------------------------------
1 | namespace Altinn.AccessManagement.UI.Core.Models.Profile
2 | {
3 | ///
4 | /// A group of parties set in the users profile. E.g. their favorites from their actors list
5 | ///
6 | public class ProfileGroup
7 | {
8 | ///
9 | /// The name of the group
10 | ///
11 | public string Name { get; set; }
12 |
13 | ///
14 | /// Whether or not this group represents the user's favorite parties
15 | ///
16 | public bool IsFavorite { get; set; }
17 |
18 | ///
19 | /// The partyUuids of the parties in this group
20 | ///
21 | public List Parties { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/BorderedList/BorderedList.tsx:
--------------------------------------------------------------------------------
1 | import cn from 'classnames';
2 | import * as React from 'react';
3 | import { DsListUnordered, type DsListUnorderedProps } from '@altinn/altinn-components';
4 |
5 | import classes from './BorderedList.module.css';
6 |
7 | export type BorderedListProps = {
8 | /**
9 | * Style of the border separating the items
10 | */
11 | borderStyle?: 'solid' | 'dashed';
12 | } & DsListUnorderedProps;
13 |
14 | export const BorderedList = ({
15 | borderStyle = 'dashed',
16 | children,
17 | className,
18 | ...rest
19 | }: BorderedListProps) => {
20 | return (
21 |
26 | {children}
27 |
28 | );
29 | };
30 |
--------------------------------------------------------------------------------
/src/components/ReloadAlert/ReloadAlert.tsx:
--------------------------------------------------------------------------------
1 | import { useTranslation } from 'react-i18next';
2 | import { DsDialog, DsParagraph, DsButton } from '@altinn/altinn-components';
3 |
4 | import { useCookieListener } from '@/resources/Cookie/CookieMethods';
5 |
6 | import classes from './ReloadAlert.module.css';
7 |
8 | export const ReloadAlert = () => {
9 | const { t } = useTranslation();
10 |
11 | const displayAlert = useCookieListener('AltinnPartyId');
12 |
13 | return (
14 | displayAlert && (
15 | window.location.reload()}
18 | closeButton={false}
19 | >
20 | {t('common.refresh_cookie_alert')}
21 | window.location.reload()}>Ok
22 |
23 | )
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/src/features/amUI/common/RightsTabs/TabContentSkeleton.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useTranslation } from 'react-i18next';
3 | import { DsHeading, DsSkeleton } from '@altinn/altinn-components';
4 |
5 | import { SkeletonAccessPackageList } from '../AccessPackageList/SkeletonAccessPackageList';
6 |
7 | export const TabContentSkeleton = () => {
8 | const { t } = useTranslation();
9 |
10 | return (
11 | <>
12 |
13 |
18 | {t('access_packages.current_access_packages_title', { count: 0 })}
19 |
20 |
21 |
25 |
26 | >
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/src/components/Filter/Filter.module.css:
--------------------------------------------------------------------------------
1 | .content {
2 | box-sizing: border-box;
3 | height: 100%;
4 | display: flex;
5 | flex-direction: column;
6 | padding: 16px 15px 4px 15px;
7 | }
8 |
9 | .content .modal {
10 | padding-top: 6px;
11 | }
12 |
13 | .modalHeader {
14 | display: flex;
15 | justify-content: space-between;
16 | align-items: center;
17 | }
18 |
19 | .optionSection {
20 | max-height: 300px;
21 | }
22 |
23 | .optionSection.modal {
24 | max-height: 100%;
25 | min-height: 0;
26 | flex: 1;
27 | }
28 |
29 | .filterActions {
30 | display: flex;
31 | justify-content: center;
32 | padding: 14px 0 14px 0;
33 | gap: 10px;
34 | }
35 |
36 | .resetButton {
37 | width: fit-content;
38 | }
39 |
40 | .loadingContainer {
41 | justify-content: center;
42 | align-items: center;
43 | display: flex;
44 | flex-direction: column;
45 | }
46 |
--------------------------------------------------------------------------------
/src/features/amUI/common/ResourceList/SkeletonResourceList.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { List, ResourceListItem, ResourceListItemProps } from '@altinn/altinn-components';
3 |
4 | export const SkeletonResourceList = ({ count = 3 }: { count?: number }) => {
5 | const resourceSkeletons: ResourceListItemProps[] = React.useMemo(
6 | () =>
7 | Array.from({ length: count }, (_, i) => ({
8 | id: String(i + 1),
9 | ownerName: 'xxxxxxxxx xxxxxxxxxxx',
10 | resourceName: 'xxxxxxxxxxxxxxxxxxxx',
11 | })),
12 | [count],
13 | );
14 |
15 | const items = resourceSkeletons.map((item) => (
16 |
24 | ));
25 |
26 | return {items}
;
27 | };
28 |
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 |
3 | import react from '@vitejs/plugin-react';
4 | import svgr from 'vite-plugin-svgr';
5 | import { defineConfig } from 'vitest/config';
6 |
7 | export default defineConfig({
8 | plugins: [react(), svgr()],
9 | resolve: {
10 | alias: [
11 | { find: '@', replacement: path.resolve(__dirname, 'src') },
12 | { find: '@mock', replacement: path.resolve(__dirname, '.mock') },
13 | ],
14 | },
15 | test: {
16 | globals: true,
17 | environment: 'jsdom',
18 | setupFiles: './tests/setupTests.ts',
19 | exclude: [
20 | '**/node_modules/**',
21 | '**/cypress/**',
22 | '**/playwright/**',
23 | '**/*.spec.ts',
24 | '**/*.test.cy.tsx',
25 | ],
26 | coverage: {
27 | provider: 'istanbul', // or 'v8'
28 | },
29 | pool: 'vmThreads',
30 | },
31 | });
32 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Mocks/Data/MaskinportenSchema/DelegationCheck/appid-136.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "rightKey": "appid-136:ScopeAccess",
4 | "resource": [
5 | {
6 | "id": "urn:altinn:resource",
7 | "value": "appid-136"
8 | }
9 | ],
10 | "action": "ScopeAccess",
11 | "status": "NotDelegable",
12 | "details": [
13 | {
14 | "code": "InsufficientAuthenticationLevel",
15 | "description": "Authenticated user does not meet the required security level for resource. Minimum authentication level is 4",
16 | "parameters": {
17 | "MinimumAuthenticationLevel": [
18 | {
19 | "Id": "urn:altinn:minimum-authenticationlevel",
20 | "Value": "4"
21 | }
22 | ]
23 | }
24 | }
25 | ]
26 | }
27 | ]
--------------------------------------------------------------------------------
/src/features/amUI/common/DelegationModal/RoleInfoModal.tsx:
--------------------------------------------------------------------------------
1 | import type { Role } from '@/rtk/features/roleApi';
2 | import type { ActionError } from '@/resources/hooks/useActionError';
3 |
4 | import { DelegationAction, EditModal } from './EditModal';
5 |
6 | interface RoleInfoModalProps {
7 | modalRef: React.RefObject;
8 | role?: Role;
9 | onClose?: () => void;
10 | availableActions?: DelegationAction[];
11 | openWithError?: ActionError | null;
12 | }
13 |
14 | export const RoleInfoModal = ({
15 | modalRef,
16 | role,
17 | onClose,
18 | availableActions,
19 | openWithError,
20 | }: RoleInfoModalProps) => {
21 | return (
22 |
29 | );
30 | };
31 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Core/Models/SystemUser/AgentDelegationRequest.cs:
--------------------------------------------------------------------------------
1 | namespace Altinn.AccessManagement.UI.Core.Models.SystemUser
2 | {
3 | ///
4 | /// Payload when adding a party to agent system user
5 | ///
6 | public class AgentDelegationRequest
7 | {
8 | ///
9 | /// The party uuid to add
10 | ///
11 | public Guid CustomerId { get; set; }
12 |
13 | ///
14 | /// PartyUuid of party which owns the agent system user
15 | ///
16 | public Guid FacilitatorId { get; set; }
17 |
18 | ///
19 | /// Gets or sets a collection of all access information for the client
20 | ///
21 | public List Access { get; set; } = [];
22 | }
23 | }
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Mocks/Data/Roles/Details/55bd7d4d-08dd-46ee-ac8e-3a44d800d752.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "55bd7d4d-08dd-46ee-ac8e-3a44d800d752",
3 | "name": "Daglig leder",
4 | "code": "daglig-leder",
5 | "description": "Fysisk- eller juridisk person som har ansvaret for den daglige driften i en virksomhet",
6 | "isKeyRole": true,
7 | "urn": "urn:altinn:external-role:ccr:daglig-leder",
8 | "legacyRoleCode": "DAGL",
9 | "legacyUrn": "urn:altinn:rolecode:DAGL",
10 | "provider": {
11 | "id": "0195ea92-2080-758b-89db-7735c4f68320",
12 | "name": "Enhetsregisteret",
13 | "refId": null,
14 | "logoUrl": null,
15 | "code": "sys-ccr",
16 | "typeId": "0195efb8-7c80-7bb5-a35c-11d58ea36695",
17 | "type": {
18 | "id": "0195efb8-7c80-7bb5-a35c-11d58ea36695",
19 | "name": "Registerenhet"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Tests/Data/ExpectedResults/SystemRegister/allSystems.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "systemId": "310144827_bare_tilgangspakke",
4 | "systemVendorOrgNumber": "310144827",
5 | "systemVendorOrgName": "N/A",
6 | "name": "Accesspackage without resource"
7 | },
8 | {
9 | "systemId": "910493353_fiken_demo_product",
10 | "systemVendorOrgNumber": "910493353",
11 | "systemVendorOrgName": "LEPSØY OG TONSTAD",
12 | "name": "Fiken"
13 | },
14 | {
15 | "systemId": "310144827_invalid_resource",
16 | "systemVendorOrgNumber": "310144827",
17 | "systemVendorOrgName": "N/A",
18 | "name": "Invalid resource"
19 | },
20 |
21 | {
22 | "systemId": "310144827_smartcloud",
23 | "systemVendorOrgNumber": "310144827",
24 | "systemVendorOrgName": "N/A",
25 | "name": "SmartCloud"
26 | }
27 | ]
28 |
--------------------------------------------------------------------------------
/src/features/singleRight/components/NavigationSection/NavigatonSection.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react-vite';
2 |
3 | import { NavigationSection } from './NavigationSection';
4 |
5 | type NavigationSectionPropsAndCustomArgs = React.ComponentProps;
6 |
7 | const exampleArgs: NavigationSectionPropsAndCustomArgs = {
8 | nextButtonProps: {
9 | onNext: () => console.log('Next button clicked'),
10 | disabled: false,
11 | },
12 | cancelButtonProps: {
13 | onCancel: () => console.log('Cancel button clicked'),
14 | showWarning: true,
15 | },
16 | };
17 |
18 | export default {
19 | title: 'Features/SingleRight/NavigationSection',
20 | component: NavigationSection,
21 | argTypes: {},
22 | } as Meta;
23 |
24 | export const Default: StoryObj = {
25 | args: exampleArgs,
26 | };
27 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Core/Models/AccessManagement/UserAccesses.cs:
--------------------------------------------------------------------------------
1 | namespace Altinn.AccessManagement.UI.Core.Models.AccessManagement
2 | {
3 | ///
4 | /// An simplified overview of what a user has access to
5 | ///
6 | public class UserAccesses
7 | {
8 | ///
9 | /// List of IDs for access packages the right holder has access to
10 | ///
11 | public List AccessPackages { get; set; }
12 |
13 | ///
14 | /// List of IDs for services the right holder has access to
15 | ///
16 | public List Services { get; set; }
17 |
18 | ///
19 | /// List of roles the right holder has access to
20 | ///
21 | public List Roles { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Mocks/Data/MaskinportenSchema/DelegationCheck/appid-400.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "rightKey": "appid-136:ScopeAccess",
4 | "resource": [
5 | {
6 | "id": "urn:altinn:resource",
7 | "value": "appid-136"
8 | }
9 | ],
10 | "action": "ScopeAccess",
11 | "status": "NotDelegable",
12 | "details": [
13 | {
14 | "code": "MissingRoleAccess",
15 | "description": "The user does not have any required role(s) for the reportee party. (urn:altinn:rolecode:HADM, urn:altinn:location:garage), would give access to delegate the service.",
16 | "parameters": {
17 | "roleRequirementsMatches": [
18 | {
19 | "id": "urn:altinn:role",
20 | "value": "DAGL"
21 | }
22 | ]
23 | }
24 | }
25 | ]
26 | }
27 | ]
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI/Configuration/CustomTelemetryInitializer.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.ApplicationInsights.Channel;
2 | using Microsoft.ApplicationInsights.Extensibility;
3 |
4 | namespace Altinn.AccessManagement.Configuration
5 | {
6 | ///
7 | /// Set up custom telemetry for Application Insights
8 | ///
9 | public class CustomTelemetryInitializer : ITelemetryInitializer
10 | {
11 | ///
12 | /// Custom TelemetryInitializer that sets some specific values for the component
13 | ///
14 | public void Initialize(ITelemetry telemetry)
15 | {
16 | if (string.IsNullOrEmpty(telemetry.Context.Cloud.RoleName))
17 | {
18 | telemetry.Context.Cloud.RoleName = "access-management-ui";
19 | }
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/features/amUI/common/OldRolesAlert/OldRolesAlert.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react-vite';
2 | import { Provider } from 'react-redux';
3 |
4 | import store from '@/rtk/app/store';
5 |
6 | import { PartyRepresentationProvider } from '../PartyRepresentationContext/PartyRepresentationContext';
7 |
8 | import { OldRolesAlert } from './OldRolesAlert';
9 |
10 | export default {
11 | title: 'Features/AMUI/OldRolesAlert',
12 | component: OldRolesAlert,
13 | render: (args) => (
14 |
15 |
20 |
21 |
22 |
23 | ),
24 | } as Meta;
25 |
26 | export const Default: StoryObj = {
27 | args: {},
28 | };
29 |
--------------------------------------------------------------------------------
/src/features/amUI/settings/SettingsModal.module.css:
--------------------------------------------------------------------------------
1 | .modalHeader {
2 | display: flex;
3 | align-items: center;
4 | gap: 0.8rem;
5 | }
6 |
7 | .modalContent {
8 | display: flex;
9 | flex-direction: column;
10 | gap: 1rem;
11 | padding-top: 1.5rem;
12 | }
13 |
14 | .addressList {
15 | display: flex;
16 | flex-direction: column;
17 | gap: 0.5rem;
18 | }
19 |
20 | .emailFieldRow {
21 | display: grid;
22 | grid-template-columns: 1fr auto;
23 | align-items: center;
24 | }
25 |
26 | .emailFieldRow > *:not(:last-child) {
27 | margin-right: 0.5rem;
28 | }
29 |
30 | .phoneFieldRow {
31 | display: grid;
32 | grid-template-columns: 0.15fr 1fr auto;
33 | justify-content: space-between;
34 | }
35 |
36 | .phoneFieldRow > *:not(:last-child) {
37 | margin-right: 0.5rem;
38 | }
39 |
40 | .buttonRow {
41 | display: flex;
42 | gap: 0.5rem;
43 | margin-top: 1rem;
44 | }
45 |
--------------------------------------------------------------------------------
/playwright/api.ts:
--------------------------------------------------------------------------------
1 | import { env } from 'playwright/util/helper';
2 |
3 | export class Api {
4 | async getToken(): Promise {
5 | const environment = env('ENV_NAME');
6 | const tokenGeneratorUrl = env('TEST_TOKEN_GENERATOR_URL');
7 | const user = env('TEST_TOKEN_GENERATOR_USER');
8 | const pass = env('TEST_TOKEN_GENERATOR_PASS');
9 |
10 | const url = `${tokenGeneratorUrl}GetEnterpriseToken?env=${encodeURIComponent(environment)}&scopes=altinn:resourceregistry/resource.admin,altinn:register/partylookup.admin&org=digdir&orgNo=991825827&ttl=200000000`;
11 | // const auth = btoa(`${user}:${pass}`);
12 | const Authorization = 'Basic ' + btoa(`${user}:${pass}`);
13 |
14 | const response = await fetch(url, {
15 | headers: {
16 | Authorization,
17 | },
18 | });
19 |
20 | const json = await response.text();
21 | return json;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Core/Models/Role/RoleArea.cs:
--------------------------------------------------------------------------------
1 | namespace Altinn.AccessManagement.UI.Core.Models.Role
2 | {
3 | ///
4 | /// An area for grouping similar types of roles
5 | ///
6 | public class RoleArea
7 | {
8 | ///
9 | /// Identifier of the RoleArea
10 | ///
11 | public string Id { get; set; }
12 |
13 | ///
14 | /// Name
15 | ///
16 | public string Name { get; set; }
17 |
18 | ///
19 | /// Description
20 | ///
21 | public string Description { get; set; }
22 |
23 | ///
24 | /// An url that provides the Icon associated with this area and it's roles
25 | ///
26 | public string IconUrl { get; set; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Core/Models/SingleRight/Details.cs:
--------------------------------------------------------------------------------
1 | namespace Altinn.AccessManagement.UI.Core.Models.SingleRight
2 | {
3 | ///
4 | /// Represents details about why something failed.
5 | ///
6 | public class Details
7 | {
8 | ///
9 | /// The detail code of the response.
10 | ///
11 | public string Code { get; set; }
12 |
13 | ///
14 | /// Info about details for the response.
15 | ///
16 | public string Description { get; set; }
17 |
18 | ///
19 | /// Further details about the reason for the response.
20 | ///
21 | public Dictionary> Parameters { get; set; } = new Dictionary>();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Tests/Data/ExpectedResults/Role/Details/55bd7d4d-08dd-46ee-ac8e-3a44d800d752.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "55bd7d4d-08dd-46ee-ac8e-3a44d800d752",
3 | "name": "Daglig leder",
4 | "code": "daglig-leder",
5 | "description": "Fysisk- eller juridisk person som har ansvaret for den daglige driften i en virksomhet",
6 | "isKeyRole": true,
7 | "urn": "urn:altinn:external-role:ccr:daglig-leder",
8 | "legacyRoleCode": "DAGL",
9 | "legacyUrn": "urn:altinn:rolecode:DAGL",
10 | "provider": {
11 | "id": "0195ea92-2080-758b-89db-7735c4f68320",
12 | "name": "Enhetsregisteret",
13 | "refId": null,
14 | "logoUrl": null,
15 | "code": "sys-ccr",
16 | "typeId": "0195efb8-7c80-7bb5-a35c-11d58ea36695",
17 | "type": {
18 | "id": "0195efb8-7c80-7bb5-a35c-11d58ea36695",
19 | "name": "Registerenhet"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/features/amUI/common/StatusSection/StatusSection.module.css:
--------------------------------------------------------------------------------
1 | .statusSection {
2 | display: flex;
3 | flex-direction: column;
4 | gap: 0.2rem;
5 | }
6 |
7 | .inheritedInfoIcon {
8 | color: var(--ds-color-info-base-default);
9 | margin-right: 0.3rem;
10 | min-width: 1.5rem;
11 | }
12 |
13 | .infoLine {
14 | display: flex;
15 | gap: 5px;
16 | align-items: center;
17 | }
18 |
19 | .infoLine svg {
20 | min-width: 24px;
21 | }
22 |
23 | .delegationCheckInfoIcon {
24 | color: var(--ds-color-warning-base-default);
25 | margin-right: 0.3rem;
26 | }
27 |
28 | .dangerIcon {
29 | color: var(--ds-color-danger-base-default);
30 | margin-right: 0.3rem;
31 | }
32 |
33 | .warningIcon {
34 | color: var(--ds-color-warning-base-default);
35 | margin-right: 0.3rem;
36 | }
37 |
38 | .hasPackageInfoIcon {
39 | color: var(--ds-color-success-base-default);
40 | margin-right: 0.3rem;
41 | }
42 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Core/Models/ResourceRegistry/CompetentAuthority.cs:
--------------------------------------------------------------------------------
1 | namespace Altinn.AccessManagement.UI.Core.Models.ResourceRegistry
2 | {
3 | ///
4 | /// Model representation of Competent Authority part of the ServiceResource model
5 | ///
6 | public class CompetentAuthority
7 | {
8 | ///
9 | /// The organization number
10 | ///
11 | public string Organization { get; set; }
12 |
13 | ///
14 | /// The organization code
15 | ///
16 | public string Orgcode { get; set; }
17 |
18 | ///
19 | /// The organization name. If not set it will be retrived from register based on Organization number
20 | ///
21 | public Dictionary Name { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/backend/src/Altinn.AccessManagement.UI/Altinn.AccessManagement.UI.Tests/Data/ExpectedResults/MaskinportenSchema/DelegationCheck/scope-access-schema/appid-136.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "rightKey": "appid-136:ScopeAccess",
4 | "resource": [
5 | {
6 | "id": "urn:altinn:resource",
7 | "value": "appid-136"
8 | }
9 | ],
10 | "action": "ScopeAccess",
11 | "status": "NotDelegable",
12 | "details": [
13 | {
14 | "code": "InsufficientAuthenticationLevel",
15 | "description": "Authenticated user does not meet the required security level for resource. Minimum authentication level is 4",
16 | "parameters": {
17 | "MinimumAuthenticationLevel": [
18 | {
19 | "Id": "urn:altinn:minimum-authenticationlevel",
20 | "Value": "4"
21 | }
22 | ]
23 | }
24 | }
25 | ]
26 | }
27 | ]
--------------------------------------------------------------------------------
/src/rtk/features/apiDelegation/apiDelegationSlice.ts:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | import { type Organization } from '../lookupApi';
4 |
5 | export interface InitialState {
6 | chosenOrgs: Organization[];
7 | }
8 |
9 | const initialState: InitialState = {
10 | chosenOrgs: [],
11 | };
12 |
13 | const apiDelegationSlice = createSlice({
14 | name: 'apiDelegation',
15 | initialState,
16 | reducers: {
17 | addOrg: (state, action) => {
18 | state.chosenOrgs.push(action.payload);
19 | },
20 | removeOrg: (state, action) => {
21 | const { chosenOrgs } = state;
22 | state.chosenOrgs = chosenOrgs.filter((org) => org.orgNumber !== action.payload.orgNumber);
23 | },
24 | resetState: () => initialState,
25 | },
26 | });
27 |
28 | export default apiDelegationSlice.reducer;
29 | export const { addOrg, removeOrg, resetState } = apiDelegationSlice.actions;
30 |
--------------------------------------------------------------------------------
/src/components/UserInfoBar/UserInfoBar.module.css:
--------------------------------------------------------------------------------
1 | .userInfoBar {
2 | margin-left: auto;
3 | margin-right: auto;
4 | margin-top: 1rem;
5 | background-color: #1eadf7;
6 | display: flex;
7 | min-width: 280px;
8 | max-width: 1056px;
9 | }
10 |
11 | .userInfoContent {
12 | flex: 1;
13 | display: flex;
14 | justify-content: flex-end;
15 | margin-right: 13px;
16 | }
17 |
18 | .userInfoText {
19 | font-weight: 500;
20 | margin: 0;
21 | margin-right: 1rem;
22 | margin-left: 1rem;
23 | text-align: right;
24 | word-break: break-word;
25 | font-size: 12px;
26 | }
27 |
28 | .companyIconContainer {
29 | font-size: 28px;
30 | }
31 |
32 | .userInfoTextContainer {
33 | display: flex;
34 | flex-direction: column;
35 | justify-content: center;
36 | }
37 |
38 | @media only screen and (max-width: 576px) {
39 | .userInfoBar {
40 | margin-left: 8px;
41 | margin-right: 8px;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/features/amUI/common/List/ListItem.tsx:
--------------------------------------------------------------------------------
1 | import cn from 'classnames';
2 |
3 | import classes from './List.module.css';
4 |
5 | interface ListItemProps extends React.HtmlHTMLAttributes {
6 | children: React.ReactNode;
7 | onClick?: () => void;
8 | }
9 |
10 | // TODO: Make this into a complete ListItem component with avatars, title and subtitle as designed
11 | export const ListItem = ({ children, onClick, className, ...props }: ListItemProps) => {
12 | return (
13 |
17 | {onClick ? (
18 |
25 | ) : (
26 | {children}
27 | )}
28 |
29 | );
30 | };
31 |
--------------------------------------------------------------------------------