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
28 changes: 28 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# The contents of this file are subject to the terms of the Common Development and
# Distribution License (the License). You may not use this file except in compliance with the
# License.
#
# You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
# specific language governing permission and limitations under the License.
#
# When distributing Covered Software, include this CDDL Header Notice in each file and include
# the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
# Header, with the fields enclosed by brackets [] replaced by your own identifying
# information: "Portions copyright [year] [name of copyright owner]".
#
# Copyright 2026 3A Systems, LLC.
version: 2
updates:
# Keeps the SHA-pinned third-party actions in .github/workflows up to date: Dependabot
# bumps the commit hash and the trailing "# vX.Y.Z" version comment together.
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
groups:
# One pull request per week for all action updates instead of one per action.
github-actions:
patterns: ["*"]
labels:
- "ci"
- "dependencies"
18 changes: 10 additions & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ on:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
build-maven:
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -299,20 +301,20 @@ jobs:
echo "release_version=$git_version_last" >> $GITHUB_ENV
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
with:
images: |
localhost:5000/${{ github.repository }}
tags: |
type=raw,value=${{ env.release_version }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
with:
driver-opts: network=host
- name: Build image (default)
uses: docker/build-push-action@v7
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
continue-on-error: true
with:
context: .
Expand Down Expand Up @@ -347,22 +349,22 @@ jobs:
echo "release_version=$git_version_last" >> $GITHUB_ENV
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
with:
images: |
localhost:5000/${{ github.repository }}
tags: |
type=raw,value=alpine
type=raw,value=${{ env.release_version }}-alpine
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
with:
driver-opts: network=host
- name: Build image
continue-on-error: true
uses: docker/build-push-action@v7
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
context: .
file: ./Dockerfile-alpine
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@ on:
concurrency:
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: false
permissions:
contents: read

jobs:
deploy-maven:
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event=='push' }}
runs-on: 'ubuntu-latest'
permissions:
contents: write # the docs push to the repository wiki uses github.token
steps:
- name: Print github context
env:
Expand Down
36 changes: 23 additions & 13 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ on:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
jobs:
release-maven:
runs-on: 'ubuntu-latest'
permissions:
contents: write # release:prepare pushes the release tag, action-gh-release creates the release, docs go to the wiki
steps:
- name: Print github context
env:
Expand Down Expand Up @@ -75,7 +79,7 @@ jobs:
if: ${{ env.MAVEN_USERNAME!='' && env.MAVEN_PASSWORD!='' }}
run: mvn --batch-mode -Darguments="-Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }}" -DsignTag=true -DtagNameFormat="${{ github.event.inputs.releaseVersion }}" -DreleaseVersion=${{ github.event.inputs.releaseVersion }} -DdevelopmentVersion=${{ github.event.inputs.developmentVersion }} release:prepare release:perform --file pom.xml
- name: Release on GitHub
uses: softprops/action-gh-release@v3
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with:
name: ${{ github.event.inputs.releaseVersion }}
tag_name: ${{ github.event.inputs.releaseVersion }}
Expand Down Expand Up @@ -128,6 +132,9 @@ jobs:
release-docker:
name: Docker release
runs-on: 'ubuntu-latest'
permissions:
contents: read
packages: write # push to ghcr.io with GITHUB_TOKEN
needs:
- release-maven
steps:
Expand All @@ -138,7 +145,7 @@ jobs:
submodules: recursive
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
with:
images: |
${{ github.repository }}
Expand All @@ -147,22 +154,22 @@ jobs:
type=raw,value=latest
type=raw,value=${{ github.event.inputs.releaseVersion }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Login to DockerHub
uses: docker/login-action@v4
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GHCR
uses: docker/login-action@v4
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v7
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
continue-on-error: true
with:
context: .
Expand All @@ -176,6 +183,9 @@ jobs:
release-docker-alpine:
name: Docker release
runs-on: 'ubuntu-latest'
permissions:
contents: read
packages: write # push to ghcr.io with GITHUB_TOKEN
needs:
- release-maven
steps:
Expand All @@ -186,7 +196,7 @@ jobs:
submodules: recursive
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
with:
images: |
${{ github.repository }}
Expand All @@ -195,23 +205,23 @@ jobs:
type=raw,value=alpine
type=raw,value=${{ github.event.inputs.releaseVersion }}-alpine
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Login to DockerHub
uses: docker/login-action@v4
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GHCR
uses: docker/login-action@v4
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
continue-on-error: true
uses: docker/build-push-action@v7
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
context: .
file: ./Dockerfile-alpine
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
information: "Portions copyright [year] [name of copyright owner]".

Copyright 2017 ForgeRock AS.
Portions Copyright 2024 3A Systems LLC.
Portions Copyright 2024-2026 3A Systems LLC.
////

:figure-caption!:
Expand Down Expand Up @@ -123,6 +123,13 @@ If `"enable" : false`, you can leave the entries for `"username"` and `"password

`starttls`::
If `"enable" : true`, enables the use of the STARTTLS command (if supported by the server) to switch the connection to a TLS-protected connection before issuing any login commands. If the server does not support STARTTLS, the connection continues without the use of TLS.
+
The SMTP server certificate is validated against the JVM trust store. Two optional settings relax that:
+

* `trustedHosts`—a list of SMTP host names whose certificate is accepted without validation, for example `"trustedHosts" : [ "smtp.internal.example.com" ]`.

* `trustAll`—when `true`, accepts any server certificate. This disables protection against man-in-the-middle attacks; use it only in development environments.

`from`::
(Optional) Specifies a default `From:` address, that users see when they receive emails from OpenIDM.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,21 @@
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Portions Copyright 2026 3A Systems, LLC.
*/

package org.forgerock.openidm.external.email.impl;

import com.sun.mail.util.MailSSLSocketFactory;
import org.forgerock.json.JsonValue;
import org.forgerock.json.resource.BadRequestException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
Expand All @@ -42,6 +49,8 @@
*/
public class EmailClient {

private static final Logger logger = LoggerFactory.getLogger(EmailClient.class);

private static final String DEFAULT_HOST = "localhost";
private static final String DEFAULT_PORT = "25";
private String username = null;
Expand All @@ -60,6 +69,10 @@ public class EmailClient {
public static final String CONFIG_MAIL_SMTP_AUTH_USERNAME = "username";
public static final String CONFIG_MAIL_SMTP_STARTTLS = "starttls";
public static final String CONFIG_MAIL_SMTP_STARTTLS_ENABLE = "enable";
/** Opt-in: accept any server certificate over STARTTLS. Never use outside development. */
public static final String CONFIG_MAIL_SMTP_STARTTLS_TRUST_ALL = "trustAll";
/** Optional list of SMTP hosts whose certificate is accepted without validation. */
public static final String CONFIG_MAIL_SMTP_STARTTLS_TRUSTED_HOSTS = "trustedHosts";
public static final String CONFIG_MAIL_FROM = "from";
public static final String CONFIG_MAIL_DEBUG = "debug";

Expand All @@ -83,19 +96,39 @@ public EmailClient(JsonValue config) throws RuntimeException {
boolean startTLS = starttlsConfig.get(CONFIG_MAIL_SMTP_STARTTLS_ENABLE).defaultTo(false).asBoolean();
if (startTLS) {
props.put("mail.smtp.starttls.enable", String.valueOf(startTLS));
// temporary hack to avoid cert check
try {
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
props.put("mail.smtp.ssl.socketFactory", sf);
} catch (Exception e) {
}
configureStartTlsTrust(starttlsConfig);
}

fromAddr = config.get(CONFIG_MAIL_FROM).asString();
session = Session.getInstance(props);
}

/**
* By default the server certificate is validated against the JVM trust store. A custom
* socket factory is installed only when the configuration explicitly relaxes that, either
* for a list of {@code trustedHosts} or, for development only, for all hosts.
*/
private void configureStartTlsTrust(JsonValue starttlsConfig) {
boolean trustAll = starttlsConfig.get(CONFIG_MAIL_SMTP_STARTTLS_TRUST_ALL).defaultTo(false).asBoolean();
List<String> trustedHosts = starttlsConfig.get(CONFIG_MAIL_SMTP_STARTTLS_TRUSTED_HOSTS)
.defaultTo(Collections.emptyList()).asList(String.class);
if (!trustAll && trustedHosts.isEmpty()) {
return;
}
try {
MailSSLSocketFactory sf = new MailSSLSocketFactory();
if (trustAll) {
logger.warn("SMTP STARTTLS certificate validation is disabled (starttls.trustAll=true)");
sf.setTrustAllHosts(trustAll);
} else {
sf.setTrustedHosts(trustedHosts.toArray(new String[0]));
}
props.put("mail.smtp.ssl.socketFactory", sf);
} catch (GeneralSecurityException e) {
throw new IllegalStateException("Unable to configure the SMTP STARTTLS socket factory", e);
}
}

/**
* Send the email according to the parameters in <em>params</em>:
*
Expand Down
Loading
Loading