Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions cypress/e2e/limit-public-config.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
describe('Limit public config properties', () => {
it('Should not include cache.serverSide properties in the html source code',
() => {
cy.request('/').its('body').then((text: string) => {
expect(text).to.not.contain('"serverSide":');
});
},
);
it('Should not include cache.serverSide properties in the config.json',
() => {
cy.request('/assets/config.json').its('body').then((obj: any) => {
expect(obj.cache).to.not.haveOwnProperty('serverSide');
});
},
);
});
3 changes: 2 additions & 1 deletion server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { buildAppConfig } from './src/config/config.server';
import {
APP_CONFIG,
AppConfig,
toClientConfig,
} from './src/config/app-config.interface';
import { extendEnvironmentWithAppConfig } from './src/config/config.util';
import { logStartupMessage } from './startup-message';
Expand Down Expand Up @@ -265,7 +266,7 @@ function serverSideRender(req, res, next, sendToUser: boolean = true) {
},
{
provide: APP_CONFIG,
useValue: environment,
useValue: toClientConfig(environment as AppConfig),
},
],
})
Expand Down
30 changes: 30 additions & 0 deletions src/config/app-config.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,38 @@ const APP_CONFIG = new InjectionToken<AppConfig>('APP_CONFIG');

const APP_CONFIG_STATE = makeStateKey<AppConfig>('APP_CONFIG_STATE');

type DeepPartial<T> = T extends object ? { [k in keyof T]?: DeepPartial<T[k]>} : T;

/**
* Removes all server-side specific settings from the application configuration.
* This method is used to ensure the "assets/config.json" that provides runtime
* configuration to CSR (client side rendering) excludes these server-side keys.
*
* @param config the application configuration
*/
const toClientConfig = ({
rest: {
ssrBaseUrl: _ssrBaseUrl,
hasSsrBaseUrl: _hasSsrBaseUrl,
...rest
},
cache: {
serverSide: _serverSide,
...cache
},
ui: {
rateLimiter: _rateLimiter,
useProxies: _useProxies,
...ui
},
...config
}: AppConfig): DeepPartial<AppConfig> => ({
...config, rest, cache, ui,
});

export {
APP_CONFIG,
APP_CONFIG_STATE,
AppConfig,
toClientConfig,
};
10 changes: 7 additions & 3 deletions src/config/config.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import {
} from 'colors';
import { load } from 'js-yaml';

import { AppConfig } from './app-config.interface';
import {
AppConfig,
toClientConfig,
} from './app-config.interface';
import { Config } from './config.interface';
import { mergeConfig } from './config.util';
import { DefaultAppConfig } from './default-app-config';
Expand Down Expand Up @@ -121,7 +124,7 @@ const overrideWithConfig = (config: Config, pathToConfig: string) => {
try {
console.log(`Overriding app config with ${pathToConfig}`);
const externalConfig = readFileSync(pathToConfig, 'utf8');
mergeConfig(config, load(externalConfig));
mergeConfig(config, load(externalConfig) as AppConfig);
} catch (err) {
console.error(err);
}
Expand Down Expand Up @@ -247,7 +250,8 @@ export const buildAppConfig = (destConfigPath?: string): AppConfig => {
buildBaseUrl(appConfig.rest);

if (isNotEmpty(destConfigPath)) {
writeFileSync(destConfigPath, JSON.stringify(appConfig, null, 2));
const clientConfig = toClientConfig(appConfig);
writeFileSync(destConfigPath, JSON.stringify(clientConfig, null, 2));

console.log(`Angular ${bold('config.json')} file generated correctly at ${bold(destConfigPath)} \n`);
}
Expand Down
19 changes: 6 additions & 13 deletions src/modules/app/server-init.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ import {
APP_CONFIG,
APP_CONFIG_STATE,
AppConfig,
toClientConfig,
} from '@dspace/config/app-config.interface';
import { BuildConfig } from '@dspace/config/build-config.interface';
import { CorrelationIdService } from '@dspace/core/correlation-id/correlation-id.service';
import { LocaleService } from '@dspace/core/locale/locale.service';
import { HeadTagService } from '@dspace/core/metadata/head-tag.service';
import {
isEmpty,
isNotEmpty,
} from '@dspace/shared/utils/empty.util';
import { isEmpty } from '@dspace/shared/utils/empty.util';
import { Store } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core';
import { lastValueFrom } from 'rxjs';
Expand Down Expand Up @@ -117,14 +115,9 @@ export class ServerInitService extends InitService {
}

private saveAppConfigForCSR(): void {
if (isNotEmpty(environment.rest.ssrBaseUrl) && environment.rest.baseUrl !== environment.rest.ssrBaseUrl) {
// Avoid to transfer ssrBaseUrl in order to prevent security issues
const config: AppConfig = Object.assign({}, environment as AppConfig, {
rest: Object.assign({}, environment.rest, { ssrBaseUrl: '', hasSsrBaseUrl: true }),
});
this.transferState.set<AppConfig>(APP_CONFIG_STATE, config);
} else {
this.transferState.set<AppConfig>(APP_CONFIG_STATE, environment as AppConfig);
}
this.transferState.set<AppConfig>(
APP_CONFIG_STATE,
toClientConfig(environment as AppConfig) as AppConfig,
);
}
}
Loading