Skip to content

Commit

Permalink
ZCS-15588: Added SOAP API for ChangePassword for Admin namespace
Browse files Browse the repository at this point in the history
  • Loading branch information
zimsuchitgupta committed Aug 28, 2024
1 parent a680f3b commit 32da14c
Show file tree
Hide file tree
Showing 12 changed files with 362 additions and 34 deletions.
4 changes: 4 additions & 0 deletions common/src/java/com/zimbra/common/soap/AdminConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public final class AdminConstants {

public static final String E_AUTH_REQUEST = "AuthRequest";
public static final String E_AUTH_RESPONSE = "AuthResponse";
public static final String E_CHANGE_PASSWORD_REQUEST = "ChangePasswordRequest";
public static final String E_CHANGE_PASSWORD_RESPONSE = "ChangePasswordResponse";
public static final String E_CREATE_ACCOUNT_REQUEST = "CreateAccountRequest";
public static final String E_CREATE_ACCOUNT_RESPONSE = "CreateAccountResponse";
public static final String E_CREATE_GAL_SYNC_ACCOUNT_REQUEST = "CreateGalSyncAccountRequest";
Expand Down Expand Up @@ -599,6 +601,8 @@ public final class AdminConstants {
public static final QName SEARCH_ACCOUNTS_RESPONSE = QName.get(E_SEARCH_ACCOUNTS_RESPONSE, NAMESPACE);
public static final QName RENAME_ACCOUNT_REQUEST = QName.get(E_RENAME_ACCOUNT_REQUEST, NAMESPACE);
public static final QName RENAME_ACCOUNT_RESPONSE = QName.get(E_RENAME_ACCOUNT_RESPONSE, NAMESPACE);
public static final QName CHANGE_PASSWORD_REQUEST = QName.get(E_CHANGE_PASSWORD_REQUEST, NAMESPACE);
public static final QName CHANGE_PASSWORD_RESPONSE = QName.get(E_CHANGE_PASSWORD_RESPONSE, NAMESPACE);
public static final QName CHANGE_PRIMARY_EMAIL_REQUEST = QName.get(E_CHANGE_PRIMARY_EMAIL_REQUEST, NAMESPACE);
public static final QName CHANGE_PRIMARY_EMAIL_RESPONSE = QName.get(E_CHANGE_PRIMARY_EMAIL_RESPONSE, NAMESPACE);

Expand Down
2 changes: 2 additions & 0 deletions soap/src/java/com/zimbra/soap/JaxbUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ public final class JaxbUtil {
com.zimbra.soap.admin.message.CancelPendingAccountOnlyRemoteWipeResponse.class,
com.zimbra.soap.admin.message.CancelPendingRemoteWipeRequest.class,
com.zimbra.soap.admin.message.CancelPendingRemoteWipeResponse.class,
com.zimbra.soap.admin.message.ChangePasswordRequest.class,
com.zimbra.soap.admin.message.ChangePasswordResponse.class,
com.zimbra.soap.admin.message.CheckAuthConfigRequest.class,
com.zimbra.soap.admin.message.CheckAuthConfigResponse.class,
com.zimbra.soap.admin.message.CheckBlobConsistencyRequest.class,
Expand Down
113 changes: 113 additions & 0 deletions soap/src/java/com/zimbra/soap/admin/message/ChangePasswordRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Zimbra Collaboration Suite Server
* Copyright (C) 2024 Synacor, Inc.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software Foundation,
* version 2 of the License.
*
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
* ***** END LICENSE BLOCK *****
*/
package com.zimbra.soap.admin.message;

import com.zimbra.common.soap.AccountConstants;
import com.zimbra.common.soap.AdminConstants;
import com.zimbra.soap.account.type.AuthToken;
import com.zimbra.soap.type.AccountSelector;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(name= AdminConstants.E_CHANGE_PASSWORD_REQUEST)
@XmlType(propOrder = {})
public class ChangePasswordRequest {
/**
* @zm-api-field-description Details of the account
*/
@XmlElement(name=AccountConstants.E_ACCOUNT, required=true)
private AccountSelector account;
/**
* @zm-api-field-description Old password
*/
@XmlElement(name=AccountConstants.E_OLD_PASSWORD, required=true)
private String oldPassword;
/**
* @zm-api-field-description New Password to assign
*/
@XmlElement(name=AccountConstants.E_PASSWORD, required=true)
private String password;
/**
* @zm-api-field-tag virtual-host
* @zm-api-field-description if specified virtual-host is used to determine the domain of the account name,
* if it does not include a domain component. For example, if the domain foo.com has a zimbraVirtualHostname of
* "mail.foo.com", and an auth request comes in for "joe" with a virtualHost of "mail.foo.com", then the request
* will be equivalent to logging in with "joe@foo.com".
*/
@XmlElement(name=AccountConstants.E_VIRTUAL_HOST, required=false)
private String virtualHost;

@XmlElement(name=AccountConstants.E_DRYRUN, required=false)
private boolean dryRun;

@XmlElement(name=AccountConstants.E_AUTH_TOKEN /* authToken */, required=false)
private AuthToken authToken;

public ChangePasswordRequest() {
}

public ChangePasswordRequest(AccountSelector account, String oldPassword, String newPassword) {
setAccount(account);
setOldPassword(oldPassword);
setPassword(newPassword);
}

public ChangePasswordRequest(AccountSelector account, String oldPassword, String newPassword, boolean dryRun) {
setAccount(account);
setOldPassword(oldPassword);
setPassword(newPassword);
setDryRun(dryRun);
}

public AccountSelector getAccount() { return account; }
public String oldPassword() { return oldPassword; }
public String getPassword() { return password; }
public String getVirtualHost() { return virtualHost; }

public ChangePasswordRequest setAccount(AccountSelector account) {
this.account = account;
return this;
}

public ChangePasswordRequest setOldPassword(String oldPassword) {
this.oldPassword = oldPassword;
return this;
}

public ChangePasswordRequest setPassword(String password) {
this.password = password;
return this;
}

public ChangePasswordRequest setVirtualHost(String host) {
virtualHost = host;
return this;
}

public boolean isDryRun() {
return dryRun;
}

public void setDryRun(boolean dryRun) {
this.dryRun = dryRun;
}

public AuthToken getAuthToken() { return authToken; }
public ChangePasswordRequest setAuthToken(AuthToken authToken) { this.authToken = authToken; return this; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Zimbra Collaboration Suite Server
* Copyright (C) 2024 Synacor, Inc.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software Foundation,
* version 2 of the License.
*
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
* ***** END LICENSE BLOCK *****
*/
package com.zimbra.soap.admin.message;

import com.zimbra.common.gql.GqlConstants;
import com.zimbra.common.soap.AccountConstants;
import com.zimbra.common.soap.AdminConstants;
import com.zimbra.soap.json.jackson.annotate.ZimbraJsonAttribute;
import io.leangen.graphql.annotations.GraphQLQuery;
import io.leangen.graphql.annotations.types.GraphQLType;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(name= AdminConstants.E_CHANGE_PASSWORD_RESPONSE)
@GraphQLType(name= GqlConstants.CLASS_CHANGE_PASSWORD_RESPONSE, description="The response to change password request.")
@XmlType(propOrder = {})
public class ChangePasswordResponse {

/**
* @zm-api-field-tag new-auth-token
* @zm-api-field-description New authToken, as old authToken is invalidated on password change.
*/
@XmlElement(name=AccountConstants.E_AUTH_TOKEN /* authToken */, required=true)
private String authToken;
/**
* @zm-api-field-description Life time associated with <b>{new-auth-token}</b>
*/
@ZimbraJsonAttribute
@XmlElement(name=AccountConstants.E_LIFETIME /* lifetime */, required=true)
private long lifetime;

public ChangePasswordResponse() {
}

@GraphQLQuery(name=GqlConstants.AUTH_TOKEN, description="Auth token based on the new password")
public String getAuthToken() { return authToken; }
@GraphQLQuery(name=GqlConstants.LIFETIME, description="Life time of the auth token")
public long getLifetime() { return lifetime; }

public ChangePasswordResponse setAuthToken(String authToken) {
this.authToken = authToken;
return this;
}

public ChangePasswordResponse setLifetime(long lifetime) {
this.lifetime = lifetime;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,34 +50,7 @@ protected Element proxyIfNecessary(Element request, Map<String, Object> context)
throw e;
}
}

/*
* bug 27389
*/
protected boolean checkPasswordSecurity(Map<String, Object> context) throws ServiceException {
HttpServletRequest req = (HttpServletRequest)context.get(SoapServlet.SERVLET_REQUEST);
boolean isHttps = req.getScheme().equals("https");
if (isHttps)
return true;

// clear text
Server server = Provisioning.getInstance().getLocalServer();
String modeString = server.getAttr(Provisioning.A_zimbraMailMode, null);
if (modeString == null) {
// not likely, but just log and let it through
ZimbraLog.soap.warn("missing " + Provisioning.A_zimbraMailMode +
" for checking password security, allowing the request");
return true;
}

MailMode mailMode = Provisioning.MailMode.fromString(modeString);
if (mailMode == MailMode.mixed &&
!server.getBooleanAttr(Provisioning.A_zimbraMailClearTextPasswordEnabled, true))
return false;
else
return true;
}


protected Set<String> getReqAttrs(Element request, AttributeClass klass) throws ServiceException {
String attrsStr = request.getAttribute(AccountConstants.A_ATTRS, null);
if (attrsStr == null) {
Expand Down
2 changes: 1 addition & 1 deletion store/src/java/com/zimbra/cs/service/account/Auth.java
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ private Element needTwoFactorAuth(Map<String, Object> context, Element requestEl
private Element needResetPassword(Map<String, Object> context, Element requestElement, Account account, TwoFactorAuth auth,
ZimbraSoapContext zsc, TokenType tokenType) throws ServiceException {
Element response = zsc.createElement(AccountConstants.AUTH_RESPONSE);
AuthToken authToken = AuthProvider.getAuthToken(account, Usage.RESET_PASSWORD , tokenType);
AuthToken authToken = AuthProvider.getAuthToken(account, Usage.RESET_PASSWORD, tokenType);
response.addAttribute(AccountConstants.E_LIFETIME, authToken.getExpires() - System.currentTimeMillis(), Element.Disposition.CONTENT);
response.addUniqueElement(AccountConstants.E_RESET_PWD).setText("true");
authToken.encodeAuthResp(response, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,16 @@
import com.zimbra.cs.account.names.NameUtil;
import com.zimbra.cs.session.Session;
import com.zimbra.soap.DocumentHandler;
import com.zimbra.soap.SoapServlet;
import com.zimbra.soap.ZimbraSoapContext;
import com.zimbra.soap.admin.type.CosSelector;
import com.zimbra.soap.admin.type.CosSelector.CosBy;
import com.zimbra.soap.admin.type.DomainSelector;
import com.zimbra.soap.admin.type.ServerSelector;
import com.zimbra.soap.type.AccountSelector;

import javax.servlet.http.HttpServletRequest;

/**
* @since Oct 4, 2004
* @author schemers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public void registerHandlers(DocumentDispatcher dispatcher) {
dispatcher.registerHandler(AdminConstants.REMOVE_ACCOUNT_ALIAS_REQUEST, new RemoveAccountAlias());
dispatcher.registerHandler(AdminConstants.SEARCH_ACCOUNTS_REQUEST, new SearchAccounts());
dispatcher.registerHandler(AdminConstants.RENAME_ACCOUNT_REQUEST, new RenameAccount());
dispatcher.registerHandler(AdminConstants.CHANGE_PASSWORD_REQUEST, new ChangePassword());
dispatcher.registerHandler(AdminConstants.CHANGE_PRIMARY_EMAIL_REQUEST, new ChangePrimaryEmail());

dispatcher.registerHandler(AdminConstants.RESET_ACCOUNT_PASSWORD_REQUEST, new ResetAccountPassword());
Expand Down
16 changes: 12 additions & 4 deletions store/src/java/com/zimbra/cs/service/admin/Auth.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,17 @@ public Element handle(Element request, Map<String, Object> context) throws Servi

Map<String, Object> authCtxt = AccountUtil.getAdminAuthContext(context, acctName, zsc);
context.put(Provisioning.AUTH_MODE_KEY, AuthMode.PASSWORD);

prov.authAccount(acct, password, AuthContext.Protocol.soap, authCtxt);
Usage usage = Usage.AUTH;
try {
prov.authAccount(acct, password, AuthContext.Protocol.soap, authCtxt);
} catch (AccountServiceException ase) {
if (AccountServiceException.CHANGE_PASSWORD.equals(ase.getCode())) {
ZimbraLog.account.info("zimbraPasswordMustChange is enabled so creating a auth-token used to change password.");
usage = Usage.RESET_PASSWORD;
}
}

AuthMech authedByMech = (AuthMech) authCtxt.get(AuthContext.AC_AUTHED_BY_MECH);
Usage usage = Usage.AUTH;
TokenType tokenType = null;

if (AccountUtil.isTwoFactorAccount(acct)) {
Expand Down Expand Up @@ -157,7 +163,9 @@ private Element doResponse(Element request, AuthToken at, ZimbraSoapContext zsc,
Element response = zsc.createElement(AdminConstants.AUTH_RESPONSE);
response.addAttribute(AdminConstants.E_LIFETIME, at.getExpires() - System.currentTimeMillis(), Element.Disposition.CONTENT);
at.setCsrfTokenEnabled(csrfSupport);
if (at.getUsage() == Usage.TWO_FACTOR_AUTH) {
if (at.getUsage() == Usage.RESET_PASSWORD) {
response.addUniqueElement(AccountConstants.E_RESET_PWD).setText("true");
} else if (at.getUsage() == Usage.TWO_FACTOR_AUTH) {
response.addUniqueElement(AccountConstants.E_TWO_FACTOR_AUTH_REQUIRED).setText("true");
AccountUtil.addTwoFactorAttributes(response, acct);
} else {
Expand Down
Loading

0 comments on commit 32da14c

Please sign in to comment.