Skip to content

Commit

Permalink
Added a new option for password via environment - Closes #2
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-schnell committed Jan 20, 2024
1 parent 003ecea commit e9f5126
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 21 deletions.
32 changes: 17 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A simple command line application for sending mails.
[![Java Development Kit 11](https://img.shields.io/badge/JDK-11-green.svg)](https://openjdk.java.net/projects/jdk/11/)

## Versions
- 0.2.x = **Java 11** with new 'jakarta' namespace
- > 0.2.x = **Java 11** with new 'jakarta' namespace
- 0.1.0 = **Java 8**

## Why?
Expand All @@ -25,19 +25,20 @@ You can download the latest JAR file here: https://github.com/fuinorg/sjsm/relea

## Command line arguments

| Argument | Value | Required | Example |
| -------- |--------------------------------------------------------| -------- |--------------------------------------------------------------------------------------------------|
| -host | SMTPS server name | yes | "smtp.no-where-no-no.com" |
| -port | SMTPS port number (SSL/TLS) | yes | 465 |
| -user | Your mailbox user | yes | "acc12345_from.not.exist" or "[email protected]" (depends on your mail provider) |
| -pw | Your mailbox password | yes | "xxxxxxx" |
| -from | Sender's email address | yes | "[email protected]" |
| -to | Receiver's email address (multiple separated with ";") | yes | "[email protected]" or "[email protected];[email protected]" |
| -subject | Mail subject | yes | "My subject" |
| -message | Message body (TEXT or HTML) | yes | "<html><body><h1>This is a test mail</h1></body></html>" |
| -html | - | no | - |
| -charset | Mail encoding (defaults to "utf-8") | no | "utf-8" |
| -important | Send High Priority Email (X-Priority flag) | no | - |
| Argument | Value | Required | Example |
|------------|---------------------------------------------------------------------|----------|--------------------------------------------------------------------------------------------------|
| -host | SMTPS server name | yes | "smtp.no-where-no-no.com" |
| -port | SMTPS port number (SSL/TLS) | yes | 465 |
| -user | Your mailbox user | yes | "acc12345_from.not.exist" or "[email protected]" (depends on your mail provider) |
| -pw | Your mailbox password (Either `pw`or `envPw` is mandatory) | no | - |
| -envPw | Name of an environment variable that contains your mailbox password | no | - |
| -from | Sender's email address | yes | "[email protected]" |
| -to | Receiver's email address (multiple separated with ";") | yes | "[email protected]" or "[email protected];[email protected]" |
| -subject | Mail subject | yes | "My subject" |
| -message | Message body (TEXT or HTML) | yes | "<html><body><h1>This is a test mail</h1></body></html>" |
| -html | - | no | - |
| -charset | Mail encoding (defaults to "utf-8") | no | "utf-8" |
| -important | Send High Priority Email (X-Priority flag) | no | - |

## TEXT example

Expand Down Expand Up @@ -65,7 +66,8 @@ You can download the latest JAR file here: https://github.com/fuinorg/sjsm/relea
-html \

## CAUTION
<b>Be aware that passing your password via the command line will most probably be visible in your command line history.</b>
:warning: Be aware that passing your password via the command line (`-pw`) will most probably be visible in your command line history.
It's better to use an environment variable with `-envPw`.

* * *

Expand Down
23 changes: 23 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
Expand All @@ -75,12 +82,28 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-lambda</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>

<plugins>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.3</version>
<configuration>
<argLine>-Djava.security.SecurityManager=allow --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED</argLine>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
Expand Down
32 changes: 30 additions & 2 deletions src/main/java/org/fuin/sjsm/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@ public final class Config {
@Option(name = "-user", usage = "User", metaVar = "USER", required = true)
private String user;

@Option(name = "-pw", usage = "Password", metaVar = "PW", required = true)
@Option(name = "-pw", usage = "Password", metaVar = "PW")
private String pw;

@Option(name = "-envPw", usage = "Name of an environment variable that contains the password", metaVar = "ENV_PW")
private String envPw;

@Option(name = "-from", usage = "Sender", metaVar = "SEND", required = true)
private String from;

Expand Down Expand Up @@ -149,6 +152,25 @@ public void setPw(final String pw) {
this.pw = pw;
}

/**
* Returns the name of an environment variable that contains the password.
*
* @return Environment variable with password.
*/
public String getEnvPw() {
return envPw;
}

/**
* Sets the name of an environment variable that contains the password.
*
* @param envPw
* Environment variable to use.
*/
public void setEnvPw(final String envPw) {
this.envPw = envPw;
}

/**
* Returns the important (X-Priority) flag.
*
Expand Down Expand Up @@ -440,7 +462,13 @@ public Properties createSessionProperties() {
public Authenticator createAuthenticator() {
return new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, pw);
final String p;
if (pw == null) {
p = System.getenv(envPw);
} else {
p = pw;
}
return new PasswordAuthentication(user, p);
}
};
}
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/org/fuin/sjsm/Messages.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.fuin.sjsm;

import org.kohsuke.args4j.Localizable;

import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;

/**
* Message constants.
*/
public enum Messages implements Localizable {

/** Neither 'pw' nor 'envPw' argument is set. */
MISSING_PASSWORD_OPTION,

/** The environment variable from 'envPw' argument is not set. */
PASSWORD_ENV_VAR_NOT_SET;

@Override
public String formatWithLocale(final Locale locale, final Object... args) {
final ResourceBundle localized = ResourceBundle.getBundle("sjsm-messages", locale);
return MessageFormat.format(localized.getString(name()), args);
}

@Override
public String format(final Object... args) {
return formatWithLocale(Locale.getDefault(), args);
}

}
11 changes: 11 additions & 0 deletions src/main/java/org/fuin/sjsm/SendMailApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ public void send(final Config config) {

}

private static void ensurePasswordIsSet(final CmdLineParser parser , final Config config) throws CmdLineException {
if (config.getPw() == null && config.getEnvPw() == null) {
throw new CmdLineException(parser, Messages.MISSING_PASSWORD_OPTION);
}
if (config.getEnvPw() != null && System.getenv(config.getEnvPw()) == null) {
throw new CmdLineException(parser, Messages.PASSWORD_ENV_VAR_NOT_SET, config.getEnvPw());
}
}


/**
* Main application method.
*
Expand All @@ -78,6 +88,7 @@ public static void main(final String[] args) {
final CmdLineParser parser = new CmdLineParser(config);
try {
parser.parseArgument(args);
ensurePasswordIsSet(parser, config);
new SendMailApp().send(config);
System.exit(0);
} catch (final CmdLineException ex) {
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/sjsm-messages.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
MISSING_PASSWORD_OPTION=A password or an environment variable with the password is mandatory
PASSWORD_ENV_VAR_NOT_SET=The environment variable {0} is not set (has no value)
115 changes: 111 additions & 4 deletions src/test/java/org/fuin/sjsm/SendMailAppTest.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
/**
* Copyright (C) 2015 Michael Schnell. All rights reserved.
* Copyright (C) 2015 Michael Schnell. All rights reserved.
* http://www.fuin.org/
*
* <p>
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
*
* <p>
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* <p>
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
package org.fuin.sjsm;

import static com.github.stefanbirkner.systemlambda.SystemLambda.*;
import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -122,4 +125,108 @@ void testSendMultipleReceiver() {

}

@Test
void testMissingPasswordOption() throws Exception {

final String[] args = new String[]{
"-host", "localhost",
"-port", "" + dumbster.getPort(),
"-user", "myaccount",
"-from", "[email protected]",
"-to", "[email protected]",
"-subject", "Whatever",
"-message", "None"
};

final AtomicLong exitCode = new AtomicLong();
final String systemErr = tapSystemErr(() -> {
exitCode.set(catchSystemExit(() -> {
SendMailApp.main(args);
}));
});
assertThat(systemErr).contains("A password or an environment variable with the password is mandatory");
assertThat(exitCode.get()).isEqualTo(1);

}

@Test
void testPwOption() throws Exception {

final String[] args = new String[]{
"-host", "localhost",
"-port", "" + dumbster.getPort(),
"-user", "myaccount",
"-pw", "abc",
"-from", "[email protected]",
"-to", "[email protected]",
"-subject", "Whatever",
"-message", "None",
"-smtp"
};

final AtomicLong exitCode = new AtomicLong();
final String systemOut = tapSystemOut(() -> {
exitCode.set(catchSystemExit(() -> {
SendMailApp.main(args);
}));
});
assertThat(systemOut).contains("Successfully sent message 'Whatever' to '[email protected]");
assertThat(exitCode.get()).isEqualTo(0);

}

@Test
void testEnvPwOption() throws Exception {

final String[] args = new String[]{
"-host", "localhost",
"-port", "" + dumbster.getPort(),
"-user", "myaccount",
"-envPw", "MAIL_PW_TEST",
"-from", "[email protected]",
"-to", "[email protected]",
"-subject", "Whatever",
"-message", "None",
"-smtp"
};

final AtomicLong exitCode = new AtomicLong();
final AtomicReference<String> systemOut = new AtomicReference<>();
withEnvironmentVariable("MAIL_PW_TEST", "abc").execute(() -> {
systemOut.set(tapSystemOut(() -> {
exitCode.set(catchSystemExit(() -> {
SendMailApp.main(args);
}));
}));
});
assertThat(systemOut.get()).contains("Successfully sent message 'Whatever' to '[email protected]");
assertThat(exitCode.get()).isEqualTo(0);

}

@Test
void testMissingEnvPassword() throws Exception {

final String[] args = new String[]{
"-host", "localhost",
"-port", "" + dumbster.getPort(),
"-user", "myaccount",
"-envPw", "NOT_EXISTING_PW_ENV_VAR",
"-from", "[email protected]",
"-to", "[email protected]",
"-subject", "Whatever",
"-message", "None"
};

final AtomicLong exitCode = new AtomicLong();
final String systemErr = tapSystemErr(() -> {
exitCode.set(catchSystemExit(() -> {
SendMailApp.main(args);
}));
});
assertThat(systemErr).contains("The environment variable NOT_EXISTING_PW_ENV_VAR is not set");
assertThat(exitCode.get()).isEqualTo(1);

}

}

0 comments on commit e9f5126

Please sign in to comment.