implemented:
- altering of mail settings - sending email Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -13,6 +13,7 @@ dependencies {
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
implementation 'org.json:json:20240303'
|
||||
implementation 'org.bitbucket.b_c:jose4j:0.9.6'
|
||||
implementation 'com.sun.mail:jakarta.mail:2.0.1'
|
||||
implementation project(':de.srsoftware.utils')
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,8 @@ public class Constants {
|
||||
public static final String RESPONSE_TYPE = "response_type";
|
||||
public static final String SCOPE = "scope";
|
||||
public static final String SECRET = "secret";
|
||||
public static final String SENDER_ADDRESS = "sender_address";
|
||||
public static final String SMTP_USER = "smtp_user";
|
||||
public static final String SMTP_PASSWORD = "smtp_pass";
|
||||
public static final String SMTP_AUTH = "smtp_auth";
|
||||
public static final String SMTP_HOST = "smtp_host";
|
||||
public static final String SMTP_PORT = "smtp_port";
|
||||
|
||||
@@ -3,6 +3,7 @@ package de.srsoftware.oidc.api;
|
||||
|
||||
import static de.srsoftware.oidc.api.Constants.*;
|
||||
|
||||
import jakarta.mail.Authenticator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
@@ -35,15 +36,20 @@ public interface MailConfig {
|
||||
props.put("mail.smtp.port", smtpPort());
|
||||
props.put("mail.smtp.auth", smtpAuth() ? "true" : "false");
|
||||
props.put("mail.smtp.starttls.enable", startTls() ? "true" : "false");
|
||||
props.put("mail.smtp.ssl.trust", smtpHost());
|
||||
return props;
|
||||
}
|
||||
|
||||
default Map<String, Object> map() {
|
||||
return Map.of( //
|
||||
SMTP_HOST, smtpHost(), //
|
||||
SMTP_PORT, smtpPort(), //
|
||||
SMTP_AUTH, smtpAuth(), //
|
||||
SENDER_ADDRESS, senderAddress(), //
|
||||
return Map.of( //
|
||||
SMTP_HOST, smtpHost(), //
|
||||
SMTP_PORT, smtpPort(), //
|
||||
SMTP_AUTH, smtpAuth(), //
|
||||
SMTP_USER, senderAddress(), //
|
||||
START_TLS, startTls());
|
||||
}
|
||||
|
||||
Authenticator authenticator();
|
||||
|
||||
MailConfig save();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* © SRSoftware 2024 */
|
||||
package de.srsoftware.oidc.backend;
|
||||
|
||||
import static de.srsoftware.oidc.api.Constants.*;
|
||||
import static de.srsoftware.oidc.api.data.Permission.MANAGE_SMTP;
|
||||
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
|
||||
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
|
||||
@@ -31,8 +32,33 @@ public class EmailController extends Controller {
|
||||
return notFound(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doPost(String path, HttpExchange ex) throws IOException {
|
||||
var optSession = getSession(ex);
|
||||
if (optSession.isEmpty()) return sendEmptyResponse(HTTP_UNAUTHORIZED, ex);
|
||||
var user = optSession.get().user();
|
||||
switch (path) {
|
||||
case "/settings":
|
||||
return saveSettings(ex, user);
|
||||
}
|
||||
return notFound(ex);
|
||||
}
|
||||
|
||||
private boolean provideSettings(HttpExchange ex, User user) throws IOException {
|
||||
if (!user.hasPermission(MANAGE_SMTP)) return sendEmptyResponse(HTTP_FORBIDDEN, ex);
|
||||
return sendContent(ex, mailConfig.map());
|
||||
}
|
||||
|
||||
private boolean saveSettings(HttpExchange ex, User user) throws IOException {
|
||||
if (!user.hasPermission(MANAGE_SMTP)) return sendEmptyResponse(HTTP_FORBIDDEN, ex);
|
||||
var data = json(ex);
|
||||
if (data.has(SMTP_HOST)) mailConfig.smtpHost(data.getString(SMTP_HOST));
|
||||
if (data.has(SMTP_PORT)) mailConfig.smtpPort(data.getInt(SMTP_PORT));
|
||||
if (data.has(SMTP_USER)) mailConfig.senderAddress(data.getString(SMTP_USER));
|
||||
if (data.has(SMTP_PASSWORD)) mailConfig.senderPassword(data.getString(SMTP_PASSWORD));
|
||||
if (data.has(SMTP_AUTH)) mailConfig.smtpAuth(data.getBoolean(SMTP_AUTH));
|
||||
if (data.has(START_TLS)) mailConfig.startTls(data.getBoolean(START_TLS));
|
||||
mailConfig.save();
|
||||
return sendContent(ex, "saved");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,20 +20,13 @@ import java.util.Optional;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class UserController extends Controller {
|
||||
private final UserService users;
|
||||
private final MailConfig mailConfig;
|
||||
private final Authenticator auth;
|
||||
private final UserService users;
|
||||
private final MailConfig mailConfig;
|
||||
|
||||
public UserController(MailConfig mailConfig, SessionService sessionService, UserService userService) {
|
||||
super(sessionService);
|
||||
users = userService;
|
||||
this.mailConfig = mailConfig;
|
||||
auth = new Authenticator() {
|
||||
// override the getPasswordAuthentication method
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(mailConfig.senderAddress(), mailConfig.senderPassword());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean addUser(HttpExchange ex, Session session) throws IOException {
|
||||
@@ -127,7 +120,7 @@ public class UserController extends Controller {
|
||||
private void senPasswordLink(User user) {
|
||||
LOG.log(WARNING, "Sending password link to {0}", user.email());
|
||||
try {
|
||||
var session = jakarta.mail.Session.getDefaultInstance(mailConfig.props(), auth);
|
||||
var session = jakarta.mail.Session.getDefaultInstance(mailConfig.props(), mailConfig.authenticator());
|
||||
Message message = new MimeMessage(session);
|
||||
message.setFrom(new InternetAddress(mailConfig.senderAddress()));
|
||||
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(user.email()));
|
||||
|
||||
@@ -16,6 +16,7 @@ dependencies {
|
||||
implementation project(':de.srsoftware.utils')
|
||||
implementation 'org.json:json:20240303'
|
||||
implementation 'org.bitbucket.b_c:jose4j:0.9.6'
|
||||
implementation 'com.sun.mail:jakarta.mail:2.0.1'
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import static java.util.Optional.empty;
|
||||
|
||||
import de.srsoftware.oidc.api.*;
|
||||
import de.srsoftware.oidc.api.data.*;
|
||||
import jakarta.mail.Authenticator;
|
||||
import jakarta.mail.PasswordAuthentication;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
@@ -37,6 +39,7 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
|
||||
private Map<String, Client> clients = new HashMap<>();
|
||||
private Map<String, User> accessTokens = new HashMap<>();
|
||||
private Map<String, Authorization> authCodes = new HashMap<>();
|
||||
private Authenticator auth;
|
||||
|
||||
public FileStore(File storage, PasswordHasher<String> passwordHasher) throws IOException {
|
||||
this.storageFile = storage.toPath();
|
||||
@@ -48,9 +51,10 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
|
||||
Files.writeString(storageFile, "{}");
|
||||
}
|
||||
json = new JSONObject(Files.readString(storageFile));
|
||||
auth = null; // lazy init!
|
||||
}
|
||||
|
||||
private FileStore save() {
|
||||
public FileStore save() {
|
||||
try {
|
||||
Files.writeString(storageFile, json.toString(2));
|
||||
return this;
|
||||
@@ -319,6 +323,19 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
|
||||
|
||||
/*** MailConfig implementation ***/
|
||||
|
||||
@Override
|
||||
public Authenticator authenticator() {
|
||||
if (auth == null) {
|
||||
auth = new Authenticator() {
|
||||
// override the getPasswordAuthentication method
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(senderAddress(), senderPassword());
|
||||
}
|
||||
};
|
||||
}
|
||||
return auth;
|
||||
}
|
||||
|
||||
private String mailConfig(String key) {
|
||||
var config = json.getJSONObject(MAILCONFIG);
|
||||
if (config.has(key)) return config.getString(key);
|
||||
@@ -328,71 +345,71 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
|
||||
private FileStore mailConfig(String key, Object newValue) {
|
||||
var config = json.getJSONObject(MAILCONFIG);
|
||||
config.put(key, newValue);
|
||||
auth = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String smtpHost() {
|
||||
return mailConfig("smtp_host");
|
||||
return mailConfig(SMTP_HOST);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MailConfig smtpHost(String newValue) {
|
||||
return mailConfig("smtp_host", newValue);
|
||||
return mailConfig(SMTP_HOST, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int smtpPort() {
|
||||
try {
|
||||
return Integer.parseInt(mailConfig("smtp_port"));
|
||||
} catch (NumberFormatException nfe) {
|
||||
return 0;
|
||||
}
|
||||
var config = json.getJSONObject(MAILCONFIG);
|
||||
return config.has(SMTP_PORT) ? config.getInt(SMTP_PORT) : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MailConfig smtpPort(int newValue) {
|
||||
return mailConfig("smtp_port", newValue);
|
||||
return mailConfig(SMTP_PORT, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String senderAddress() {
|
||||
return mailConfig("sender_address");
|
||||
return mailConfig(SMTP_USER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MailConfig senderAddress(String newValue) {
|
||||
return mailConfig("sender_address", newValue);
|
||||
return mailConfig(SMTP_USER, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String senderPassword() {
|
||||
return mailConfig("smtp_password");
|
||||
return mailConfig(SMTP_PASSWORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MailConfig senderPassword(String newValue) {
|
||||
return mailConfig("smtp_password", newValue);
|
||||
return mailConfig(SMTP_PASSWORD, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startTls() {
|
||||
return "true".equals(mailConfig("start_tls"));
|
||||
var config = json.getJSONObject(MAILCONFIG);
|
||||
return config.has(START_TLS) ? config.getBoolean(START_TLS) : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MailConfig startTls(boolean newValue) {
|
||||
return mailConfig("start_tls", newValue);
|
||||
return mailConfig(START_TLS, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean smtpAuth() {
|
||||
return "true".equals(mailConfig("smtp_auth"));
|
||||
var config = json.getJSONObject(MAILCONFIG);
|
||||
return config.has(SMTP_AUTH) ? config.getBoolean(SMTP_AUTH) : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MailConfig smtpAuth(boolean newValue) {
|
||||
return mailConfig("smtp_auth", newValue);
|
||||
return mailConfig(SMTP_AUTH, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ function hide(id){
|
||||
get(id).style.display = 'none';
|
||||
}
|
||||
|
||||
function isChecked(id){
|
||||
return get(id).checked;
|
||||
}
|
||||
|
||||
function login(){
|
||||
redirect('login.html?return_to='+encodeURIComponent(window.location.href));
|
||||
}
|
||||
|
||||
@@ -28,6 +28,24 @@ async function handlePasswordResponse(response){
|
||||
},10000);
|
||||
}
|
||||
|
||||
async function handleSmtpResponse(response){
|
||||
if (response.ok){
|
||||
hide('wrong_password');
|
||||
hide('password_mismatch');
|
||||
setText('smtpBtn', 'saved.');
|
||||
} else {
|
||||
setText('smtpBtn', 'Update failed!');
|
||||
var text = await response.text();
|
||||
if (text == 'wrong password') show('wrong_password');
|
||||
if (text == 'password mismatch') show('password_mismatch');
|
||||
|
||||
}
|
||||
setTimeout(function(){
|
||||
enable('smtpBtn');
|
||||
setText('smtpBtn','Update');
|
||||
},10000);
|
||||
}
|
||||
|
||||
function handleResponse(response){
|
||||
if (response.ok){
|
||||
hide('update_error')
|
||||
@@ -36,8 +54,8 @@ function handleResponse(response){
|
||||
show('update_error');
|
||||
setText('updateBtn', 'Update failed!');
|
||||
}
|
||||
enable('updateBtn');
|
||||
setTimeout(function(){
|
||||
enable('updateBtn');
|
||||
setText('updateBtn','Update');
|
||||
},10000);
|
||||
}
|
||||
@@ -49,6 +67,8 @@ async function handleSettings(response){
|
||||
for (var key in json){
|
||||
setValue(key,json[key]);
|
||||
}
|
||||
get('start_tls').checked = json.start_tls;
|
||||
get('smtp_auth').checked = json.smtp_auth;
|
||||
show('mail_settings');
|
||||
} else {
|
||||
hide('mail_settings');
|
||||
@@ -59,6 +79,25 @@ function passKeyDown(ev){
|
||||
if (event.keyCode == 13) updatePass();
|
||||
}
|
||||
|
||||
function updateSmtp(){
|
||||
disable('smtpBtn');
|
||||
var newData = {
|
||||
smtp_host : getValue('smtp_host'),
|
||||
smtp_port : getValue('smtp_port'),
|
||||
smtp_user : getValue('smtp_user'),
|
||||
smtp_pass : getValue('smtp_pass'),
|
||||
smtp_auth : isChecked('smtp_auth'),
|
||||
start_tls : isChecked('start_tls')
|
||||
}
|
||||
fetch("/api/email/settings",{
|
||||
method : 'POST',
|
||||
headers : {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body : JSON.stringify(newData)
|
||||
}).then(handleSmtpResponse);
|
||||
setText('smtpBtn','sent…');
|
||||
}
|
||||
|
||||
|
||||
function updatePass(){
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
<br/>
|
||||
<fieldset id="mail_settings" style="display: none">
|
||||
<legend>
|
||||
Mail settings
|
||||
@@ -85,20 +86,32 @@
|
||||
<table>
|
||||
<tr>
|
||||
<th>Smtp host</th>
|
||||
<td><input type="text" id="smtp_host"></td>
|
||||
<td><input type="text" id="smtp_host" placeholder="smtp host"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Smtp port</th>
|
||||
<td><input type="text" id="smtp_port"></td>
|
||||
<td><input type="number" id="smtp_port"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Sender email address</th>
|
||||
<td><input type="text" id="sender_mail"></td>
|
||||
<th>Smtp user</th>
|
||||
<td><input type="text" id="smtp_user" placeholder="smtp user"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Sender password</th>
|
||||
<td><input type="password" id="sender_password"></td>
|
||||
<th>Smtp password</th>
|
||||
<td><input type="password" id="smtp_pass" placeholder="password"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Security</th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" id="smtp_auth"> Auth
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" id="start_tls"> StartTLS
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><button id="smtpBtn" type="button" onClick="updateSmtp()">Update</button></td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user