
org.restcomm.connect.http.AccountsEndpoint Maven / Gradle / Ivy
/*
* TeleStax, Open Source Cloud Communications
* Copyright 2011-2014, Telestax Inc and individual contributors
* by the @authors tag.
*
* This program is free software: you can redistribute it and/or modify
* under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see
*
*/
package org.restcomm.connect.http;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.sun.jersey.core.util.MultivaluedMapImpl;
import com.thoughtworks.xstream.XStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.MediaType;
import static javax.ws.rs.core.MediaType.*;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import static javax.ws.rs.core.Response.*;
import static javax.ws.rs.core.Response.Status.*;
import org.apache.commons.configuration.Configuration;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.joda.time.DateTime;
import org.restcomm.connect.commons.configuration.RestcommConfiguration;
import org.restcomm.connect.commons.configuration.sets.RcmlserverConfigurationSet;
import org.restcomm.connect.dao.ClientsDao;
import org.restcomm.connect.dao.DaoManager;
import org.restcomm.connect.dao.IncomingPhoneNumbersDao;
import org.restcomm.connect.dao.entities.Account;
import org.restcomm.connect.dao.entities.AccountList;
import org.restcomm.connect.dao.entities.Client;
import org.restcomm.connect.dao.entities.IncomingPhoneNumber;
import org.restcomm.connect.dao.entities.RestCommResponse;
import org.restcomm.connect.commons.dao.Sid;
import org.restcomm.connect.http.client.rcmlserver.RcmlserverNotifications;
import org.restcomm.connect.http.converter.AccountConverter;
import org.restcomm.connect.http.converter.AccountListConverter;
import org.restcomm.connect.http.converter.RestCommResponseConverter;
import org.restcomm.connect.http.exceptions.AuthorizationException;
import org.restcomm.connect.http.exceptions.InsufficientPermission;
import org.restcomm.connect.http.exceptions.AccountAlreadyClosed;
import org.restcomm.connect.commons.util.StringUtils;
import org.restcomm.connect.http.exceptions.PasswordTooWeak;
import org.restcomm.connect.identity.passwords.PasswordValidator;
import org.restcomm.connect.identity.passwords.PasswordValidatorFactory;
import org.restcomm.connect.http.client.rcmlserver.RcmlserverApi;
import org.restcomm.connect.http.exceptions.RcmlserverNotifyError;
import org.restcomm.connect.provisioning.number.api.PhoneNumberProvisioningManager;
import org.restcomm.connect.provisioning.number.api.PhoneNumberProvisioningManagerProvider;
/**
* @author [email protected] (Thomas Quintana)
*/
public class AccountsEndpoint extends SecuredEndpoint {
protected Configuration runtimeConfiguration;
protected Configuration rootConfiguration; // top-level configuration element
protected Gson gson;
protected XStream xstream;
protected ClientsDao clientDao;
public AccountsEndpoint() {
super();
}
// used for testing
public AccountsEndpoint(ServletContext context, HttpServletRequest request) {
super(context,request);
}
@PostConstruct
void init() {
rootConfiguration = (Configuration) context.getAttribute(Configuration.class.getName());
runtimeConfiguration = rootConfiguration.subset("runtime-settings");
super.init(runtimeConfiguration);
clientDao = ((DaoManager) context.getAttribute(DaoManager.class.getName())).getClientsDao();
final AccountConverter converter = new AccountConverter(runtimeConfiguration);
final GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Account.class, converter);
builder.setPrettyPrinting();
gson = builder.create();
xstream = new XStream();
xstream.alias("RestcommResponse", RestCommResponse.class);
xstream.registerConverter(converter);
xstream.registerConverter(new AccountListConverter(runtimeConfiguration));
xstream.registerConverter(new RestCommResponseConverter(runtimeConfiguration));
// Make sure there is an authenticated account present when this endpoint is used
checkAuthenticatedAccount();
}
private Account createFrom(final Sid accountSid, final MultivaluedMap data) throws PasswordTooWeak {
validate(data);
final DateTime now = DateTime.now();
final String emailAddress = (data.getFirst("EmailAddress")).toLowerCase();
// Issue 108: https://bitbucket.org/telestax/telscale-restcomm/issue/108/account-sid-could-be-a-hash-of-the
final Sid sid = Sid.generate(Sid.Type.ACCOUNT, emailAddress);
String friendlyName = emailAddress;
if (data.containsKey("FriendlyName")) {
friendlyName = data.getFirst("FriendlyName");
}
final Account.Type type = Account.Type.FULL;
Account.Status status = Account.Status.ACTIVE;
if (data.containsKey("Status")) {
status = Account.Status.getValueOf(data.getFirst("Status").toLowerCase());
}
final String password = data.getFirst("Password");
PasswordValidator validator = PasswordValidatorFactory.createDefault();
if (!validator.isStrongEnough(password))
throw new PasswordTooWeak();
final String authToken = new Md5Hash(password).toString();
final String role = data.getFirst("Role");
String rootUri = runtimeConfiguration.getString("root-uri");
rootUri = StringUtils.addSuffixIfNotPresent(rootUri, "/");
final StringBuilder buffer = new StringBuilder();
buffer.append(rootUri).append(getApiVersion(null)).append("/Accounts/").append(sid.toString());
final URI uri = URI.create(buffer.toString());
return new Account(sid, now, now, emailAddress, friendlyName, accountSid, type, status, authToken, role, uri);
}
protected Response getAccount(final String accountSid, final MediaType responseType) {
//First check if the account has the required permissions in general, this way we can fail fast and avoid expensive DAO operations
Account account = null;
checkPermission("RestComm:Read:Accounts");
if (Sid.pattern.matcher(accountSid).matches()) {
try {
account = accountsDao.getAccount(new Sid(accountSid));
} catch (Exception e) {
return status(NOT_FOUND).build();
}
} else {
try {
account = accountsDao.getAccount(accountSid);
} catch (Exception e) {
return status(NOT_FOUND).build();
}
}
secure(account, "RestComm:Read:Accounts", SecuredType.SECURED_ACCOUNT );
if (account == null) {
return status(NOT_FOUND).build();
} else {
if (APPLICATION_XML_TYPE == responseType) {
final RestCommResponse response = new RestCommResponse(account);
return ok(xstream.toXML(response), APPLICATION_XML).build();
} else if (APPLICATION_JSON_TYPE == responseType) {
return ok(gson.toJson(account), APPLICATION_JSON).build();
} else {
return null;
}
}
}
// Account removal disabled as per https://github.com/RestComm/Restcomm-Connect/issues/1270
/*
protected Response deleteAccount(final String operatedSid) {
//First check if the account has the required permissions in general, this way we can fail fast and avoid expensive DAO operations
checkPermission("RestComm:Delete:Accounts");
// what if effectiveAccount is null ?? - no need to check since we checkAuthenticatedAccount() in AccountsEndoint.init()
final Sid accountSid = userIdentityContext.getEffectiveAccount().getSid();
final Sid sidToBeRemoved = new Sid(operatedSid);
Account removedAccount = accountsDao.getAccount(sidToBeRemoved);
secure(removedAccount, "RestComm:Delete:Accounts", SecuredType.SECURED_ACCOUNT);
// Prevent removal of Administrator account
if (operatedSid.equalsIgnoreCase(accountSid.toString()))
return status(BAD_REQUEST).build();
if (accountsDao.getAccount(sidToBeRemoved) == null)
return status(NOT_FOUND).build();
// the whole tree of sub-accounts has to be removed as well
List removedAccounts = accountsDao.getSubAccountSidsRecursive(sidToBeRemoved);
if (removedAccounts != null && !removedAccounts.isEmpty()) {
int i = removedAccounts.size(); // is is the count of accounts left to process
while (i > 0) {
i --;
String removedSid = removedAccounts.get(i);
try {
removeSingleAccount(removedSid);
} catch (Exception e) {
// if anything bad happens, log the error and continue removing the rest of the accounts.
logger.error("Failed removing (child) account '" + removedSid + "'");
}
}
}
// remove the parent account too
removeSingleAccount(operatedSid);
return ok().build();
}*/
/*
protected Response deleteAccount(final String operatedSid) {
//First check if the account has the required permissions in general, this way we can fail fast and avoid expensive DAO operations
checkPermission("RestComm:Delete:Accounts");
// what if effectiveAccount is null ?? - no need to check since we checkAuthenticatedAccount() in AccountsEndoint.init()
final Sid accountSid = userIdentityContext.getEffectiveAccount().getSid();
final Sid sidToBeRemoved = new Sid(operatedSid);
Account removedAccount = accountsDao.getAccount(sidToBeRemoved);
secure(removedAccount, "RestComm:Delete:Accounts", SecuredType.SECURED_ACCOUNT);
// Prevent removal of Administrator account
if (operatedSid.equalsIgnoreCase(accountSid.toString()))
return status(BAD_REQUEST).build();
if (accountsDao.getAccount(sidToBeRemoved) == null)
return status(NOT_FOUND).build();
accountsDao.removeAccount(sidToBeRemoved);
// Remove its SIP client account
clientDao.removeClients(sidToBeRemoved);
return ok().build();
}
*/
/**
* Removes all dependent resources of an account. Some resources like
* CDRs are excluded.
*
* @param sid
*/
private void removeAccoundDependencies(Sid sid) {
DaoManager daoManager = (DaoManager) context.getAttribute(DaoManager.class.getName());
// remove dependency entities first and dependent entities last. Also, do safer operation first (as a secondary rule)
daoManager.getAnnouncementsDao().removeAnnouncements(sid);
daoManager.getNotificationsDao().removeNotifications(sid);
daoManager.getShortCodesDao().removeShortCodes(sid);
daoManager.getOutgoingCallerIdsDao().removeOutgoingCallerIds(sid);
daoManager.getTranscriptionsDao().removeTranscriptions(sid);
daoManager.getRecordingsDao().removeRecordings(sid);
daoManager.getApplicationsDao().removeApplications(sid);
removeIncomingPhoneNumbers(sid,daoManager.getIncomingPhoneNumbersDao());
daoManager.getClientsDao().removeClients(sid);
}
/**
* Removes incoming phone numbers that belong to an account from the database.
* For provided numbers the provider is also contacted to get them released.
*
* @param accountSid
* @param dao
*/
private void removeIncomingPhoneNumbers(Sid accountSid, IncomingPhoneNumbersDao dao) {
List numbers = dao.getIncomingPhoneNumbers(accountSid);
if (numbers != null && numbers.size() > 0) {
// manager is retrieved in a lazy way. If any number needs it, it will be first retrieved
// from the servlet context. If not there it will be created, stored in context and returned.
boolean managerQueried = false;
PhoneNumberProvisioningManager manager = null;
for (IncomingPhoneNumber number : numbers) {
// if this is not just a SIP number try to release it by contacting the provider
if (number.isPureSip() == null || !number.isPureSip()) {
if ( ! managerQueried )
manager = new PhoneNumberProvisioningManagerProvider(rootConfiguration,context).get(); // try to retrieve/build manager only once
if (manager != null) {
try {
if (! manager.cancelNumber(IncomingPhoneNumbersEndpoint.convertIncomingPhoneNumbertoPhoneNumber(number)) ) {
logger.error("Number cancelation failed for provided number '" + number.getPhoneNumber()+"'. Number entity " + number.getSid() + " will stay in database");
} else {
dao.removeIncomingPhoneNumber(number.getSid());
}
} catch (Exception e) {
logger.error("Number cancelation failed for provided number '" + number.getPhoneNumber()+"'",e);
}
}
else
logger.error("Number cancelation failed for provided number '" + number.getPhoneNumber()+"'. Provisioning Manager was null. "+"Number entity " + number.getSid() + " will stay in database");
} else {
// pureSIP numbers only to be removed from database. No need to contact provider
dao.removeIncomingPhoneNumber(number.getSid());
}
}
}
}
protected Response getAccounts(final MediaType responseType) {
//First check if the account has the required permissions in general, this way we can fail fast and avoid expensive DAO operations
checkPermission("RestComm:Read:Accounts");
final Account account = userIdentityContext.getEffectiveAccount();
if (account == null) {
return status(NOT_FOUND).build();
} else {
final List accounts = new ArrayList();
// accounts.add(account);
accounts.addAll(accountsDao.getChildAccounts(account.getSid()));
if (APPLICATION_XML_TYPE == responseType) {
final RestCommResponse response = new RestCommResponse(new AccountList(accounts));
return ok(xstream.toXML(response), APPLICATION_XML).build();
} else if (APPLICATION_JSON_TYPE == responseType) {
return ok(gson.toJson(accounts), APPLICATION_JSON).build();
} else {
return null;
}
}
}
protected Response putAccount(final MultivaluedMap data, final MediaType responseType) {
//First check if the account has the required permissions in general, this way we can fail fast and avoid expensive DAO operations
checkPermission("RestComm:Create:Accounts");
// what if effectiveAccount is null ?? - no need to check since we checkAuthenticatedAccount() in AccountsEndoint.init()
final Sid sid = userIdentityContext.getEffectiveAccount().getSid();
Account account = null;
try {
account = createFrom(sid, data);
} catch (final NullPointerException exception) {
return status(BAD_REQUEST).entity(exception.getMessage()).build();
} catch (PasswordTooWeak passwordTooWeak) {
return status(BAD_REQUEST).entity(buildErrorResponseBody("Password too weak",responseType)).type(responseType).build();
}
// If Account already exists don't add it again
/*
Account creation rules:
- either be Administrator or have the following permission: RestComm:Create:Accounts
- only Administrators can choose a role for newly created accounts. Normal users will create accounts with the same role as their own.
*/
if (accountsDao.getAccount(account.getSid()) == null && !account.getEmailAddress().equalsIgnoreCase("[email protected]")) {
final Account parent = accountsDao.getAccount(sid);
if (parent.getStatus().equals(Account.Status.ACTIVE) && isSecuredByPermission("RestComm:Create:Accounts")) {
if (!hasAccountRole(getAdministratorRole()) || !data.containsKey("Role")) {
account = account.setRole(parent.getRole());
}
accountsDao.addAccount(account);
// Create default SIP client data
MultivaluedMap clientData = new MultivaluedMapImpl();
String username = data.getFirst("EmailAddress").split("@")[0];
clientData.add("Login", username);
clientData.add("Password", data.getFirst("Password"));
clientData.add("FriendlyName", account.getFriendlyName());
clientData.add("AccountSid", account.getSid().toString());
Client client = clientDao.getClient(clientData.getFirst("Login"));
if (client == null) {
client = createClientFrom(account.getSid(), clientData);
clientDao.addClient(client);
}
} else {
throw new InsufficientPermission();
}
} else {
return status(CONFLICT).entity("The email address used for the new account is already in use.").build();
}
if (APPLICATION_JSON_TYPE == responseType) {
return ok(gson.toJson(account), APPLICATION_JSON).build();
} else if (APPLICATION_XML_TYPE == responseType) {
final RestCommResponse response = new RestCommResponse(account);
return ok(xstream.toXML(response), APPLICATION_XML).build();
} else {
return null;
}
}
private Client createClientFrom(final Sid accountSid, final MultivaluedMap data) {
final Client.Builder builder = Client.builder();
final Sid sid = Sid.generate(Sid.Type.CLIENT);
// TODO: need to encrypt this password because it's same with Account
// password.
// Don't implement now. Opened another issue for it.
// String password = new Md5Hash(data.getFirst("Password")).toString();
String password = data.getFirst("Password");
builder.setSid(sid);
builder.setAccountSid(accountSid);
builder.setApiVersion(getApiVersion(data));
builder.setLogin(data.getFirst("Login"));
builder.setPassword(password);
builder.setFriendlyName(data.getFirst("FriendlyName"));
builder.setStatus(Client.ENABLED);
String rootUri = runtimeConfiguration.getString("root-uri");
rootUri = StringUtils.addSuffixIfNotPresent(rootUri, "/");
final StringBuilder buffer = new StringBuilder();
buffer.append(rootUri).append(getApiVersion(data)).append("/Accounts/").append(accountSid.toString())
.append("/Clients/").append(sid.toString());
builder.setUri(URI.create(buffer.toString()));
return builder.build();
}
/**
* Fills an account entity object with values supplied from an http request
*
* @param account
* @param data
* @return
* @throws AccountAlreadyClosed
*/
private Account prepareAccountForUpdate(final Account account, final MultivaluedMap data) throws AccountAlreadyClosed, PasswordTooWeak {
Account result = account;
boolean isPasswordReset = false;
Account.Status newStatus = null;
try {
// if the account is already CLOSED, no updates are allowed
if (account.getStatus() == Account.Status.CLOSED) {
throw new AccountAlreadyClosed();
}
if (data.containsKey("Status")) {
newStatus = Account.Status.getValueOf(data.getFirst("Status").toLowerCase());
if (newStatus == Account.Status.CLOSED)
return account.setStatus(Account.Status.CLOSED);
// if the status is switched to CLOSED, the rest of the updates are ignored.
}
if (data.containsKey("FriendlyName")) {
result = result.setFriendlyName(data.getFirst("FriendlyName"));
}
if (data.containsKey("Password")) {
// if this is a reset-password operation, we also need to set the account status to active
if (account.getStatus() == Account.Status.UNINITIALIZED)
isPasswordReset = true;
String password = data.getFirst("Password");
PasswordValidator validator = PasswordValidatorFactory.createDefault();
if (!validator.isStrongEnough(password))
throw new PasswordTooWeak();
final String hash = new Md5Hash(data.getFirst("Password")).toString();
result = result.setAuthToken(hash);
}
if (data.containsKey("Auth_Token")) {
// TODO cannot validate AuthToken strength since it's already hashed
result = result.setAuthToken(data.getFirst("Auth_Token"));
// if this is a reset-password operation, we also need to set the account status to active
if (account.getStatus() == Account.Status.UNINITIALIZED)
isPasswordReset = true;
}
if (newStatus != null) {
result = result.setStatus(newStatus);
} else {
// if this is a password reset operation we need to activate the account (in case there is no explicity Status passed of course)
if (isPasswordReset)
result = result.setStatus(Account.Status.ACTIVE);
}
if (data.containsKey("Role")) {
Account operatingAccount = userIdentityContext.getEffectiveAccount();
// Only allow role change for administrators. Multitenancy checks will take care of restricting the modification scope to sub-accounts.
if (userIdentityContext.getEffectiveAccountRoles().contains(getAdministratorRole())) {
result = result.setRole(data.getFirst("Role"));
} else
throw new AuthorizationException();
}
} catch (AuthorizationException | AccountAlreadyClosed | PasswordTooWeak e) {
// some exceptions should reach outer layers and result in 403
throw e;
} catch (Exception e) {
if (logger.isInfoEnabled()) {
logger.info("Exception during Account update: "+e.getStackTrace());
}
}
return result;
}
protected Response updateAccount(final String identifier, final MultivaluedMap data,
final MediaType responseType) {
// First check if the account has the required permissions in general, this way we can fail fast and avoid expensive DAO
// operations
checkPermission("RestComm:Modify:Accounts");
Sid sid = null;
Account account = null;
try {
sid = new Sid(identifier);
account = accountsDao.getAccount(sid);
} catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("At update account, exception trying to get SID. Seems we have email as identifier"); // TODO check when this exception is thrown
}
}
if (account == null) {
if (logger.isDebugEnabled()) {
logger.debug("At update account, trying to get account using email as identifier");
}
account = accountsDao.getAccount(identifier);
}
if (account == null) {
return status(NOT_FOUND).build();
} else {
// If the account is CLOSED, no updates are allowed. Return a BAD_REQUEST status code.
Account modifiedAccount;
try {
modifiedAccount = prepareAccountForUpdate(account, data);
} catch (AccountAlreadyClosed accountAlreadyClosed) {
return status(BAD_REQUEST).build();
} catch (PasswordTooWeak passwordTooWeak) {
return status(BAD_REQUEST).entity(buildErrorResponseBody("Password too weak",responseType)).type(responseType).build();
}
secure(modifiedAccount, "RestComm:Modify:Accounts", SecuredType.SECURED_ACCOUNT);
// are we closing the account ?
if (account.getStatus() != Account.Status.CLOSED && modifiedAccount.getStatus() == Account.Status.CLOSED) {
closeAccountTree(modifiedAccount);
accountsDao.updateAccount(modifiedAccount);
} else {
// if we're not closing the account, update SIP client of the corresponding Account.
// Password and FriendlyName fields are synched.
String email = modifiedAccount.getEmailAddress();
if (email != null && !email.equals("")) {
String username = email.split("@")[0];
Client client = clientDao.getClient(username);
if (client != null) {
// TODO: need to encrypt this password because it's
// same with Account password.
// Don't implement now. Opened another issue for it.
if (data.containsKey("Password")) {
// Md5Hash(data.getFirst("Password")).toString();
String password = data.getFirst("Password");
client = client.setPassword(password);
}
if (data.containsKey("FriendlyName")) {
client = client.setFriendlyName(data.getFirst("FriendlyName"));
}
clientDao.updateClient(client);
}
}
accountsDao.updateAccount(modifiedAccount);
}
if (APPLICATION_JSON_TYPE == responseType) {
return ok(gson.toJson(modifiedAccount), APPLICATION_JSON).build();
} else if (APPLICATION_XML_TYPE == responseType) {
final RestCommResponse response = new RestCommResponse(modifiedAccount);
return ok(xstream.toXML(response), APPLICATION_XML).build();
} else {
return null;
}
}
}
/**
* Removes all resources belonging to an account and sets its status to CLOSED. If skipStatusUpdate is true, it will only
* remove resources and not update the status to CLOSED.
*
* @param closedAccount
*/
private void closeSingleAccount(Account closedAccount, boolean skipStatusUpdate) {
removeAccoundDependencies(closedAccount.getSid());
// finally, set account status to closed.
if (!skipStatusUpdate) {
closedAccount = closedAccount.setStatus(Account.Status.CLOSED);
accountsDao.updateAccount(closedAccount);
}
}
/**
* Closes an account along with all its children (the whole tree). Dependent entities are removed and,
* if configured, notification are sent to the rcml server (RVD) as well.
*
* @param parentAccount
*/
private void closeAccountTree(Account parentAccount) {
// close child accounts
List subAccountsToClose = accountsDao.getSubAccountSidsRecursive(parentAccount.getSid());
List closedSubAccounts = new ArrayList(); // the accounts that were really closed. Maybe these are not the same as those that were supposed to get closed
if (subAccountsToClose != null && !subAccountsToClose.isEmpty()) {
int i = subAccountsToClose.size(); // is is the count of accounts left to process
// we iterate backwards to handle child accounts first, parent accounts next
while (i > 0) {
i --;
String removedSid = subAccountsToClose.get(i);
try {
Account subAccount = accountsDao.getAccount(new Sid(removedSid));
closeSingleAccount(subAccount,false);
closedSubAccounts.add(subAccount);
} catch (Exception e) {
// if anything bad happens, log the error and continue removing the rest of the accounts.
logger.error("Failed removing (child) account '" + removedSid + "'");
}
}
}
// close parent account too. Skip status update. We need to send notifications first if needed.
closeSingleAccount(parentAccount,true);
closedSubAccounts.add(parentAccount);
// do we need to also notify the application sever (RVD) ?
RestcommConfiguration rcommConfiguration = RestcommConfiguration.getInstance();
RcmlserverConfigurationSet config = rcommConfiguration.getRcmlserver();
if (config != null && config.getNotify()) {
RcmlserverApi rcmlserverApi = new RcmlserverApi(rcommConfiguration.getMain(), rcommConfiguration.getRcmlserver());
// build a list of notifications out of the accounts that we really need to update
RcmlserverNotifications notifications = new RcmlserverNotifications();
for (Account account : closedSubAccounts) {
notifications.add(rcmlserverApi.buildAccountClosingNotification(account));
}
Account loggedAccount = userIdentityContext.getEffectiveAccount();
try {
rcmlserverApi.transmitNotifications(notifications, loggedAccount.getSid().toString(), loggedAccount.getAuthToken() );
} catch (RcmlserverNotifyError e) {
logger.error(e.getMessage(),e); // just report
}
}
// now that the notifications are sent we can proceed and set parent account to CLOSED
// note, we have assumed that while 'parent account' is open, the same applies to the loggedAccount
// on whose behalf the notifications are sent.
parentAccount = parentAccount.setStatus(Account.Status.CLOSED);
accountsDao.updateAccount(parentAccount);
}
private void validate(final MultivaluedMap data) throws NullPointerException {
if (!data.containsKey("EmailAddress")) {
throw new NullPointerException("Email address can not be null.");
} else if (!data.containsKey("Password")) {
throw new NullPointerException("Password can not be null.");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy