Search in sources :

Example 1 with EmailAddress

use of com.zimbra.cs.account.names.NameUtil.EmailAddress in project zm-mailbox by Zimbra.

the class AutoProvision method mapName.

/**
     * map external name to zimbra name for the account to be created in Zimbra.
     *
     * @param externalAttrs
     * @return
     * @throws ServiceException
     */
protected String mapName(ZAttributes externalAttrs, String loginName) throws ServiceException {
    String localpart = null;
    String localpartAttr = domain.getAutoProvAccountNameMap();
    if (localpartAttr != null) {
        localpart = externalAttrs.getAttrString(localpartAttr);
        if (localpart == null) {
            throw ServiceException.FAILURE("AutoProvision: unable to get localpart: " + loginName, null);
        }
    } else {
        if (loginName == null) {
            throw ServiceException.FAILURE("AutoProvision: unable to map acount name, must configure " + Provisioning.A_zimbraAutoProvAccountNameMap, null);
        }
        EmailAddress emailAddr = new EmailAddress(loginName, false);
        localpart = emailAddr.getLocalPart();
    }
    return localpart + "@" + domain.getName();
}
Also used : EmailAddress(com.zimbra.cs.account.names.NameUtil.EmailAddress)

Example 2 with EmailAddress

use of com.zimbra.cs.account.names.NameUtil.EmailAddress in project zm-mailbox by Zimbra.

the class LdapProvisioning method createDynamicGroup.

