Search in sources :

Example 1 with AccountBy

use of com.zimbra.common.account.Key.AccountBy in project zm-mailbox by Zimbra.

the class LdapProvisioning method flushCache.

@Override
public void flushCache(CacheEntryType type, CacheEntry[] entries) throws ServiceException {
    switch(type) {
        case all:
            if (entries != null) {
                throw ServiceException.INVALID_REQUEST("cannot specify entry for flushing all", null);
            }
            ZimbraLog.account.info("Flushing all LDAP entry caches");
            flushCache(CacheEntryType.account, null);
            flushCache(CacheEntryType.group, null);
            flushCache(CacheEntryType.config, null);
            flushCache(CacheEntryType.globalgrant, null);
            flushCache(CacheEntryType.cos, null);
            flushCache(CacheEntryType.domain, null);
            flushCache(CacheEntryType.mime, null);
            flushCache(CacheEntryType.server, null);
            flushCache(CacheEntryType.alwaysOnCluster, null);
            flushCache(CacheEntryType.zimlet, null);
            break;
        case account:
            if (entries != null) {
                for (CacheEntry entry : entries) {
                    AccountBy accountBy = (entry.mEntryBy == Key.CacheEntryBy.id) ? AccountBy.id : AccountBy.name;
                    Account account = getFromCache(accountBy, entry.mEntryIdentity);
                    /*
                     * We now call removeFromCache instead of reload for flushing an account
                     * from cache.   This change was originally for bug 25028, but that would still
                     * need an extrat step to flush cache after the account's zimbraCOSId changed.
                     * (if we call reload insteasd of removeFromCache, flush cache of the account would
                     * not clear the mDefaults for inherited attrs, that was the bug.)
                     * Bug 25028 is now taken care of by the callback.  We still call removeFromCache
                     * for flushing account cache, because that does a cleaner flush.
                     *
                     * Note, we only call removeFromCache for account.
                     * We should *NOT* do removeFromCache when flushing global config and cos caches.
                     *
                     * Because the "mDefaults" Map(contains a ref to the old instance of COS.mAccountDefaults for
                     * all the accountInherited COS attrs) stored in all the cached accounts.   Same for the
                     * inherited attrs of server/domain from global config.  If we do removeFromCache for flushing
                     * cos/global config, then after FlushCache(cos) if you do a ga on a cached account, it still
                     * shows the old COS value for values that are inherited from COS.   Although, for newly loaded
                     * accounts or when a cached account is going thru auth(that'll trigger a reload) they will get
                     * the new COS values(refreshed as a result of FlushCache(cos)).
                     */
                    if (account != null) {
                        removeFromCache(account);
                    }
                }
            } else {
                accountCache.clear();
            }
            return;
        case group:
            if (entries != null) {
                for (CacheEntry entry : entries) {
                    Key.DistributionListBy dlBy = (entry.mEntryBy == Key.CacheEntryBy.id) ? Key.DistributionListBy.id : Key.DistributionListBy.name;
                    removeGroupFromCache(dlBy, entry.mEntryIdentity);
                }
            } else {
                allDLs.clear();
                groupCache.clear();
            }
            return;
        case config:
            if (entries != null) {
                throw ServiceException.INVALID_REQUEST("cannot specify entry for flushing global config", null);
            }
            Config config = getConfig();
            reload(config, false);
            EphemeralStore.clearFactory();
            return;
        case globalgrant:
            if (entries != null) {
                throw ServiceException.INVALID_REQUEST("cannot specify entry for flushing global grant", null);
            }
            GlobalGrant globalGrant = getGlobalGrant();
            reload(globalGrant, false);
            return;
        case cos:
            if (entries != null) {
                for (CacheEntry entry : entries) {
                    Key.CosBy cosBy = (entry.mEntryBy == Key.CacheEntryBy.id) ? Key.CosBy.id : Key.CosBy.name;
                    Cos cos = getFromCache(cosBy, entry.mEntryIdentity);
                    if (cos != null)
                        reload(cos, false);
                }
            } else
                cosCache.clear();
            return;
        case domain:
            if (entries != null) {
                for (CacheEntry entry : entries) {
                    Key.DomainBy domainBy = (entry.mEntryBy == Key.CacheEntryBy.id) ? Key.DomainBy.id : Key.DomainBy.name;
                    Domain domain = getFromCache(domainBy, entry.mEntryIdentity, GetFromDomainCacheOption.BOTH);
                    if (domain != null) {
                        if (domain instanceof DomainCache.NonExistingDomain)
                            domainCache.removeFromNegativeCache(domainBy, entry.mEntryIdentity);
                        else
                            reload(domain, false);
                    }
                }
            } else
                domainCache.clear();
            return;
        case mime:
            mimeTypeCache.flushCache(this);
            return;
        case server:
            if (entries != null) {
                for (CacheEntry entry : entries) {
                    Key.ServerBy serverBy = (entry.mEntryBy == Key.CacheEntryBy.id) ? Key.ServerBy.id : Key.ServerBy.name;
                    Server server = get(serverBy, entry.mEntryIdentity);
                    if (server != null)
                        reload(server, false);
                }
            } else
                serverCache.clear();
            return;
        case alwaysOnCluster:
            if (entries != null) {
                for (CacheEntry entry : entries) {
                    Key.AlwaysOnClusterBy clusterBy = Key.AlwaysOnClusterBy.id;
                    AlwaysOnCluster cluster = get(clusterBy, entry.mEntryIdentity);
                    if (cluster != null)
                        reload(cluster, false);
                }
            } else
                alwaysOnClusterCache.clear();
            return;
        case zimlet:
            if (entries != null) {
                for (CacheEntry entry : entries) {
                    Key.ZimletBy zimletBy = (entry.mEntryBy == Key.CacheEntryBy.id) ? Key.ZimletBy.id : Key.ZimletBy.name;
                    Zimlet zimlet = getFromCache(zimletBy, entry.mEntryIdentity);
                    if (zimlet != null)
                        reload(zimlet, false);
                }
            } else
                zimletCache.clear();
            return;
        default:
            throw ServiceException.INVALID_REQUEST("invalid cache type " + type, null);
    }
}
Also used : Account(com.zimbra.cs.account.Account) GuestAccount(com.zimbra.cs.account.GuestAccount) LdapAccount(com.zimbra.cs.account.ldap.entry.LdapAccount) InMemoryLdapServer(com.zimbra.cs.ldap.unboundid.InMemoryLdapServer) LdapServer(com.zimbra.cs.account.ldap.entry.LdapServer) Server(com.zimbra.cs.account.Server) LdapAlwaysOnCluster(com.zimbra.cs.account.ldap.entry.LdapAlwaysOnCluster) AlwaysOnCluster(com.zimbra.cs.account.AlwaysOnCluster) Config(com.zimbra.cs.account.Config) LdapConfig(com.zimbra.cs.account.ldap.entry.LdapConfig) ExternalLdapConfig(com.zimbra.cs.ldap.LdapServerConfig.ExternalLdapConfig) GalSearchConfig(com.zimbra.cs.gal.GalSearchConfig) LdapCos(com.zimbra.cs.account.ldap.entry.LdapCos) Cos(com.zimbra.cs.account.Cos) DistributionListBy(com.zimbra.common.account.Key.DistributionListBy) AccountBy(com.zimbra.common.account.Key.AccountBy) Zimlet(com.zimbra.cs.account.Zimlet) LdapZimlet(com.zimbra.cs.account.ldap.entry.LdapZimlet) LdapGlobalGrant(com.zimbra.cs.account.ldap.entry.LdapGlobalGrant) GlobalGrant(com.zimbra.cs.account.GlobalGrant) LdapDomain(com.zimbra.cs.account.ldap.entry.LdapDomain) Domain(com.zimbra.cs.account.Domain) EntryCacheDataKey(com.zimbra.cs.account.EntryCacheDataKey) Key(com.zimbra.common.account.Key) EphemeralKey(com.zimbra.cs.ephemeral.EphemeralKey) DataKey(com.zimbra.cs.account.callback.CallbackContext.DataKey) PreAuthKey(com.zimbra.cs.account.PreAuthKey)

Example 2 with AccountBy

use of com.zimbra.common.account.Key.AccountBy in project zm-mailbox by Zimbra.

the class Auth method handle.

@Override
public Element handle(Element request, Map<String, Object> context) throws ServiceException {
    ZimbraSoapContext zsc = getZimbraSoapContext(context);
    Provisioning prov = Provisioning.getInstance();
    // Look up the specified account.  It is optional in the <authToken> case.
    String acctValuePassedIn = null, acctValue = null, acctByStr = null;
    AccountBy acctBy = null;
    Account acct = null;
    Element acctEl = request.getOptionalElement(AccountConstants.E_ACCOUNT);
    boolean csrfSupport = request.getAttributeBool(AccountConstants.A_CSRF_SUPPORT, false);
    String reqTokenType = request.getAttribute(AccountConstants.A_TOKEN_TYPE, "");
    TokenType tokenType = TokenType.fromCode(reqTokenType);
    if (TokenType.JWT.equals(tokenType)) {
        // in case of jwt, csrfSupport has no significance
        csrfSupport = false;
    }
    ZimbraLog.account.debug("auth: reqTokenType: %s, tokenType: %s", reqTokenType, tokenType);
    if (acctEl != null) {
        acctValuePassedIn = acctEl.getText();
        acctValue = acctValuePassedIn;
        acctByStr = acctEl.getAttribute(AccountConstants.A_BY, AccountBy.name.name());
        acctBy = AccountBy.fromString(acctByStr);
        if (acctBy == AccountBy.name) {
            Element virtualHostEl = request.getOptionalElement(AccountConstants.E_VIRTUAL_HOST);
            String virtualHost = virtualHostEl == null ? null : virtualHostEl.getText().toLowerCase();
            if (virtualHost != null && acctValue.indexOf('@') == -1) {
                Domain d = prov.get(Key.DomainBy.virtualHostname, virtualHost);
                if (d != null)
                    acctValue = acctValue + "@" + d.getName();
            }
        }
        acct = prov.get(acctBy, acctValue);
    }
    TrustedDeviceToken trustedToken = null;
    if (acct != null) {
        TrustedDevices trustedDeviceManager = TwoFactorAuth.getFactory().getTrustedDevices(acct);
        if (trustedDeviceManager != null) {
            trustedToken = trustedDeviceManager.getTokenFromRequest(request, context);
            if (trustedToken != null && trustedToken.isExpired()) {
                TrustedDevice device = trustedDeviceManager.getTrustedDeviceByTrustedToken(trustedToken);
                if (device != null) {
                    device.revoke();
                }
            }
        }
    }
    String password = request.getAttribute(AccountConstants.E_PASSWORD, null);
    String recoveryCode = request.getAttribute(AccountConstants.E_RECOVERY_CODE, null);
    boolean generateDeviceId = request.getAttributeBool(AccountConstants.A_GENERATE_DEVICE_ID, false);
    String twoFactorCode = request.getAttribute(AccountConstants.E_TWO_FACTOR_CODE, null);
    String newDeviceId = generateDeviceId ? UUIDUtil.generateUUID() : null;
    Element authTokenEl = request.getOptionalElement(AccountConstants.E_AUTH_TOKEN);
    Element jwtTokenElement = request.getOptionalElement(AccountConstants.E_JWT_TOKEN);
    boolean validationSuccess = tokenTypeAndElementValidation(tokenType, authTokenEl, jwtTokenElement);
    if (!validationSuccess) {
        throw AuthFailedServiceException.AUTH_FAILED("auth: incorrect tokenType and Element combination");
    }
    boolean acctAutoProvisioned = false;
    Claims claims = null;
    // if jwtToken is present in request then use it
    if (jwtTokenElement != null && authTokenEl == null) {
        String jwt = jwtTokenElement.getText();
        String salt = JWTUtil.getSalt(null, context);
        claims = JWTUtil.validateJWT(jwt, salt);
        acct = prov.getAccountById(claims.getSubject());
        if (acct == null) {
            throw AccountServiceException.NO_SUCH_ACCOUNT(claims.getSubject());
        }
        acctAutoProvisioned = true;
    }
    // if authToken is present in request then use it
    if (authTokenEl != null) {
        boolean verifyAccount = authTokenEl.getAttributeBool(AccountConstants.A_VERIFY_ACCOUNT, false);
        if (verifyAccount && acctEl == null) {
            throw ServiceException.INVALID_REQUEST("missing required element: " + AccountConstants.E_ACCOUNT, null);
        }
        try {
            AuthToken at = AuthProvider.getAuthToken(authTokenEl, acct);
            addAccountToLogContextByAuthToken(prov, at);
            // so the account will show in log context
            if (!checkPasswordSecurity(context))
                throw ServiceException.INVALID_REQUEST("clear text password is not allowed", null);
            AuthToken.Usage usage = at.getUsage();
            if (usage != Usage.AUTH && usage != Usage.TWO_FACTOR_AUTH) {
                throw AuthFailedServiceException.AUTH_FAILED("invalid auth token");
            }
            Account authTokenAcct = AuthProvider.validateAuthToken(prov, at, false, usage);
            if (verifyAccount) {
                // can treat the auth token as an opaque string.
                if (acct == null || !acct.getId().equalsIgnoreCase(authTokenAcct.getId())) {
                    throw new AuthTokenException("auth token doesn't match the named account");
                }
            }
            if (usage == Usage.AUTH) {
                ServletRequest httpReq = (ServletRequest) context.get(SoapServlet.SERVLET_REQUEST);
                httpReq.setAttribute(CsrfFilter.AUTH_TOKEN, at);
                if (csrfSupport && !at.isCsrfTokenEnabled()) {
                    // handle case where auth token was originally generated with csrf support
                    // and now client sends the same auth token but saying csrfSupport is turned off
                    // in that case do not disable CSRF check for this authToken.
                    at.setCsrfTokenEnabled(csrfSupport);
                }
                return doResponse(request, at, zsc, context, authTokenAcct, csrfSupport, trustedToken, newDeviceId);
            } else {
                acct = authTokenAcct;
            }
        } catch (AuthTokenException e) {
            throw ServiceException.AUTH_REQUIRED();
        }
    }
    if (!checkPasswordSecurity(context)) {
        throw ServiceException.INVALID_REQUEST("clear text password is not allowed", null);
    }
    Element preAuthEl = request.getOptionalElement(AccountConstants.E_PREAUTH);
    String deviceId = request.getAttribute(AccountConstants.E_DEVICE_ID, null);
    long expires = 0;
    Map<String, Object> authCtxt = new HashMap<String, Object>();
    authCtxt.put(AuthContext.AC_ORIGINATING_CLIENT_IP, context.get(SoapEngine.ORIG_REQUEST_IP));
    authCtxt.put(AuthContext.AC_REMOTE_IP, context.get(SoapEngine.SOAP_REQUEST_IP));
    authCtxt.put(AuthContext.AC_ACCOUNT_NAME_PASSEDIN, acctValuePassedIn);
    authCtxt.put(AuthContext.AC_USER_AGENT, zsc.getUserAgent());
    AuthMode mode = AuthMode.PASSWORD;
    String code = password;
    if (StringUtils.isEmpty(password) && StringUtils.isNotEmpty(recoveryCode)) {
        mode = AuthMode.RECOVERY_CODE;
        code = recoveryCode;
    }
    authCtxt.put(Provisioning.AUTH_MODE_KEY, mode);
    if (acct == null) {
        // try LAZY auto provision if it is enabled
        if (acctBy == AccountBy.name || acctBy == AccountBy.krb5Principal) {
            try {
                if (acctBy == AccountBy.name) {
                    EmailAddress email = new EmailAddress(acctValue, false);
                    String domainName = email.getDomain();
                    Domain domain = domainName == null ? null : prov.get(Key.DomainBy.name, domainName);
                    if (password != null) {
                        acct = prov.autoProvAccountLazy(domain, acctValuePassedIn, password, null);
                    } else if (preAuthEl != null) {
                        long timestamp = preAuthEl.getAttributeLong(AccountConstants.A_TIMESTAMP);
                        expires = preAuthEl.getAttributeLong(AccountConstants.A_EXPIRES, 0);
                        String preAuth = preAuthEl.getTextTrim();
                        prov.preAuthAccount(domain, acctValue, acctByStr, timestamp, expires, preAuth, authCtxt);
                        acct = prov.autoProvAccountLazy(domain, acctValuePassedIn, null, AutoProvAuthMech.PREAUTH);
                    }
                } else {
                    if (password != null) {
                        Domain domain = Krb5Principal.getDomainByKrb5Principal(acctValuePassedIn);
                        if (domain != null) {
                            acct = prov.autoProvAccountLazy(domain, acctValuePassedIn, password, null);
                        }
                    }
                }
                if (acct != null) {
                    acctAutoProvisioned = true;
                }
            } catch (AuthFailedServiceException e) {
                ZimbraLog.account.debug("auth failed, unable to auto provisioing acct " + acctValue, e);
            } catch (ServiceException e) {
                ZimbraLog.account.info("unable to auto provisioing acct " + acctValue, e);
            }
        }
    }
    if (acct == null) {
        // try ZMG Proxy auto provision if it is enabled
        if (acctBy == AccountBy.name && password != null) {
            Pair<Account, Boolean> result = null;
            try {
                result = prov.autoProvZMGProxyAccount(acctValuePassedIn, password);
            } catch (AuthFailedServiceException e) {
            // Most likely in error with user creds
            } catch (ServiceException e) {
                ZimbraLog.account.info("unable to auto provision acct " + acctValuePassedIn, e);
            }
            if (result != null) {
                acct = result.getFirst();
                acctAutoProvisioned = result.getSecond();
            }
        }
    }
    if (acct == null) {
        throw AuthFailedServiceException.AUTH_FAILED(acctValue, acctValuePassedIn, "account not found");
    }
    AccountUtil.addAccountToLogContext(prov, acct.getId(), ZimbraLog.C_NAME, ZimbraLog.C_ID, null);
    Boolean registerTrustedDevice = false;
    TwoFactorAuth twoFactorManager = TwoFactorAuth.getFactory().getTwoFactorAuth(acct);
    if (twoFactorManager.twoFactorAuthEnabled()) {
        registerTrustedDevice = trustedToken == null && request.getAttributeBool(AccountConstants.A_TRUSTED_DEVICE, false);
    }
    // if account was auto provisioned, we had already authenticated the principal
    if (!acctAutoProvisioned) {
        boolean trustedDeviceOverride = false;
        if (trustedToken != null && acct.isFeatureTrustedDevicesEnabled()) {
            if (trustedToken.isExpired()) {
                ZimbraLog.account.debug("trusted token is expired");
                registerTrustedDevice = false;
            } else {
                Map<String, Object> attrs = getTrustedDeviceAttrs(zsc, deviceId);
                try {
                    verifyTrustedDevice(acct, trustedToken, attrs);
                    trustedDeviceOverride = true;
                } catch (AuthFailedServiceException e) {
                    ZimbraLog.account.info("trusted device not verified");
                }
            }
        }
        boolean usingTwoFactorAuth = acct != null && twoFactorManager.twoFactorAuthRequired() && !trustedDeviceOverride;
        boolean twoFactorAuthWithToken = usingTwoFactorAuth && authTokenEl != null;
        if (password != null || recoveryCode != null || twoFactorAuthWithToken) {
            // authentication logic can be reached with either a password, or a 2FA auth token
            if (usingTwoFactorAuth && twoFactorCode == null && (password != null || recoveryCode != null)) {
                int mtaAuthPort = acct.getServer().getMtaAuthPort();
                boolean supportsAppSpecificPaswords = acct.isFeatureAppSpecificPasswordsEnabled() && zsc.getPort() == mtaAuthPort;
                if (supportsAppSpecificPaswords && password != null) {
                    // if we are here, it means we are authenticating SMTP,
                    // so app-specific passwords are accepted. Other protocols (pop, imap)
                    // doesn't touch this code, so their authentication happens in ZimbraAuth.
                    AppSpecificPasswords appPasswords = TwoFactorAuth.getFactory().getAppSpecificPasswords(acct, acctValuePassedIn);
                    appPasswords.authenticate(password);
                } else {
                    prov.authAccount(acct, code, AuthContext.Protocol.soap, authCtxt);
                    return needTwoFactorAuth(acct, twoFactorManager, zsc, tokenType);
                }
            } else {
                if (password != null || recoveryCode != null) {
                    prov.authAccount(acct, code, AuthContext.Protocol.soap, authCtxt);
                } else {
                    // it's ok to not have a password if the client is using a 2FA auth token for the 2nd step of 2FA
                    if (!twoFactorAuthWithToken) {
                        throw ServiceException.AUTH_REQUIRED();
                    }
                }
                if (usingTwoFactorAuth) {
                    // check that 2FA has been enabled, in case the client is passing in a twoFactorCode prior to setting up 2FA
                    if (!twoFactorManager.twoFactorAuthEnabled()) {
                        throw AccountServiceException.TWO_FACTOR_SETUP_REQUIRED();
                    }
                    AuthToken twoFactorToken = null;
                    if (password == null) {
                        try {
                            twoFactorToken = AuthProvider.getAuthToken(authTokenEl, acct);
                            Account twoFactorTokenAcct = AuthProvider.validateAuthToken(prov, twoFactorToken, false, Usage.TWO_FACTOR_AUTH);
                            boolean verifyAccount = authTokenEl.getAttributeBool(AccountConstants.A_VERIFY_ACCOUNT, false);
                            if (verifyAccount && !twoFactorTokenAcct.getId().equalsIgnoreCase(acct.getId())) {
                                throw new AuthTokenException("two-factor auth token doesn't match the named account");
                            }
                        } catch (AuthTokenException e) {
                            throw AuthFailedServiceException.AUTH_FAILED("bad auth token");
                        }
                    }
                    TwoFactorAuth manager = TwoFactorAuth.getFactory().getTwoFactorAuth(acct);
                    if (twoFactorCode != null) {
                        manager.authenticate(twoFactorCode);
                    } else {
                        throw AuthFailedServiceException.AUTH_FAILED("no two-factor code provided");
                    }
                    if (twoFactorToken != null) {
                        try {
                            twoFactorToken.deRegister();
                        } catch (AuthTokenException e) {
                            throw ServiceException.FAILURE("cannot de-register two-factor auth token", e);
                        }
                    }
                }
            }
        } else if (preAuthEl != null) {
            long timestamp = preAuthEl.getAttributeLong(AccountConstants.A_TIMESTAMP);
            expires = preAuthEl.getAttributeLong(AccountConstants.A_EXPIRES, 0);
            String preAuth = preAuthEl.getTextTrim();
            prov.preAuthAccount(acct, acctValue, acctByStr, timestamp, expires, preAuth, authCtxt);
        } else {
            throw ServiceException.INVALID_REQUEST("must specify " + AccountConstants.E_PASSWORD, null);
        }
    }
    AuthToken at = expires == 0 ? AuthProvider.getAuthToken(acct, tokenType) : AuthProvider.getAuthToken(acct, expires, tokenType);
    if (registerTrustedDevice && (trustedToken == null || trustedToken.isExpired())) {
        // generate a new trusted device token if there is no existing one or if the current one is no longer valid
        Map<String, Object> attrs = getTrustedDeviceAttrs(zsc, newDeviceId == null ? deviceId : newDeviceId);
        TrustedDevices trustedDeviceManager = TwoFactorAuth.getFactory().getTrustedDevices(acct);
        if (trustedDeviceManager != null) {
            trustedToken = trustedDeviceManager.registerTrustedDevice(attrs);
        }
    }
    ServletRequest httpReq = (ServletRequest) context.get(SoapServlet.SERVLET_REQUEST);
    // For CSRF filter so that token generation can happen
    if (csrfSupport && !at.isCsrfTokenEnabled()) {
        // handle case where auth token was originally generated with csrf support
        // and now client sends the same auth token but saying csrfSupport is turned off
        // in that case do not disable CSRF check for this authToken.
        at.setCsrfTokenEnabled(csrfSupport);
    }
    httpReq.setAttribute(CsrfFilter.AUTH_TOKEN, at);
    return doResponse(request, at, zsc, context, acct, csrfSupport, trustedToken, newDeviceId);
}
Also used : Account(com.zimbra.cs.account.Account) HttpServletRequest(javax.servlet.http.HttpServletRequest) ServletRequest(javax.servlet.ServletRequest) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) TrustedDeviceToken(com.zimbra.cs.account.TrustedDeviceToken) HashMap(java.util.HashMap) Element(com.zimbra.common.soap.Element) Provisioning(com.zimbra.cs.account.Provisioning) AccountBy(com.zimbra.common.account.Key.AccountBy) TokenType(com.zimbra.cs.account.AuthToken.TokenType) TwoFactorAuth(com.zimbra.cs.account.auth.twofactor.TwoFactorAuth) Claims(io.jsonwebtoken.Claims) Usage(com.zimbra.cs.account.AuthToken.Usage) AppSpecificPasswords(com.zimbra.cs.account.auth.twofactor.AppSpecificPasswords) EmailAddress(com.zimbra.cs.account.names.NameUtil.EmailAddress) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) ZimbraSoapContext(com.zimbra.soap.ZimbraSoapContext) AuthTokenException(com.zimbra.cs.account.AuthTokenException) TrustedDevice(com.zimbra.cs.account.TrustedDevice) AuthToken(com.zimbra.cs.account.AuthToken) Domain(com.zimbra.cs.account.Domain) AuthMode(com.zimbra.cs.account.Provisioning.AuthMode) TrustedDevices(com.zimbra.cs.account.auth.twofactor.TrustedDevices)

Example 3 with AccountBy

use of com.zimbra.common.account.Key.AccountBy in project zm-mailbox by Zimbra.

the class PreAuthServlet method doGet.

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ZimbraLog.clearContext();
    try {
        Provisioning prov = Provisioning.getInstance();
        Server server = prov.getLocalServer();
        String referMode = server.getAttr(Provisioning.A_zimbraMailReferMode, "wronghost");
        boolean isRedirect = getOptionalParam(req, PARAM_ISREDIRECT, "0").equals("1");
        String rawAuthToken = getOptionalParam(req, PARAM_AUTHTOKEN, null);
        AuthToken authToken = null;
        if (rawAuthToken != null) {
            authToken = AuthProvider.getAuthToken(rawAuthToken);
            if (authToken == null) {
                throw new AuthTokenException("unable to get auth token from " + PARAM_AUTHTOKEN);
            } else if (authToken.isExpired()) {
                throw new AuthTokenException("auth token expired");
            } else if (!authToken.isRegistered()) {
                throw new AuthTokenException("authtoken is invalid");
            }
        }
        if (rawAuthToken != null) {
            if (!authToken.isRegistered()) {
                throw new AuthTokenException("authtoken is not registered");
            }
            if (authToken.isExpired()) {
                throw new AuthTokenException("authtoken is expired registered");
            }
            // we've got an auth token in the request:
            // See if we need a redirect to the correct server
            boolean isAdmin = authToken != null && AuthToken.isAnyAdmin(authToken);
            Account acct = prov.get(AccountBy.id, authToken.getAccountId(), authToken);
            if (isAdmin || !needReferral(acct, referMode, isRedirect)) {
                // authtoken in get request is for one time use only. Deregister and generate new one.
                if (authToken instanceof ZimbraAuthToken) {
                    ZimbraAuthToken oneTimeToken = (ZimbraAuthToken) authToken;
                    ZimbraAuthToken newZimbraAuthToken = null;
                    try {
                        newZimbraAuthToken = oneTimeToken.clone();
                    } catch (CloneNotSupportedException e) {
                        throw new ServletException(e);
                    }
                    newZimbraAuthToken.resetTokenId();
                    oneTimeToken.deRegister();
                    authToken = newZimbraAuthToken;
                    ZimbraLog.account.debug("Deregistered the one time preauth token and issuing new one to the user.");
                }
                // no need to redirect to the correct server, just send them off to do business
                setCookieAndRedirect(req, resp, authToken);
            } else {
                // redirect to the correct server with the incoming auth token
                // we no longer send the auth token we generate over when we redirect to the correct server,
                // but customer can be sending a token in their preauth URL, in this case, just
                // send over the auth token as is.
                redirectToCorrectServer(req, resp, acct, rawAuthToken);
            }
        } else {
            // no auth token in the request URL.  See if we should redirect this request
            // to the correct server, or should do the preauth locally.
            String preAuth = getRequiredParam(req, resp, PARAM_PREAUTH);
            String account = getRequiredParam(req, resp, PARAM_ACCOUNT);
            String accountBy = getOptionalParam(req, PARAM_BY, AccountBy.name.name());
            AccountBy by = AccountBy.fromString(accountBy);
            boolean admin = getOptionalParam(req, PARAM_ADMIN, "0").equals("1") && isAdminRequest(req);
            long timestamp = Long.parseLong(getRequiredParam(req, resp, PARAM_TIMESTAMP));
            long expires = Long.parseLong(getRequiredParam(req, resp, PARAM_EXPIRES));
            Account acct = null;
            acct = prov.get(by, account, authToken);
            Map<String, Object> authCtxt = new HashMap<String, Object>();
            authCtxt.put(AuthContext.AC_ORIGINATING_CLIENT_IP, ZimbraServlet.getOrigIp(req));
            authCtxt.put(AuthContext.AC_REMOTE_IP, ZimbraServlet.getClientIp(req));
            authCtxt.put(AuthContext.AC_ACCOUNT_NAME_PASSEDIN, account);
            authCtxt.put(AuthContext.AC_USER_AGENT, req.getHeader("User-Agent"));
            boolean acctAutoProvisioned = false;
            if (acct == null) {
                // 
                if (by == AccountBy.name && !admin) {
                    try {
                        EmailAddress email = new EmailAddress(account, false);
                        String domainName = email.getDomain();
                        Domain domain = domainName == null ? null : prov.get(Key.DomainBy.name, domainName);
                        prov.preAuthAccount(domain, account, accountBy, timestamp, expires, preAuth, authCtxt);
                        acct = prov.autoProvAccountLazy(domain, account, null, AutoProvAuthMech.PREAUTH);
                        if (acct != null) {
                            acctAutoProvisioned = true;
                        }
                    } catch (AuthFailedServiceException e) {
                        ZimbraLog.account.debug("auth failed, unable to auto provision acct " + account, e);
                    } catch (ServiceException e) {
                        ZimbraLog.account.info("unable to auto provision acct " + account, e);
                    }
                }
            }
            if (acct == null) {
                throw AuthFailedServiceException.AUTH_FAILED(account, account, "account not found");
            }
            String accountStatus = acct.getAccountStatus(prov);
            if (!Provisioning.ACCOUNT_STATUS_ACTIVE.equalsIgnoreCase(accountStatus)) {
                if (Provisioning.ACCOUNT_STATUS_MAINTENANCE.equalsIgnoreCase(accountStatus)) {
                    throw AccountServiceException.MAINTENANCE_MODE();
                } else {
                    throw AccountServiceException.ACCOUNT_INACTIVE(acct.getName());
                }
            }
            if (admin) {
                boolean isDomainAdminAccount = acct.getBooleanAttr(Provisioning.A_zimbraIsDomainAdminAccount, false);
                boolean isAdminAccount = acct.getBooleanAttr(Provisioning.A_zimbraIsAdminAccount, false);
                boolean isDelegatedAdminAccount = acct.getBooleanAttr(Provisioning.A_zimbraIsDelegatedAdminAccount, false);
                boolean ok = (isDomainAdminAccount || isAdminAccount || isDelegatedAdminAccount);
                if (!ok)
                    throw ServiceException.PERM_DENIED("not an admin account");
            }
            if (admin || !needReferral(acct, referMode, isRedirect)) {
                // do preauth locally
                if (!acctAutoProvisioned) {
                    prov.preAuthAccount(acct, account, accountBy, timestamp, expires, preAuth, admin, authCtxt);
                }
                AuthToken at;
                if (admin)
                    at = (expires == 0) ? AuthProvider.getAuthToken(acct, admin) : AuthProvider.getAuthToken(acct, expires, admin, null);
                else
                    at = (expires == 0) ? AuthProvider.getAuthToken(acct) : AuthProvider.getAuthToken(acct, expires);
                setCookieAndRedirect(req, resp, at);
            } else {
                // redirect to the correct server.
                // Note: we do not send over the generated auth token (the auth token param passed to
                // redirectToCorrectServer is null).
                redirectToCorrectServer(req, resp, acct, null);
            }
        }
    } catch (ServiceException e) {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
    } catch (AuthTokenException e) {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
    }
}
Also used : Account(com.zimbra.cs.account.Account) Server(com.zimbra.cs.account.Server) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) HashMap(java.util.HashMap) ZimbraAuthToken(com.zimbra.cs.account.ZimbraAuthToken) Provisioning(com.zimbra.cs.account.Provisioning) EmailAddress(com.zimbra.cs.account.names.NameUtil.EmailAddress) AccountBy(com.zimbra.common.account.Key.AccountBy) ServletException(javax.servlet.ServletException) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) AuthTokenException(com.zimbra.cs.account.AuthTokenException) ZimbraAuthToken(com.zimbra.cs.account.ZimbraAuthToken) AuthToken(com.zimbra.cs.account.AuthToken) Domain(com.zimbra.cs.account.Domain)

