-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Merge pull request #23 from GregDThomas/restore-compatibility
Ensure compatibility with newer versions of Openfire (fixes #22)
Showing
15 changed files
with
1,010 additions
and
889 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,92 +1,121 @@ | ||
import com.github.spotbugs.snom.Confidence | ||
import com.github.spotbugs.snom.Effort | ||
import com.github.spotbugs.snom.SpotBugsReport | ||
import com.github.spotbugs.snom.SpotBugsTask | ||
|
||
plugins { | ||
id 'java-library' | ||
id 'maven-publish' | ||
id 'checkstyle' | ||
id 'com.github.spotbugs' version '4.2.1' | ||
} | ||
apply from: 'build.openfire-plugin.gradle' | ||
|
||
ext.lombokVersion = '1.18.12' | ||
ext.junitVersion = '5.3.1' | ||
|
||
group = 'org.igniterealtime.openfire.plugins' | ||
|
||
java { | ||
sourceCompatibility = JavaVersion.VERSION_1_8 | ||
targetCompatibility = JavaVersion.VERSION_1_8 | ||
withJavadocJar() | ||
withSourcesJar() | ||
} | ||
|
||
test { | ||
useJUnitPlatform() | ||
testLogging { | ||
showStandardStreams = true | ||
} | ||
} | ||
|
||
checkstyle { | ||
toolVersion '8.31' | ||
maxWarnings 0 | ||
} | ||
|
||
tasks.withType(Checkstyle) { | ||
reports { | ||
xml.enabled = false | ||
html.enabled = true | ||
} | ||
exclude '**/org/jivesoftware/openfire/plugin/passwordreset/jsp/**' | ||
} | ||
|
||
spotbugs { | ||
effort = Effort.MAX | ||
reportLevel = Confidence.LOW | ||
ignoreFailures.set false | ||
extraArgs.add "-longBugCodes" | ||
} | ||
|
||
tasks.withType(SpotBugsTask) { | ||
reports(({ | ||
text.enabled = true | ||
} as Closure<NamedDomainObjectContainer<? extends SpotBugsReport>>)) | ||
|
||
//noinspection GroovyAssignabilityCheck | ||
task "${it.name}Report" { | ||
def input = file(reports.getByName("TEXT").destination) | ||
inputs.file input | ||
doLast { | ||
input.readLines().forEach { | ||
println(it) | ||
} | ||
} | ||
} | ||
it.finalizedBy "${it.name}Report" | ||
} | ||
|
||
spotbugsMain { | ||
classes = classes.filter { | ||
!it.path.contains(new File('/org/jivesoftware/openfire/plugin/passwordreset/jsp/').path) | ||
} | ||
} | ||
|
||
dependencies { | ||
compileOnly "org.projectlombok:lombok:${lombokVersion}" | ||
annotationProcessor "org.projectlombok:lombok:${lombokVersion}" | ||
|
||
testCompileOnly "org.projectlombok:lombok:${lombokVersion}" | ||
testAnnotationProcessor "org.projectlombok:lombok:${lombokVersion}" | ||
|
||
implementation 'com.github.bbottema:emailaddress-rfc2822:2.1.4' | ||
|
||
testImplementation 'com.github.spotbugs:spotbugs-annotations:4.0.1' | ||
testImplementation platform('org.junit:junit-bom:5.6.2') | ||
testImplementation 'org.junit.jupiter:junit-jupiter-api' | ||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' | ||
testImplementation 'org.mockito:mockito-junit-jupiter:3.3.3' | ||
testImplementation 'org.assertj:assertj-core:3.11.1' | ||
import com.github.spotbugs.snom.Confidence | ||
import com.github.spotbugs.snom.Effort | ||
import com.github.spotbugs.snom.SpotBugsReport | ||
import com.github.spotbugs.snom.SpotBugsTask | ||
|
||
plugins { | ||
id 'java-library' | ||
id 'maven-publish' | ||
id 'checkstyle' | ||
id 'com.github.spotbugs' version '6.0.27' | ||
id 'com.github.ben-manes.versions' version '0.51.0' | ||
} | ||
|
||
ext { | ||
minOpenfireVersion = '4.7.0' | ||
pluginName = 'Password Reset' | ||
pluginDescription = 'Provides the ability for users to reset their own passwords if they have forgotten them.' | ||
} | ||
|
||
java { | ||
toolchain { | ||
languageVersion.set(JavaLanguageVersion.of(11)) | ||
} | ||
withJavadocJar() | ||
withSourcesJar() | ||
} | ||
|
||
apply from: 'build.openfire-plugin.gradle' | ||
|
||
test { | ||
useJUnitPlatform() | ||
testLogging { | ||
showStandardStreams = true | ||
} | ||
} | ||
|
||
checkstyle { | ||
toolVersion = '8.31' | ||
maxWarnings = 0 | ||
} | ||
|
||
tasks.withType(Checkstyle).configureEach { | ||
reports { | ||
xml.required = false | ||
html.required = true | ||
} | ||
exclude '**/org/jivesoftware/openfire/plugin/passwordreset/jsp/**' | ||
} | ||
|
||
spotbugs { | ||
effort = Effort.MAX | ||
reportLevel = Confidence.values()[0] // LOW - See also https://github.com/spotbugs/spotbugs-gradle-plugin/issues/972 | ||
ignoreFailures.set false | ||
extraArgs.add "-longBugCodes" | ||
} | ||
|
||
//noinspection ConfigurationAvoidance - as this registers a new task, we can't use .forEach | ||
tasks.withType(SpotBugsTask) { | ||
reports(({ | ||
text.enabled = true | ||
xml.enabled = false | ||
} as Closure<NamedDomainObjectContainer<? extends SpotBugsReport>>)) | ||
|
||
//noinspection GroovyAssignabilityCheck | ||
tasks.register("${it.name}Report") { | ||
def input = reports.named("text").get().outputLocation.asFile.get() | ||
inputs.file input | ||
doLast { | ||
input.readLines().forEach { | ||
println(it) | ||
} | ||
} | ||
} | ||
it.finalizedBy "${it.name}Report" | ||
} | ||
|
||
spotbugsMain { | ||
classes = classes.filter { | ||
!it.path.contains(new File('/org/jivesoftware/openfire/plugin/passwordreset/jsp/').path) | ||
} | ||
} | ||
|
||
dependencies { | ||
|
||
def lombok = 'org.projectlombok:lombok:1.18.36' | ||
|
||
annotationProcessor lombok | ||
|
||
compileOnly lombok | ||
compileOnly 'com.github.spotbugs:spotbugs-annotations:4.8.6' | ||
|
||
implementation 'com.github.bbottema:emailaddress-rfc2822:2.3.1' | ||
|
||
testAnnotationProcessor lombok | ||
|
||
testCompileOnly lombok | ||
|
||
testImplementation 'com.github.spotbugs:spotbugs-annotations:4.0.1' | ||
testImplementation platform('org.junit:junit-bom:5.11.4') | ||
testImplementation 'org.junit.jupiter:junit-jupiter-api' | ||
testImplementation 'org.mockito:mockito-junit-jupiter:5.14.2' | ||
testImplementation 'org.assertj:assertj-core:3.27.0' | ||
|
||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher' | ||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' | ||
} | ||
|
||
dependencyUpdates.gradleReleaseChannel="current" | ||
|
||
// See https://github.com/jeremylong/DependencyCheck/issues/2764#issuecomment-680680558 flr an expla nation | ||
dependencies { | ||
components { | ||
withModule('org.dom4j:dom4j', ClearDependencies) | ||
} | ||
} | ||
|
||
class ClearDependencies implements ComponentMetadataRule { | ||
void execute(ComponentMetadataContext context) { | ||
context.details.allVariants { withDependencies { clear() } } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,2 @@ | ||
minOpenfireVersion=4.5.0 | ||
version=0.0.1-SNAPSHOT | ||
name=PasswordReset | ||
description=Provides the ability for users to reset their own passwords if they have forgotten them. | ||
pluginName=Password Reset | ||
version=0.0.1-SNAPSHOT | ||
name=PasswordReset |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
distributionBase=GRADLE_USER_HOME | ||
distributionPath=wrapper/dists | ||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-all.zip | ||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip | ||
networkTimeout=10000 | ||
validateDistributionUrl=true | ||
zipStoreBase=GRADLE_USER_HOME | ||
zipStorePath=wrapper/dists |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
config.stopBubbling = true | ||
lombok.addLombokGeneratedAnnotation = true | ||
lombok.addLombokGeneratedAnnotation = true | ||
lombok.extern.findbugs.addSuppressFBWarnings = true |
151 changes: 75 additions & 76 deletions
151
src/main/java/org/jivesoftware/openfire/plugin/passwordreset/PasswordResetMailer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,76 +1,75 @@ | ||
package org.jivesoftware.openfire.plugin.passwordreset; | ||
|
||
import java.net.URI; | ||
import java.util.Arrays; | ||
import java.util.HashSet; | ||
import java.util.Locale; | ||
import java.util.Set; | ||
import org.jivesoftware.openfire.user.User; | ||
import org.jivesoftware.util.EmailService; | ||
|
||
public class PasswordResetMailer { | ||
|
||
// Avoid sending emails to domains that are "special" | ||
// https://en.wikipedia.org/wiki/Example.com | ||
// https://en.wikipedia.org/wiki/.local | ||
// https://en.wikipedia.org/wiki/Top-level_domain#Reserved_domains | ||
private static final Set<String> IGNORED_DOMAINS = new HashSet<>( | ||
Arrays.asList( | ||
"example.com", | ||
"example.net", | ||
"example.org", | ||
"example.edu", | ||
"local", | ||
"example", | ||
"invalid", | ||
"localhost", | ||
"test" | ||
)); | ||
|
||
private final EmailService emailService; | ||
|
||
public PasswordResetMailer(final EmailService emailService) { | ||
this.emailService = emailService; | ||
} | ||
|
||
/** | ||
* Sends a password reset email. N | ||
* | ||
* @param user the user to whom the reset should be sent. | ||
* @param token the token used to identify the request | ||
*/ | ||
public void sendEmail(final User user, final String token) { | ||
final String email = user.getEmail(); | ||
// Ignore empty email address | ||
if (email == null || email.isEmpty()) { | ||
return; | ||
} | ||
|
||
// Ignore email addresses from (English) special domains | ||
final String lowerCaseEmail = email.toLowerCase(Locale.ENGLISH); | ||
for (final String ignoredDomain : IGNORED_DOMAINS) { | ||
if (lowerCaseEmail.endsWith(ignoredDomain)) { | ||
return; | ||
} | ||
} | ||
|
||
final URI uri = URI.create(PasswordResetPlugin.SERVER.getValue() | ||
+ "/change-password?token=" + token); | ||
final String subject = substitute(PasswordResetPlugin.SUBJECT.getValue(), user, uri); | ||
final String body = substitute(PasswordResetPlugin.BODY.getValue(), user, uri); | ||
|
||
emailService.sendMessage(user.getName(), email, | ||
PasswordResetPlugin.SENDER_NAME.getValue(), | ||
PasswordResetPlugin.SENDER_ADDRESS.getValue(), | ||
subject, body, null); | ||
} | ||
|
||
private String substitute( | ||
final String stringToSubstitute, final User user, final URI uri) { | ||
return stringToSubstitute.replace("${url}", uri.toASCIIString()) | ||
.replace("${userId}", user.getUsername()) | ||
.replace("${userName}", user.getName()) | ||
.replace("${userEmail}", user.getEmail()); | ||
} | ||
|
||
} | ||
package org.jivesoftware.openfire.plugin.passwordreset; | ||
|
||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; | ||
import java.net.URI; | ||
import java.util.Locale; | ||
import java.util.Set; | ||
import org.jivesoftware.openfire.user.User; | ||
import org.jivesoftware.util.EmailService; | ||
|
||
@SuppressFBWarnings({"EI_EXPOSE_REP2"}) | ||
public class PasswordResetMailer { | ||
|
||
// Avoid sending emails to domains that are "special" | ||
// https://en.wikipedia.org/wiki/Example.com | ||
// https://en.wikipedia.org/wiki/.local | ||
// https://en.wikipedia.org/wiki/Top-level_domain#Reserved_domains | ||
private static final Set<String> IGNORED_DOMAINS = Set.of( | ||
"example.com", | ||
"example.net", | ||
"example.org", | ||
"example.edu", | ||
"local", | ||
"example", | ||
"invalid", | ||
"localhost", | ||
"test" | ||
); | ||
|
||
private final EmailService emailService; | ||
|
||
public PasswordResetMailer(final EmailService emailService) { | ||
this.emailService = emailService; | ||
} | ||
|
||
/** | ||
* Sends a password reset email. N | ||
* | ||
* @param user the user to whom the reset should be sent. | ||
* @param token the token used to identify the request | ||
*/ | ||
public void sendEmail(final User user, final String token) { | ||
final String email = user.getEmail(); | ||
// Ignore empty email address | ||
if (email == null || email.isEmpty()) { | ||
return; | ||
} | ||
|
||
// Ignore email addresses from (English) special domains | ||
final String lowerCaseEmail = email.toLowerCase(Locale.ENGLISH); | ||
for (final String ignoredDomain : IGNORED_DOMAINS) { | ||
if (lowerCaseEmail.endsWith(ignoredDomain)) { | ||
return; | ||
} | ||
} | ||
|
||
final URI uri = URI.create(PasswordResetPlugin.SERVER.getValue() | ||
+ "/change-password?token=" + token); | ||
final String subject = substitute(PasswordResetPlugin.SUBJECT.getValue(), user, uri); | ||
final String body = substitute(PasswordResetPlugin.BODY.getValue(), user, uri); | ||
|
||
emailService.sendMessage(user.getName(), email, | ||
PasswordResetPlugin.SENDER_NAME.getValue(), | ||
PasswordResetPlugin.SENDER_ADDRESS.getValue(), | ||
subject, body, null); | ||
} | ||
|
||
private String substitute( | ||
final String stringToSubstitute, final User user, final URI uri) { | ||
return stringToSubstitute.replace("${url}", uri.toASCIIString()) | ||
.replace("${userId}", user.getUsername()) | ||
.replace("${userName}", user.getName()) | ||
.replace("${userEmail}", user.getEmail()); | ||
} | ||
|
||
} |
363 changes: 184 additions & 179 deletions
363
src/main/java/org/jivesoftware/openfire/plugin/passwordreset/PasswordResetPlugin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,179 +1,184 @@ | ||
package org.jivesoftware.openfire.plugin.passwordreset; | ||
|
||
import java.io.File; | ||
import java.time.Duration; | ||
import java.time.temporal.ChronoUnit; | ||
import java.util.Arrays; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.apache.tomcat.InstanceManager; | ||
import org.apache.tomcat.SimpleInstanceManager; | ||
import org.eclipse.jetty.webapp.WebAppContext; | ||
import org.jivesoftware.database.DbConnectionManager; | ||
import org.jivesoftware.openfire.XMPPServer; | ||
import org.jivesoftware.openfire.container.Plugin; | ||
import org.jivesoftware.openfire.container.PluginManager; | ||
import org.jivesoftware.openfire.http.HttpBindManager; | ||
import org.jivesoftware.openfire.user.UserManager; | ||
import org.jivesoftware.util.EmailService; | ||
import org.jivesoftware.util.LocaleUtils; | ||
import org.jivesoftware.util.SystemProperty; | ||
|
||
@Slf4j | ||
public class PasswordResetPlugin implements Plugin { | ||
|
||
public static final String PLUGIN_NAME = "Password Reset"; // Exact match to plugin.xml | ||
public static final SystemProperty<Boolean> ENABLED = | ||
SystemProperty.Builder.ofType(Boolean.class) | ||
.setKey("plugin.passwordreset.enabled") | ||
.setDefaultValue(Boolean.FALSE) | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<String> SERVER = | ||
SystemProperty.Builder.ofType(String.class) | ||
.setKey("plugin.passwordreset.server") | ||
.setDefaultValue("") | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<String> SENDER_NAME = | ||
SystemProperty.Builder.ofType(String.class) | ||
.setKey("plugin.passwordreset.sender-name") | ||
.setDefaultValue("Openfire") | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<String> SENDER_ADDRESS = | ||
SystemProperty.Builder.ofType(String.class) | ||
.setKey("plugin.passwordreset.sender-address") | ||
.setDefaultValue("admin@example.com") | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<String> SUBJECT = | ||
SystemProperty.Builder.ofType(String.class) | ||
.setKey("plugin.passwordreset.email-subject") | ||
.setDefaultValue("Openfire password reset") | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<String> BODY = | ||
SystemProperty.Builder.ofType(String.class) | ||
.setKey("plugin.passwordreset.email-body") | ||
.setDefaultValue("Dear ${userName}\n\n" | ||
+ "To reset the password for your ${userId} Openfire account, simply go to ${url}" | ||
+ " at any time in the next five hours. After this time, you will need to request" | ||
+ " another reset email is sent to you.") | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<Duration> EXPIRY = | ||
SystemProperty.Builder.ofType(Duration.class) | ||
.setChronoUnit(ChronoUnit.MINUTES) | ||
.setKey("plugin.passwordreset.reset-expiry") | ||
.setDefaultValue(Duration.ofHours(5)) | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<Integer> MIN_LENGTH = | ||
SystemProperty.Builder.ofType(Integer.class) | ||
.setKey("plugin.passwordreset.min-length") | ||
.setMinValue(1) | ||
.setDefaultValue(8) | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<Integer> MAX_LENGTH = | ||
SystemProperty.Builder.ofType(Integer.class) | ||
.setKey("plugin.passwordreset.max-length") | ||
.setMinValue(0) | ||
.setDefaultValue(0) | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final String CONTEXT_PATH = "/passwordreset"; | ||
private static String canonicalName; | ||
private static PasswordResetPlugin plugin; | ||
private final HttpBindManager httpBindManager; | ||
private final PasswordResetMailer passwordResetMailer; | ||
private final PasswordResetTokenManager resetTokenManager; | ||
private WebAppContext webAppContext; | ||
|
||
private static void setInstance(final PasswordResetPlugin plugin) { | ||
PasswordResetPlugin.plugin = plugin; | ||
} | ||
|
||
private static void setCanonicalName(final String canonicalName) { | ||
PasswordResetPlugin.canonicalName = canonicalName; | ||
} | ||
|
||
public static PasswordResetPlugin getInstance() { | ||
return plugin; | ||
} | ||
|
||
/** | ||
* Default constructor for the plugin. | ||
*/ | ||
public PasswordResetPlugin() { | ||
setInstance(this); | ||
this.httpBindManager = HttpBindManager.getInstance(); | ||
this.passwordResetMailer = new PasswordResetMailer(EmailService.getInstance()); | ||
this.resetTokenManager = new PasswordResetTokenManager( | ||
DbConnectionManager::getConnection, | ||
UserManager.getInstance()); | ||
setBlankServerDetails(); | ||
log.debug("Plugin created"); | ||
} | ||
|
||
public static String localize(final String key, final Object... arguments) { | ||
return LocaleUtils.getLocalizedString(key, canonicalName, Arrays.asList(arguments)); | ||
} | ||
|
||
private void setBlankServerDetails() { | ||
if (SERVER.getValue().isEmpty()) { | ||
// Set a default value for this as there isn't one already | ||
final String defaultUrl; | ||
if (httpBindManager.isHttpsBindActive()) { | ||
defaultUrl = String.format("https://%s:%d%s", | ||
XMPPServer.getInstance().getServerInfo().getHostname(), | ||
httpBindManager.getHttpBindSecurePort(), | ||
CONTEXT_PATH); | ||
} else { | ||
defaultUrl = String.format("http://%s:%d%s", | ||
XMPPServer.getInstance().getServerInfo().getHostname(), | ||
httpBindManager.getHttpBindUnsecurePort(), | ||
CONTEXT_PATH); | ||
} | ||
SERVER.setValue(defaultUrl); | ||
} | ||
} | ||
|
||
@Override | ||
public void initializePlugin(final PluginManager manager, final File pluginDirectory) { | ||
log.debug("Plugin initialisation started"); | ||
|
||
setCanonicalName(manager.getCanonicalName(this)); | ||
|
||
webAppContext = new WebAppContext(pluginDirectory.getPath() + "/web-client", | ||
CONTEXT_PATH); | ||
webAppContext.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager()); | ||
httpBindManager.addJettyHandler(webAppContext); | ||
|
||
log.debug("Plugin initialisation complete"); | ||
} | ||
|
||
@Override | ||
public void destroyPlugin() { | ||
log.debug("Plugin destruction started"); | ||
httpBindManager.removeJettyHandler(webAppContext); | ||
log.debug("Plugin destruction complete"); | ||
} | ||
|
||
public PasswordResetMailer getPasswordResetMailer() { | ||
return passwordResetMailer; | ||
} | ||
|
||
public PasswordResetTokenManager getResetTokenManager() { | ||
return resetTokenManager; | ||
} | ||
} | ||
package org.jivesoftware.openfire.plugin.passwordreset; | ||
|
||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; | ||
import java.io.File; | ||
import java.time.Duration; | ||
import java.time.temporal.ChronoUnit; | ||
import java.util.Arrays; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.apache.tomcat.InstanceManager; | ||
import org.apache.tomcat.SimpleInstanceManager; | ||
import org.eclipse.jetty.webapp.WebAppContext; | ||
import org.jivesoftware.database.DbConnectionManager; | ||
import org.jivesoftware.openfire.XMPPServer; | ||
import org.jivesoftware.openfire.container.Plugin; | ||
import org.jivesoftware.openfire.container.PluginManager; | ||
import org.jivesoftware.openfire.http.HttpBindManager; | ||
import org.jivesoftware.openfire.user.UserManager; | ||
import org.jivesoftware.util.EmailService; | ||
import org.jivesoftware.util.LocaleUtils; | ||
import org.jivesoftware.util.SystemProperty; | ||
|
||
@Slf4j | ||
@SuppressFBWarnings({"EI_EXPOSE_REP", "MS_EXPOSE_REP"}) | ||
public class PasswordResetPlugin implements Plugin { | ||
|
||
public static final String PLUGIN_NAME = "Password Reset"; // Exact match to plugin.xml | ||
public static final SystemProperty<Boolean> ENABLED = | ||
SystemProperty.Builder.ofType(Boolean.class) | ||
.setKey("plugin.passwordreset.enabled") | ||
.setDefaultValue(Boolean.FALSE) | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<String> SERVER = | ||
SystemProperty.Builder.ofType(String.class) | ||
.setKey("plugin.passwordreset.server") | ||
.setDefaultValue("") | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<String> SENDER_NAME = | ||
SystemProperty.Builder.ofType(String.class) | ||
.setKey("plugin.passwordreset.sender-name") | ||
.setDefaultValue("Openfire") | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<String> SENDER_ADDRESS = | ||
SystemProperty.Builder.ofType(String.class) | ||
.setKey("plugin.passwordreset.sender-address") | ||
.setDefaultValue("admin@example.com") | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<String> SUBJECT = | ||
SystemProperty.Builder.ofType(String.class) | ||
.setKey("plugin.passwordreset.email-subject") | ||
.setDefaultValue("Openfire password reset") | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<String> BODY = | ||
SystemProperty.Builder.ofType(String.class) | ||
.setKey("plugin.passwordreset.email-body") | ||
.setDefaultValue("Dear ${userName}\n\n" | ||
+ "To reset the password for your ${userId} Openfire account, simply go to ${url}" | ||
+ " at any time in the next five hours. After this time, you will need to request" | ||
+ " another reset email is sent to you.") | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<Duration> EXPIRY = | ||
SystemProperty.Builder.ofType(Duration.class) | ||
.setChronoUnit(ChronoUnit.MINUTES) | ||
.setKey("plugin.passwordreset.reset-expiry") | ||
.setDefaultValue(Duration.ofHours(5)) | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<Integer> MIN_LENGTH = | ||
SystemProperty.Builder.ofType(Integer.class) | ||
.setKey("plugin.passwordreset.min-length") | ||
.setMinValue(1) | ||
.setDefaultValue(8) | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final SystemProperty<Integer> MAX_LENGTH = | ||
SystemProperty.Builder.ofType(Integer.class) | ||
.setKey("plugin.passwordreset.max-length") | ||
.setMinValue(0) | ||
.setDefaultValue(0) | ||
.setDynamic(true) | ||
.setPlugin(PLUGIN_NAME) | ||
.build(); | ||
public static final String CONTEXT_PATH = "/passwordreset"; | ||
private static String canonicalName; | ||
private static PasswordResetPlugin plugin; | ||
private final HttpBindManager httpBindManager; | ||
private final PasswordResetMailer passwordResetMailer; | ||
private final PasswordResetTokenManager resetTokenManager; | ||
private WebAppContext webAppContext; | ||
|
||
private static void setInstance(final PasswordResetPlugin plugin) { | ||
PasswordResetPlugin.plugin = plugin; | ||
} | ||
|
||
private static void setCanonicalName(final String canonicalName) { | ||
PasswordResetPlugin.canonicalName = canonicalName; | ||
} | ||
|
||
public static PasswordResetPlugin getInstance() { | ||
return plugin; | ||
} | ||
|
||
/** | ||
* Default constructor for the plugin. | ||
*/ | ||
public PasswordResetPlugin() { | ||
setInstance(this); | ||
this.httpBindManager = HttpBindManager.getInstance(); | ||
this.passwordResetMailer = new PasswordResetMailer(EmailService.getInstance()); | ||
this.resetTokenManager = new PasswordResetTokenManager( | ||
DbConnectionManager::getConnection, | ||
UserManager.getInstance() | ||
); | ||
setBlankServerDetails(); | ||
log.debug("Plugin created"); | ||
} | ||
|
||
public static String localize(final String key, final Object... arguments) { | ||
return LocaleUtils.getLocalizedString(key, canonicalName, Arrays.asList(arguments)); | ||
} | ||
|
||
private void setBlankServerDetails() { | ||
if (SERVER.getValue().isEmpty()) { | ||
// Set a default value for this as there isn't one already | ||
final String defaultUrl; | ||
if (httpBindManager.isHttpsBindActive()) { | ||
defaultUrl = String.format("https://%s:%d%s", | ||
XMPPServer.getInstance().getServerInfo().getHostname(), | ||
HttpBindManager.HTTP_BIND_SECURE_PORT.getValue(), | ||
CONTEXT_PATH | ||
); | ||
} else { | ||
defaultUrl = String.format("http://%s:%d%s", | ||
XMPPServer.getInstance().getServerInfo().getHostname(), | ||
HttpBindManager.HTTP_BIND_PORT.getValue(), | ||
CONTEXT_PATH | ||
); | ||
} | ||
SERVER.setValue(defaultUrl); | ||
} | ||
} | ||
|
||
@Override | ||
public void initializePlugin(final PluginManager manager, final File pluginDirectory) { | ||
log.debug("Plugin initialisation started"); | ||
|
||
setCanonicalName(manager.getCanonicalName(this)); | ||
|
||
webAppContext = new WebAppContext(pluginDirectory.getPath() + "/web-client", | ||
CONTEXT_PATH); | ||
webAppContext.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager()); | ||
httpBindManager.addJettyHandler(webAppContext); | ||
|
||
log.debug("Plugin initialisation complete"); | ||
} | ||
|
||
@Override | ||
public void destroyPlugin() { | ||
log.debug("Plugin destruction started"); | ||
httpBindManager.removeJettyHandler(webAppContext); | ||
log.debug("Plugin destruction complete"); | ||
} | ||
|
||
public PasswordResetMailer getPasswordResetMailer() { | ||
return passwordResetMailer; | ||
} | ||
|
||
public PasswordResetTokenManager getResetTokenManager() { | ||
return resetTokenManager; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
682 changes: 342 additions & 340 deletions
682
...ivesoftware/openfire/plugin/passwordreset/servlet/admin/PasswordResetSettingsServlet.java
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters