Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
68d66d7
reuse system-access in backend-proxy-middleware
tobiasqueck Jun 14, 2024
6a689c8
dont change the behavior of of the proxy for steampunk
tobiasqueck Jun 14, 2024
b178136
keep token in header for subsequent requests
tobiasqueck Jun 14, 2024
56ce5d3
copy auth token from provider
tobiasqueck Jun 14, 2024
5c3c89b
test cleanup
tobiasqueck Jun 14, 2024
d7065d6
Merge branch 'main' into feat/2029/use-system-access
tobiasqueck Jun 28, 2024
439d8b9
Merge branch 'main' into feat/2029/use-system-access
tobiasqueck Aug 2, 2024
0f8215b
Merge branch 'main' into feat/2029/use-system-access
heimwege Jul 18, 2025
7ea78cf
clean up imports
heimwege Jul 18, 2025
fc22f97
Merge remote-tracking branch 'origin/feat/2029/use-system-access' int…
heimwege Jul 18, 2025
7e15783
fix eslint error
heimwege Jul 18, 2025
82ddd1e
add unit test
heimwege Jul 21, 2025
afa6392
add unit test
heimwege Jul 21, 2025
a1f2373
adjust unit test
heimwege Jul 21, 2025
3d11561
Merge branch 'main' into feat/2029/use-system-access
heimwege Jul 21, 2025
6a1b510
add cset
heimwege Jul 21, 2025
ed16178
merge main and solve conflicts
heimwege Jul 30, 2025
2db9f0b
Merge branch 'main' into feat/2029/use-system-access
heimwege Aug 29, 2025
c52ffeb
Merge branch 'main' into feat/2029/use-system-access
heimwege Sep 3, 2025
e820ca6
Merge branch 'main' of github.com:SAP/open-ux-tools into feat/2029/us…
heimwege Sep 30, 2025
94199c2
fix imports
heimwege Sep 30, 2025
24c48c0
Merge branch 'main' into feat/2029/use-system-access
heimwege Oct 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/wise-bees-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@sap-ux/backend-proxy-middleware': patch
'@sap-ux/axios-extension': patch
'@sap-ux/system-access': patch
---