Example 4 with AccountBy

use of com.zimbra.common.account.Key.AccountBy in project zm-mailbox by Zimbra.

the class ChangePrimaryEmail method handle.

@Override
public Element handle(Element request, Map<String, Object> context) throws ServiceException {
    ZimbraSoapContext zsc = getZimbraSoapContext(context);
    Provisioning prov = Provisioning.getInstance();
    ChangePrimaryEmailRequest req = JaxbUtil.elementToJaxb(request);
    String newName = req.getNewName();
    if (StringUtils.isEmpty(newName)) {
        throw ServiceException.INVALID_REQUEST(String.format("missing <%s>", AdminConstants.E_NEW_NAME), null);
    }
    AccountSelector acctSel = req.getAccount();
    if (acctSel == null) {
        throw ServiceException.INVALID_REQUEST(String.format("missing <%s>", AdminConstants.E_ACCOUNT), null);
    }
    String accountSelectorKey = acctSel.getKey();
    AccountBy by = acctSel.getBy().toKeyAccountBy();
    Account account = prov.get(by, accountSelectorKey, zsc.getAuthToken());
    defendAgainstAccountHarvesting(account, by, accountSelectorKey, zsc, Admin.R_renameAccount);
    defendAgainstAccountOrCalendarResourceHarvesting(account, by, accountSelectorKey, zsc, Admin.R_addAccountAlias, Admin.R_addCalendarResourceAlias);
    String oldName = account.getName();
    String accountId = account.getId();
    checkAccountRight(zsc, account, Admin.R_renameAccount);
    checkDomainRightByEmail(zsc, newName, Admin.R_createAccount);
    checkDomainRightByEmail(zsc, oldName, Admin.R_createAlias);
    account.setOldMailAddress(oldName);
    try {
        ZimbraLog.security.debug("old mail address set to %s for account %s", oldName, accountId);
        Mailbox mbox = Provisioning.onLocalServer(account) ? MailboxManager.getInstance().getMailboxByAccount(account) : null;
        if (mbox == null) {
            throw ServiceException.FAILURE("unable to get mailbox for rename: " + accountId, null);
        }
        prov.renameAccount(accountId, newName);
        mbox.renameMailbox(oldName, newName);
        ZimbraLog.security.info(ZimbraLog.encodeAttrs(new String[] { "cmd", "ChangePrimaryEmail account renamed", "name", oldName, "newName", newName }));
        // get account again after rename
        account = prov.get(AccountBy.id, accountId, true, zsc.getAuthToken());
        if (account == null) {
            throw ServiceException.FAILURE("unable to get account after rename: " + accountId, null);
        }
        int sleepTime = DebugConfig.sleepTimeForTestingChangePrimaryEmail;
        try {
            ZimbraLog.security.debug("ChangePrimaryEmail thread sleeping for %d milliseconds", sleepTime);
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
            ZimbraLog.security.debug("ChangePrimaryEmail sleep was interrupted", e);
        }
        prov.addAlias(account, oldName);
        ZimbraLog.security.info(ZimbraLog.encodeAttrs(new String[] { "cmd", "ChangePrimaryEmail added alias", "name", account.getName(), "alias", oldName }));
        Date renameTime = new Date();
        account.addPrimaryEmailChangeHistory(String.format("%s|%d", oldName, renameTime.getTime()));
    } finally {
        if (account != null) {
            prov.flushCache(CacheEntryType.account, new CacheEntry[] { new CacheEntry(Key.CacheEntryBy.id, account.getId()) });
            account.unsetOldMailAddress();
        }
    }
    Element response = zsc.createElement(AdminConstants.CHANGE_PRIMARY_EMAIL_RESPONSE);
    ToXML.encodeAccount(response, account);
    return response;
}
Also used : Account(com.zimbra.cs.account.Account) Element(com.zimbra.common.soap.Element) AccountSelector(com.zimbra.soap.type.AccountSelector) CacheEntry(com.zimbra.cs.account.Provisioning.CacheEntry) Provisioning(com.zimbra.cs.account.Provisioning) ChangePrimaryEmailRequest(com.zimbra.soap.admin.message.ChangePrimaryEmailRequest) Date(java.util.Date) AccountBy(com.zimbra.common.account.Key.AccountBy) Mailbox(com.zimbra.cs.mailbox.Mailbox) ZimbraSoapContext(com.zimbra.soap.ZimbraSoapContext)

