Skip to content

Commit

Permalink
[#171703313] SAML certs and Startup IDP metadata from ENV (#595)
Browse files Browse the repository at this point in the history
  • Loading branch information
BurnedMarshal authored Mar 10, 2020
1 parent 502cba5 commit 006d258
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 277 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ Those are all Environment variables needed by the application:
| SAML_CALLBACK_URL | The absolute URL of the assertion consumer service endpoint | string |
| SAML_ISSUER | The issuer id for this Service Provider | string |
| SAML_ATTRIBUTE_CONSUMING_SERVICE_INDEX | The index in the attribute consumer list | int |
| SAML_KEY | Private Key used by SAML protocol | string |
| SAML_CERT | Certificate used by SAML protocol | string |
| PRE_SHARED_KEY | The key shared with the API backend to authenticate the webhook notifications | string |
| ALLOW_NOTIFY_IP_SOURCE_RANGE | The range in CIDR form of allowed IPs for the webhook notifications | string |
| AZURE_NH_HUB_NAME | The hub name configured in the Azure Notification HUB | string |
Expand All @@ -153,6 +155,7 @@ Those are all Environment variables needed by the application:
| PAGOPA_API_URL_TEST | The url for the PagoPA api endpoints in test mode | string |
| PAGOPA_BASE_PATH | The root path for the PagoPA endpoints | string |
| SPID_AUTOLOGIN | The user used in the autologin feature, omit this to disable autologin | string |
| STARTUP_IDPS_METADATA | Stringified JSON containing idps metadata `Record<string, string>` | string |
| CIE_METADATA_URL | Url to download CIE metadata from | string |
| IDP_METADATA_URL | Url to download IDP metadata from | string |
| IDP_METADATA_REFRESH_INTERVAL_SECONDS | The number of seconds when the IDPs Metadata are refreshed | int |
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
},
"homepage": "https://github.com/pagopa/io-backend#readme",
"dependencies": {
"@pagopa/io-spid-commons": "^2.7.0",
"@pagopa/io-spid-commons": "^2.9.0",
"apicache": "^1.4.0",
"applicationinsights": "^1.4.2",
"azure-sb": "^0.10.6",
Expand Down
23 changes: 16 additions & 7 deletions src/__tests__/app.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as spid from "@pagopa/io-spid-commons";
import * as spid from "@pagopa/io-spid-commons/dist/utils/metadata";
import { Express } from "express";
import { isRight } from "fp-ts/lib/Either";
import { Task } from "fp-ts/lib/Task";
Expand Down Expand Up @@ -124,24 +124,33 @@ describe("Success app start", () => {
});

describe("Failure app start", () => {
let app: Express | undefined;
afterAll(() => {
jest.restoreAllMocks();
app?.emit("server:stop");
});

it("Close app if download IDP metadata fails on startup", async () => {
// Override return value of generateSpidStrategy with a rejected promise.
jest.spyOn(spid, "withSpid").mockImplementation(() => {
return TE.left(
new Task(async () => new Error("Error download metadata"))
);
});
const mockFetchIdpsMetadata = jest
.spyOn(spid, "fetchIdpsMetadata")
.mockImplementation(() => {
return TE.left(
new Task(async () => new Error("Error download metadata"))
);
});
const mockExit = jest
.spyOn(process, "exit")
.mockImplementation(() => true as never);
await appModule.newApp(
app = await appModule.newApp(
NodeEnvironmentEnum.PRODUCTION,
aValidCIDR,
aValidCIDR,
"",
"/api/v1",
"/pagopa/api/v1"
);
expect(mockFetchIdpsMetadata).toBeCalledTimes(3);
expect(mockExit).toBeCalledWith(1);
});
});
22 changes: 15 additions & 7 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ import {
} from "./utils/package";

import { withSpid } from "@pagopa/io-spid-commons";
import { toError } from "fp-ts/lib/Either";
import { tryCatch } from "fp-ts/lib/TaskEither";
import { getSpidStrategyOption } from "@pagopa/io-spid-commons/dist/utils/middleware";
import { isEmpty, StrMap } from "fp-ts/lib/StrMap";
import { Task } from "fp-ts/lib/Task";
import { VersionPerPlatform } from "../generated/public/VersionPerPlatform";
import UserDataProcessingController from "./controllers/userDataProcessingController";
import MessagesService from "./services/messagesService";
Expand Down Expand Up @@ -170,7 +171,7 @@ export function newApp(
// Setup routes
//

return tryCatch(async () => {
return new Task(async () => {
// Ceate the Token Service
const TOKEN_SERVICE = new TokenService();

Expand Down Expand Up @@ -221,7 +222,7 @@ export function newApp(
PROFILE_SERVICE
);
return { app, acsController };
}, toError)
})
.chain(_ =>
withSpid(
appConfig,
Expand All @@ -238,9 +239,16 @@ export function newApp(
_.app.on("server:stop", () => clearInterval(idpMetadataRefreshTimer));
return _.app;
})
.getOrElseL(err => {
log.error("Fatal error during Express initialization: %s", err);
process.exit(1);
.map(_ => {
const spidStrategyOption = getSpidStrategyOption(_);
// Process ends in case no IDP is configured
if (isEmpty(new StrMap(spidStrategyOption?.idp || {}))) {
log.error(
"Fatal error during application start. Cannot get IDPs metadata."
);
process.exit(1);
}
return _;
})
.run();
}
Expand Down
38 changes: 29 additions & 9 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
* Defines services and register them to the Service Container.
*/
import * as dotenv from "dotenv";
import { isLeft } from "fp-ts/lib/Either";
import { isLeft, parseJSON, toError } from "fp-ts/lib/Either";
import { fromNullable, isSome } from "fp-ts/lib/Option";
import {
getNodeEnvironmentFromProcessEnv,
NodeEnvironmentEnum
} from "italia-ts-commons/lib/environment";
import { ReadableReporter } from "italia-ts-commons/lib/reporters";
import {
errorsToReadableMessages,
ReadableReporter
} from "italia-ts-commons/lib/reporters";
import { CIDR } from "italia-ts-commons/lib/strings";
import { UrlFromString } from "italia-ts-commons/lib/url";

Expand All @@ -28,6 +31,7 @@ import {
} from "@pagopa/io-spid-commons";

import RedisSessionStorage from "./services/redisSessionStorage";
import { STRINGS_RECORD } from "./types/commons";
import {
createClusterRedisClient,
createSimpleRedisClient
Expand Down Expand Up @@ -57,18 +61,19 @@ export const CACHE_MAX_AGE_SECONDS: number = parseInt(

// Private key used in SAML authentication to a SPID IDP.
const samlKey = () => {
return readFile(
process.env.SAML_KEY_PATH || "./certs/key.pem",
"SAML private key"
return fromNullable(process.env.SAML_KEY).getOrElse(
readFile(process.env.SAML_KEY_PATH || "./certs/key.pem", "SAML private key")
);
};
export const SAML_KEY = samlKey();

// Public certificate used in SAML authentication to a SPID IDP.
const samlCert = () => {
return readFile(
process.env.SAML_CERT_PATH || "./certs/cert.pem",
"SAML certificate"
return fromNullable(process.env.SAML_CERT).getOrElse(
readFile(
process.env.SAML_CERT_PATH || "./certs/cert.pem",
"SAML certificate"
)
);
};

Expand Down Expand Up @@ -104,6 +109,20 @@ const SPID_TESTENV_URL =
export const IDP_METADATA_URL = getRequiredENVVar("IDP_METADATA_URL");
const CIE_METADATA_URL = getRequiredENVVar("CIE_METADATA_URL");

export const STARTUP_IDPS_METADATA:
| Record<string, string>
| undefined = fromNullable(process.env.STARTUP_IDPS_METADATA)
.map(_ =>
parseJSON(_, toError)
.chain<Record<string, string> | undefined>(_1 =>
STRINGS_RECORD.decode(_1).mapLeft(
err => new Error(errorsToReadableMessages(err).join(" / "))
)
)
.getOrElse(undefined)
)
.getOrElse(undefined);

export const CLIENT_ERROR_REDIRECTION_URL =
process.env.CLIENT_ERROR_REDIRECTION_URL || "/error.html";

Expand All @@ -116,7 +135,8 @@ export const appConfig: IApplicationConfig = {
clientLoginRedirectionUrl: CLIENT_REDIRECTION_URL,
loginPath: "/login",
metadataPath: "/metadata",
sloPath: "/slo"
sloPath: "/slo",
startupIdpsMetadata: STARTUP_IDPS_METADATA
};

const maybeSpidValidatorUrlOption = fromNullable(
Expand Down
3 changes: 3 additions & 0 deletions src/types/commons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ export const SuccessResponse = t.interface({
});

export type SuccessResponse = t.TypeOf<typeof SuccessResponse>;

export const STRINGS_RECORD = t.record(t.string, t.string);
export type STRINGS_RECORD = t.TypeOf<typeof STRINGS_RECORD>;
Loading

0 comments on commit 006d258

Please sign in to comment.