feat: replace backend-proxy-middleware code that accesses a system with system-access
5 changes: 4 additions & 1 deletion packages/axios-extension/src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,12 @@ export function attachUaaAuthInterceptor(

provider.interceptors.request.use(async (request: InternalAxiosRequestConfig) => {
token = token ?? (await getToken());
// add token as auth header
// add token as auth header for this request
request.headers = request.headers ?? new AxiosHeaders();
request.headers.authorization = `bearer ${token}`;
// add token as auth header for all subsequent requests
provider.defaults.headers.common = provider.defaults.headers.common ?? {};
provider.defaults.headers.common.Authorization = `bearer ${token}`;

return request;
});
Expand Down
3 changes: 2 additions & 1 deletion packages/backend-proxy-middleware/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"!dist/**/*.map"
],
"dependencies": {
"@sap-ux/axios-extension": "workspace:*",
"@sap-ux/system-access": "workspace:*",
"@sap-ux/btp-utils": "workspace:*",
"@sap-ux/logger": "workspace:*",
"@sap-ux/store": "workspace:*",
Expand All @@ -43,6 +43,7 @@
"proxy-from-env": "1.1.0"
},
"devDependencies": {
"@sap-ux/axios-extension": "workspace:*",
"@types/express": "4.17.21",
"@types/http-proxy": "^1.17.5",
"@types/prompts": "2.4.4",
Expand Down
95 changes: 19 additions & 76 deletions packages/backend-proxy-middleware/src/base/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { createProxyMiddleware } from 'http-proxy-middleware';
import i18n from 'i18next';
import type { ClientRequest, IncomingMessage, ServerResponse } from 'http';
import { ToolsLogger, type Logger, UI5ToolingTransport } from '@sap-ux/logger';
import { AbapCloudEnvironment, createForAbapOnCloud } from '@sap-ux/axios-extension';
import { createAbapServiceProvider } from '@sap-ux/system-access';
import {
isAppStudio,
getDestinationUrlForAppStudio,
Expand All @@ -14,12 +14,10 @@ import {
isFullUrlDestination,
BAS_DEST_INSTANCE_CRED_HEADER
} from '@sap-ux/btp-utils';
import type { ServiceInfo } from '@sap-ux/btp-utils';
import type { BackendConfig, DestinationBackendConfig, LocalBackendConfig } from './types';
import type { BackendConfig, DestinationBackendConfig } from './types';
import translations from './i18n.json';

import type { ApiHubSettings, ApiHubSettingsKey, ApiHubSettingsService, BackendSystem } from '@sap-ux/store';
import { AuthenticationType, BackendSystemKey, getService } from '@sap-ux/store';
import type { ApiHubSettings, ApiHubSettingsKey, ApiHubSettingsService } from '@sap-ux/store';
import { getService } from '@sap-ux/store';
import { updateProxyEnv } from './config';
import type { Url } from 'url';
import { addOptionsForEmbeddedBSP } from '../ext/bsp';
Expand Down Expand Up @@ -246,45 +244,26 @@ export async function enhanceConfigsForDestination(
}

/**
* Enhance the proxy options with information read from the store.
* Enhance the proxy options by attempting to connect based on stored information and environment variables.
*
* @param proxyOptions reference to a proxy options object that the function will enhance
* @param system backend system information (most likely) read from the store
* @param oAuthRequired if true then the OAuth flow is triggered to get cookies
* @param tokenChangedCallback function to call if a new refreshToken is available
* @param backend backend system specific configuration
* @param logger optional logger instance
*/
export async function enhanceConfigForSystem(
proxyOptions: Options & { headers: object },
system: BackendSystem | undefined,
oAuthRequired: boolean | undefined,
tokenChangedCallback: (refreshToken?: string) => void
backend: BackendConfig,
logger: ToolsLogger
): Promise<void> {
if (oAuthRequired) {
if (system?.serviceKeys) {
const provider = createForAbapOnCloud({
environment: AbapCloudEnvironment.Standalone,
service: system.serviceKeys as ServiceInfo,
refreshToken: system.refreshToken,
refreshTokenChangedCb: tokenChangedCallback
});
// sending a request to the backend to get token
await provider.getAtoInfo();
} else {
throw new Error('Cannot connect to ABAP Environment on BTP without service keys.');
}
} else if (system?.authenticationType === AuthenticationType.ReentranceTicket) {
const provider = createForAbapOnCloud({
ignoreCertErrors: proxyOptions.secure === false,
environment: AbapCloudEnvironment.EmbeddedSteampunk,
url: system.url
});
// sending a request to the backend to get cookies
const ato = await provider.getAtoInfo();
if (ato) {
proxyOptions.headers['cookie'] = provider.cookies.toString();
}
} else if (system?.username && system.password) {
proxyOptions.auth = `${system.username}:${system.password}`;
// create a ABAP service provider with the given configuration
const provider = await createAbapServiceProvider(backend, undefined, false, logger);
// send a request to the backend to get cookies and updated the auth header
const ato = await provider.getAtoInfo();
if (ato) {
proxyOptions.headers['cookie'] = provider.cookies.toString();
}
if (typeof provider.defaults.headers.common.Authorization === 'string') {
proxyOptions.headers.Authorization = provider.defaults.headers.common.Authorization;
}
}

Expand Down Expand Up @@ -332,43 +311,7 @@ export async function generateProxyMiddlewareOptions(
logger.info('Using destination: ' + destBackend.destination);
}
} else {
const localBackend = backend as LocalBackendConfig;
// check if system credentials are stored in the store
try {
const systemStore = await getService<BackendSystem, BackendSystemKey>({ logger, entityName: 'system' });
const system = (await systemStore.read(
new BackendSystemKey({ url: localBackend.url, client: localBackend.client })
)) ?? {
name: '<unknown>',
url: localBackend.url,
authenticationType: localBackend.authenticationType
};
await enhanceConfigForSystem(
proxyOptions,
system,
backend.scp,
(refreshToken?: string, accessToken?: string) => {
if (refreshToken) {
logger.info('Updating refresh token for: ' + localBackend.url);
systemStore.write({ ...system, refreshToken }).catch((error) => logger.error(error));
}

if (accessToken) {
logger.info('Setting access token');
proxyOptions.headers['authorization'] = `bearer ${accessToken}`;
} else {
logger.warn('Setting of access token failed.');
}
}
);
} catch (error) {
logger.warn('Accessing the credentials store failed.');
logger.debug(error as object);
}
}

if (!proxyOptions.auth && process.env.FIORI_TOOLS_USER && process.env.FIORI_TOOLS_PASSWORD) {
proxyOptions.auth = `${process.env.FIORI_TOOLS_USER}:${process.env.FIORI_TOOLS_PASSWORD}`;
await enhanceConfigForSystem(proxyOptions, backend, logger);
}

if (backend.bsp) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const btpUtils = jest.requireActual('@sap-ux/btp-utils');

module.exports = {
...btpUtils,
isAppStudio: jest.fn().mockReturnValue(false),
isAbapSystem: jest.fn().mockReturnValue(true),
getCredentialsForDestinationService: jest.fn(),
listDestinations: jest.fn()
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const store = jest.requireActual('@sap-ux/store');

const mockedService = {
read: jest.fn().mockReturnValue(undefined),
write: jest.fn()
};

module.exports = {
...store,
mockedService,
getService: jest.fn().mockResolvedValue(mockedService)
};
20 changes: 20 additions & 0 deletions packages/backend-proxy-middleware/test/__mocks__/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import mockedStore from '@sap-ux/store';
import { getCredentialsForDestinationService, isAppStudio, listDestinations } from '@sap-ux/btp-utils';
import fs from 'fs';

export const mockReadFileSync = jest.spyOn(fs, 'readFileSync');

type MockedStore = {
mockedService: {
read: jest.Mock;
write: jest.Mock;
};
};

export const mockedStoreService = (mockedStore as unknown as MockedStore).mockedService;

export const mockIsAppStudio = isAppStudio as jest.Mock;

export const mockListDestinations = listDestinations as jest.Mock;

export const mockGetCredentialsForDestinationService = getCredentialsForDestinationService as jest.Mock;
Loading
Loading