Example 5 with AccountBy

use of com.zimbra.common.account.Key.AccountBy in project zm-mailbox by Zimbra.

the class AdminDocumentHandler method verifyAccountHarvestingAndPerms.

public Account verifyAccountHarvestingAndPerms(AccountSelector acctSel, ZimbraSoapContext zsc) throws ServiceException {
    Provisioning prov = Provisioning.getInstance();
    if (acctSel == null) {
        ServiceException se = ServiceException.INVALID_REQUEST(String.format("missing <%s>", AdminConstants.E_ACCOUNT), null);
        ZimbraLog.filter.debug("AccountSelector not found", se);
        throw se;
    }
    String accountSelectorKey = acctSel.getKey();
    AccountBy by = acctSel.getBy().toKeyAccountBy();
    Account account = prov.get(by, accountSelectorKey, zsc.getAuthToken());
    defendAgainstAccountHarvesting(account, by, accountSelectorKey, zsc, Admin.R_getAccountInfo);
    if (!canModifyOptions(zsc, account)) {
        ServiceException se = ServiceException.PERM_DENIED("cannot modify options");
        ZimbraLog.filter.debug("Do not have permission to modify options on account %s", account.getName(), se);
        throw se;
    }
    return account;
}
Also used : Account(com.zimbra.cs.account.Account) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) Provisioning(com.zimbra.cs.account.Provisioning) AccountBy(com.zimbra.common.account.Key.AccountBy)

Aggregations

AccountBy (com.zimbra.common.account.Key.AccountBy)16 Account (com.zimbra.cs.account.Account)13 Provisioning (com.zimbra.cs.account.Provisioning)13 Element (com.zimbra.common.soap.Element)10 ZimbraSoapContext (com.zimbra.soap.ZimbraSoapContext)10 ServiceException (com.zimbra.common.service.ServiceException)5 Domain (com.zimbra.cs.account.Domain)4 HashMap (java.util.HashMap)4 AccountServiceException (com.zimbra.cs.account.AccountServiceException)3 AuthFailedServiceException (com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException)3 AuthToken (com.zimbra.cs.account.AuthToken)3 AuthTokenException (com.zimbra.cs.account.AuthTokenException)3 AccountSelector (com.zimbra.soap.type.AccountSelector)3 Key (com.zimbra.common.account.Key)2 Server (com.zimbra.cs.account.Server)2 TwoFactorAuth (com.zimbra.cs.account.auth.twofactor.TwoFactorAuth)2 EmailAddress (com.zimbra.cs.account.names.NameUtil.EmailAddress)2 ZMailbox (com.zimbra.client.ZMailbox)1 DistributionListBy (com.zimbra.common.account.Key.DistributionListBy)1 XMLElement (com.zimbra.common.soap.Element.XMLElement)1