private DynamicGroup createDynamicGroup(String groupAddress, Map<String, Object> groupAttrs, Account creator) throws ServiceException {
    SpecialAttrs specialAttrs = mDIT.handleSpecialAttrs(groupAttrs);
    String baseDn = specialAttrs.getLdapBaseDn();
    groupAddress = groupAddress.toLowerCase().trim();
    EmailAddress addr = new EmailAddress(groupAddress);
    String localPart = addr.getLocalPart();
    String domainName = addr.getDomain();
    domainName = IDNUtil.toAsciiDomainName(domainName);
    groupAddress = EmailAddress.getAddress(localPart, domainName);
    validEmailAddress(groupAddress);
    CallbackContext callbackContext = new CallbackContext(CallbackContext.Op.CREATE);
    callbackContext.setCreatingEntryName(groupAddress);
    // remove zimbraIsACLGroup from attrs if provided, to avoid the immutable check
    Object providedZimbraIsACLGroup = groupAttrs.get(A_zimbraIsACLGroup);
    if (providedZimbraIsACLGroup != null) {
        groupAttrs.remove(A_zimbraIsACLGroup);
    }
    AttributeManager.getInstance().preModify(groupAttrs, null, callbackContext, true);
    // put zimbraIsACLGroup back
    if (providedZimbraIsACLGroup != null) {
        groupAttrs.put(A_zimbraIsACLGroup, providedZimbraIsACLGroup);
    }
    ZLdapContext zlc = null;
    try {
        zlc = LdapClient.getContext(LdapServerType.MASTER, LdapUsage.CREATE_DYNAMICGROUP);
        Domain domain = getDomainByAsciiName(domainName, zlc);
        if (domain == null) {
            throw AccountServiceException.NO_SUCH_DOMAIN(domainName);
        }
        if (!domain.isLocal()) {
            throw ServiceException.INVALID_REQUEST("domain type must be local", null);
        }
        String domainDN = ((LdapDomain) domain).getDN();
        /*
             * ====================================
             * create the main dynamic group entry
             * ====================================
             */
        ZMutableEntry entry = LdapClient.createMutableEntry();
        entry.mapToAttrs(groupAttrs);
        Set<String> ocs = LdapObjectClass.getGroupObjectClasses(this);
        entry.addAttr(A_objectClass, ocs);
        String zimbraId = LdapUtil.generateUUID();
        // create a UUID for the static unit entry
        String staticUnitZimbraId = LdapUtil.generateUUID();
        String createTimestamp = LdapDateUtil.toGeneralizedTime(new Date());
        entry.setAttr(A_zimbraId, zimbraId);
        entry.setAttr(A_zimbraCreateTimestamp, createTimestamp);
        entry.setAttr(A_mail, groupAddress);
        entry.setAttr(A_dgIdentity, LC.zimbra_ldap_userdn.value());
        // unlike accounts (which have a zimbraMailDeliveryAddress for the primary,
        // and zimbraMailAliases only for aliases), DLs use zimbraMailAlias for both.
        // Postfix uses these two attributes to route mail, and zimbraMailDeliveryAddress
        // indicates that something has a physical mailbox, which DLs don't.
        entry.setAttr(A_zimbraMailAlias, groupAddress);
        /*
            // allow only users in the same domain
            String memberURL = String.format("ldap:///%s??one?(zimbraMemberOf=%s)",
                    mDIT.domainDNToAccountBaseDN(domainDN), groupAddress);
            */
        String specifiedIsACLGroup = entry.getAttrString(A_zimbraIsACLGroup);
        boolean isACLGroup;
        if (!entry.hasAttribute(A_memberURL)) {
            String memberURL = LdapDynamicGroup.getDefaultMemberURL(zimbraId, staticUnitZimbraId);
            entry.setAttr(Provisioning.A_memberURL, memberURL);
            // or specified to be TRUE;
            if (specifiedIsACLGroup == null) {
                entry.setAttr(A_zimbraIsACLGroup, ProvisioningConstants.TRUE);
            } else if (ProvisioningConstants.FALSE.equals(specifiedIsACLGroup)) {
                throw ServiceException.INVALID_REQUEST("No custom " + A_memberURL + " is provided, " + A_zimbraIsACLGroup + " cannot be set to FALSE", null);
            }
            isACLGroup = true;
        } else {
            // We want to be able to use dynamic groups as ACLs, for instance when sharing a folder with a group
            // This used to be disallowed via a requirement that zimbraIsACLGroup be specified and set to FALSE.
            // That requirement has been dropped.
            isACLGroup = !ProvisioningConstants.FALSE.equals(specifiedIsACLGroup);
        }
        // by default a dynamic group is always created enabled
        if (!entry.hasAttribute(Provisioning.A_zimbraMailStatus)) {
            entry.setAttr(A_zimbraMailStatus, MAIL_STATUS_ENABLED);
        }
        String mailStatus = entry.getAttrString(A_zimbraMailStatus);
        entry.setAttr(A_cn, localPart);
        // entry.setAttr(A_uid, localPart); need to use uid if we move dynamic groups to the ou=people tree
        setGroupHomeServer(entry, creator);
        String dn = mDIT.dynamicGroupNameLocalPartToDN(localPart, domainDN);
        entry.setDN(dn);
        zlc.createEntry(entry);
        if (isACLGroup) {
            /*
                 * ===========================================================
                 * create the dynamic group unit entry, for internal addresses
                 * ===========================================================
                 */
            String dynamicUnitLocalpart = dynamicGroupDynamicUnitLocalpart(localPart);
            String dynamicUnitAddr = EmailAddress.getAddress(dynamicUnitLocalpart, domainName);
            entry = LdapClient.createMutableEntry();
            ocs = LdapObjectClass.getGroupDynamicUnitObjectClasses(this);
            entry.addAttr(A_objectClass, ocs);
            String dynamicUnitZimbraId = LdapUtil.generateUUID();
            entry.setAttr(A_cn, DYNAMIC_GROUP_DYNAMIC_UNIT_NAME);
            entry.setAttr(A_zimbraId, dynamicUnitZimbraId);
            // id of the main group
            entry.setAttr(A_zimbraGroupId, zimbraId);
            entry.setAttr(A_zimbraCreateTimestamp, createTimestamp);
            entry.setAttr(A_mail, dynamicUnitAddr);
            entry.setAttr(A_zimbraMailAlias, dynamicUnitAddr);
            entry.setAttr(A_zimbraMailStatus, mailStatus);
            entry.setAttr(A_dgIdentity, LC.zimbra_ldap_userdn.value());
            // id of the main group
            String memberURL = LdapDynamicGroup.getDefaultDynamicUnitMemberURL(zimbraId);
            entry.setAttr(Provisioning.A_memberURL, memberURL);
            String dynamicUnitDN = mDIT.dynamicGroupUnitNameToDN(DYNAMIC_GROUP_DYNAMIC_UNIT_NAME, dn);
            entry.setDN(dynamicUnitDN);
            zlc.createEntry(entry);
            /*
                 * ==========================================================
                 * create the static group unit entry, for external addresses
                 * ==========================================================
                 */
            entry = LdapClient.createMutableEntry();
            ocs = LdapObjectClass.getGroupStaticUnitObjectClasses(this);
            entry.addAttr(A_objectClass, ocs);
            entry.setAttr(A_cn, DYNAMIC_GROUP_STATIC_UNIT_NAME);
            entry.setAttr(A_zimbraId, staticUnitZimbraId);
            // id of the main group
            entry.setAttr(A_zimbraGroupId, zimbraId);
            entry.setAttr(A_zimbraCreateTimestamp, createTimestamp);
            String staticUnitDN = mDIT.dynamicGroupUnitNameToDN(DYNAMIC_GROUP_STATIC_UNIT_NAME, dn);
            entry.setDN(staticUnitDN);
            zlc.createEntry(entry);
        }
        /*
             * all is well, get the group by id
             */
        DynamicGroup group = getDynamicGroupBasic(DistributionListBy.id, zimbraId, zlc);
        if (group != null) {
            AttributeManager.getInstance().postModify(groupAttrs, group, callbackContext);
            removeExternalAddrsFromAllDynamicGroups(group.getAllAddrsSet(), zlc);
            allDLs.addGroup(group);
        } else {
            throw ServiceException.FAILURE("unable to get dynamic group after creating LDAP entry: " + groupAddress, null);
        }
        return group;
    } catch (LdapEntryAlreadyExistException nabe) {
        throw AccountServiceException.DISTRIBUTION_LIST_EXISTS(groupAddress);
    } catch (LdapException e) {
        throw e;
    } catch (AccountServiceException e) {
        throw e;
    } finally {
        LdapClient.closeContext(zlc);
    }
}
Also used : ZMutableEntry(com.zimbra.cs.ldap.ZMutableEntry) DynamicGroup(com.zimbra.cs.account.DynamicGroup) LdapDynamicGroup(com.zimbra.cs.account.ldap.entry.LdapDynamicGroup) LdapEntryAlreadyExistException(com.zimbra.cs.ldap.LdapException.LdapEntryAlreadyExistException) ZLdapContext(com.zimbra.cs.ldap.ZLdapContext) LdapDomain(com.zimbra.cs.account.ldap.entry.LdapDomain) EmailAddress(com.zimbra.cs.account.names.NameUtil.EmailAddress) Date(java.util.Date) AccountServiceException(com.zimbra.cs.account.AccountServiceException) CallbackContext(com.zimbra.cs.account.callback.CallbackContext) LdapDomain(com.zimbra.cs.account.ldap.entry.LdapDomain) Domain(com.zimbra.cs.account.Domain) LdapException(com.zimbra.cs.ldap.LdapException)

Example 3 with EmailAddress

use of com.zimbra.cs.account.names.NameUtil.EmailAddress 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 provisioing acct " + account, e);
                    } catch (ServiceException e) {
                        ZimbraLog.account.info("unable to auto provisioing 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 EmailAddress

use of com.zimbra.cs.account.names.NameUtil.EmailAddress 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);
    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);
    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);
    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());
    boolean acctAutoProvisioned = false;
    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 || twoFactorAuthWithToken) {
            // authentication logic can be reached with either a password, or a 2FA auth token
            if (usingTwoFactorAuth && twoFactorCode == null && password != 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, password, AuthContext.Protocol.soap, authCtxt);
                    return needTwoFactorAuth(acct, twoFactorManager, zsc);
                }
            } else {
                if (password != null) {
                    prov.authAccount(acct, password, 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) : AuthProvider.getAuthToken(acct, expires);
    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);
        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) TwoFactorAuth(com.zimbra.cs.account.auth.twofactor.TwoFactorAuth) 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) TrustedDevices(com.zimbra.cs.account.auth.twofactor.TrustedDevices)

Aggregations

EmailAddress (com.zimbra.cs.account.names.NameUtil.EmailAddress)4 AccountServiceException (com.zimbra.cs.account.AccountServiceException)3 Domain (com.zimbra.cs.account.Domain)3 AccountBy (com.zimbra.common.account.Key.AccountBy)2 ServiceException (com.zimbra.common.service.ServiceException)2 Account (com.zimbra.cs.account.Account)2 AuthFailedServiceException (com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException)2 AuthToken (com.zimbra.cs.account.AuthToken)2 AuthTokenException (com.zimbra.cs.account.AuthTokenException)2 Provisioning (com.zimbra.cs.account.Provisioning)2 HashMap (java.util.HashMap)2 Element (com.zimbra.common.soap.Element)1 Usage (com.zimbra.cs.account.AuthToken.Usage)1 DynamicGroup (com.zimbra.cs.account.DynamicGroup)1 Server (com.zimbra.cs.account.Server)1 TrustedDevice (com.zimbra.cs.account.TrustedDevice)1 TrustedDeviceToken (com.zimbra.cs.account.TrustedDeviceToken)1 ZimbraAuthToken (com.zimbra.cs.account.ZimbraAuthToken)1 AppSpecificPasswords (com.zimbra.cs.account.auth.twofactor.AppSpecificPasswords)1 TrustedDevices (com.zimbra.cs.account.auth.twofactor.TrustedDevices)1