text:
package org.zstack.identity;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.Platform;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.EventCallback;
import org.zstack.core.cloudbus.EventFacade;
import org.zstack.core.cloudbus.MessageSafe;
import org.zstack.core.componentloader.PluginRegistry;
import org.zstack.core.config.*;
import org.zstack.core.db.*;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.core.thread.PeriodicTask;
import org.zstack.core.thread.ThreadFacade;
import org.zstack.header.APIIsOpensourceVersionMsg;
import org.zstack.header.APIIsOpensourceVersionReply;
import org.zstack.header.AbstractService;
import org.zstack.header.apimediator.ApiMessageInterceptionException;
import org.zstack.header.apimediator.ApiMessageInterceptor;
import org.zstack.header.apimediator.GlobalApiMessageInterceptor;
import org.zstack.header.errorcode.OperationFailureException;
import org.zstack.header.errorcode.SysErrors;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.identity.*;
import org.zstack.header.identity.AccountConstant.StatementEffect;
import org.zstack.header.identity.IdentityCanonicalEvents.AccountDeletedData;
import org.zstack.header.identity.IdentityCanonicalEvents.UserDeletedData;
import org.zstack.header.identity.PolicyInventory.Statement;
import org.zstack.header.identity.Quota.QuotaPair;
import org.zstack.header.managementnode.PrepareDbInitialValueExtensionPoint;
import org.zstack.header.message.*;
import org.zstack.header.notification.ApiNotification;
import org.zstack.header.notification.ApiNotificationFactory;
import org.zstack.header.notification.ApiNotificationFactoryExtensionPoint;
import org.zstack.header.search.APIGetMessage;
import org.zstack.header.search.APISearchMessage;
import org.zstack.header.vo.*;
import org.zstack.utils.*;
import org.zstack.utils.function.ForEachFunction;
import org.zstack.utils.gson.JSONObjectUtil;
import org.zstack.utils.logging.CLogger;
import org.zstack.utils.path.PathUtil;

import static org.zstack.core.Platform.argerr;
import static org.zstack.core.Platform.operr;

import javax.persistence.Query;
import javax.persistence.Tuple;
import javax.persistence.TypedQuery;
import java.io.File;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static org.zstack.utils.CollectionDSL.list;

public class AccountManagerImpl extends AbstractService implements AccountManager, PrepareDbInitialValueExtensionPoint,
        SoftDeleteEntityExtensionPoint, HardDeleteEntityExtensionPoint,
        GlobalApiMessageInterceptor, ApiMessageInterceptor, ApiNotificationFactoryExtensionPoint {
    private static final CLogger logger = Utils.getLogger(AccountManagerImpl.class);

    @Autowired
    private CloudBus bus;
    @Autowired
    private DatabaseFacade dbf;
    @Autowired
    private DbEntityLister dl;
    @Autowired
    private ErrorFacade errf;
    @Autowired
    private ThreadFacade thdf;
    @Autowired
    private PluginRegistry pluginRgty;
    @Autowired
    private EventFacade evtf;
    @Autowired
    private GlobalConfigFacade gcf;

    private List<String> resourceTypeForAccountRef;
    private List<Class> resourceTypes;
    private Map<String, SessionInventory> sessions = new ConcurrentHashMap<>();
    private Map<Class, List<Quota>> messageQuotaMap = new HashMap<>();
    private Map<String, Quota> nameQuotaMap = new HashMap<>();
    private HashSet<Class> accountApiControl = new HashSet<>();
    private HashSet<Class> accountApiControlInternal = new HashSet<>();
    private List<Quota> definedQuotas = new ArrayList<>();

    @Override
    public Map<Class, ApiNotificationFactory> apiNotificationFactory() {
        Map<Class, ApiNotificationFactory> factories = new HashMap<>();

        factories.put(APIChangeResourceOwnerMsg.class, new ApiNotificationFactory() {
            @Override
            public ApiNotification createApiNotification(APIMessage msg) {
                APIChangeResourceOwnerMsg cmsg = (APIChangeResourceOwnerMsg) msg;

                return new ApiNotification() {
                    String originAccountUuid;
                    String resourceType;

                    @Override
                    public void before() {
                        Tuple tuple = Q.New(AccountResourceRefVO.class)
                                .select(AccountResourceRefVO_.accountUuid, AccountResourceRefVO_.resourceType)
                                .eq(AccountResourceRefVO_.resourceUuid, cmsg.getResourceUuid()).findTuple();

                        originAccountUuid = tuple.get(0, String.class);
                        resourceType = tuple.get(1, String.class);
                    }

                    @Override
                    public void after(APIEvent evt) {
                        ntfy("changing the ownership to the account[uuid:%s]", cmsg.getAccountUuid())
                                .resource(cmsg.getResourceUuid(), resourceType)
                                .context("srcAccountUuid", originAccountUuid)
                                .context("dstAccountUuid", cmsg.getAccountUuid())
                                .messageAndEvent(cmsg, evt)
                                .done();

                        ntfy("transferring a resource[type: %s, uuid: %s] to the account[uuid:%s]", resourceType, cmsg.getResourceUuid(), cmsg.getAccountUuid())
                                .resource(originAccountUuid, AccountVO.class.getSimpleName())
                                .context("dstAccountUuid", cmsg.getAccountUuid())
                                .context("resourceUuid", cmsg.getResourceUuid())
                                .context("resourceType", resourceType)
                                .messageAndEvent(cmsg, evt)
                                .done();

                        ntfy("receiving a resource[type: %s, uuid: %s] from the account[uuid:%s]", resourceType, cmsg.getResourceUuid(), originAccountUuid)
                                .resource(originAccountUuid, AccountVO.class.getSimpleName())
                                .context("srcAccountUuid", cmsg.getAccountUuid())
                                .context("resourceUuid", cmsg.getResourceUuid())
                                .context("resourceType", resourceType)
                                .messageAndEvent(cmsg, evt)
                                .done();
                    }
                };
            }
        });

        // TODO: handle APIRevokeResourceSharingMsg
        // TODO: handle APIShareResourceMsg

        return factories;
    }

    class AccountCheckField {
        Field field;
        APIParam param;
    }

    class MessageAction {
        boolean adminOnly;
        List<String> actions;
        String category;
        boolean accountOnly;
        List<AccountCheckField> accountCheckFields;
        boolean accountControl;
    }

    private Map<Class, MessageAction> actions = new HashMap<>();
    private Future<Void> expiredSessionCollector;

    @Override
    @MessageSafe
    public void handleMessage(Message msg) {
        if (msg instanceof AccountMessage) {
            passThrough((AccountMessage) msg);
        } else if (msg instanceof APIMessage) {
            handleApiMessage((APIMessage) msg);
        } else {
            handleLocalMessage(msg);
        }
    }

    private void handleLocalMessage(Message msg) {
        if (msg instanceof GenerateMessageIdentityCategoryMsg) {
            handle((GenerateMessageIdentityCategoryMsg) msg);
        } else {
            bus.dealWithUnknownMessage(msg);
        }
    }

    @Override
    public Map<Class, List<Quota>> getMessageQuotaMap() {
        return messageQuotaMap;
    }

    @Override
    public List<Quota> getQuotas() {
        return definedQuotas;
    }

    @Override
    @Transactional
    public AccountResourceRefInventory changeResourceOwner(String resourceUuid, String newOwnerUuid) {
        String sql = "select ref from AccountResourceRefVO ref where ref.resourceUuid = :resUuid";
        TypedQuery<AccountResourceRefVO> q = dbf.getEntityManager().createQuery(sql, AccountResourceRefVO.class);
        q.setParameter("resUuid", resourceUuid);
        List<AccountResourceRefVO> refs = q.getResultList();
        if (refs.isEmpty()) {
            throw new OperationFailureException(argerr("cannot find the resource[uuid:%s]; wrong resourceUuid or the resource is admin resource",
                            resourceUuid));
        }

        AccountResourceRefVO ref = refs.get(0);
        final AccountResourceRefInventory origin = AccountResourceRefInventory.valueOf(ref);

        for (ResourceOwnerPreChangeExtensionPoint ext : pluginRgty.getExtensionList(ResourceOwnerPreChangeExtensionPoint.class)) {
            ext.resourceOwnerPreChange(origin, newOwnerUuid);
        }

        ref.setAccountUuid(newOwnerUuid);
        ref.setOwnerAccountUuid(newOwnerUuid);
        ref = dbf.getEntityManager().merge(ref);

        CollectionUtils.safeForEach(pluginRgty.getExtensionList(ResourceOwnerAfterChangeExtensionPoint.class),
                new ForEachFunction<ResourceOwnerAfterChangeExtensionPoint>() {
                    @Override
                    public void run(ResourceOwnerAfterChangeExtensionPoint ext) {
                        ext.resourceOwnerAfterChange(origin, newOwnerUuid);
                    }
                });

        return AccountResourceRefInventory.valueOf(ref);
    }

    @Override
    public void checkApiMessagePermission(APIMessage msg) {
        new Auth().check(msg);
    }

    @Override
    public boolean isAdmin(SessionInventory session) {
        return AccountConstant.INITIAL_SYSTEM_ADMIN_UUID.equals(session.getAccountUuid());
    }

    private void handle(GenerateMessageIdentityCategoryMsg msg) {
        List<String> adminMsgs = new ArrayList<>();
        List<String> userMsgs = new ArrayList<>();

        List<Class> apiMsgClasses = BeanUtils.scanClassByType("org.zstack", APIMessage.class);
        for (Class clz : apiMsgClasses) {
            if (APISearchMessage.class.isAssignableFrom(clz) || APIGetMessage.class.isAssignableFrom(clz)
                    || APIListMessage.class.isAssignableFrom(clz)) {
                continue;
            }

            String name = clz.getSimpleName().replaceAll("API", "").replaceAll("Msg", "");

            if (clz.isAnnotationPresent(Action.class)) {
                userMsgs.add(name);
            } else {
                adminMsgs.add(name);
            }
        }

        List<String> quotas = new ArrayList<>();
        for (List<Quota> quotaList : messageQuotaMap.values()) {
            for (Quota q : quotaList) {
                for (QuotaPair p : q.getQuotaPairs()) {
                    quotas.add(String.format("%s        %s", p.getName(), p.getValue()));
                }
            }
        }

        List<String> as = new ArrayList<>();
        for (Map.Entry<Class, MessageAction> e : actions.entrySet()) {
            Class api = e.getKey();
            MessageAction a = e.getValue();
            if (a.adminOnly || a.accountOnly) {
                continue;
            }

            String name = api.getSimpleName().replaceAll("API", "").replaceAll("Msg", "");
            StringBuilder sb = new StringBuilder();
            sb.append(String.format("%s: ", name));
            sb.append(StringUtils.join(a.actions, ", "));
            sb.append("\n");
            as.add(sb.toString());
        }

        try {
            String folder = PathUtil.join(System.getProperty("user.home"), "zstack-identity");
            FileUtils.deleteDirectory(new File(folder));

            new File(folder).mkdirs();

            String userMsgsPath = PathUtil.join(folder, "non-admin-api.txt");
            FileUtils.writeStringToFile(new File(userMsgsPath), StringUtils.join(userMsgs, "\n"));
            String adminMsgsPath = PathUtil.join(folder, "admin-api.txt");
            FileUtils.writeStringToFile(new File(adminMsgsPath), StringUtils.join(adminMsgs, "\n"));
            String quotaPath = PathUtil.join(folder, "quota.txt");
            FileUtils.writeStringToFile(new File(quotaPath), StringUtils.join(quotas, "\n"));
            String apiIdentityPath = PathUtil.join(folder, "api-identity.txt");
            FileUtils.writeStringToFile(new File(apiIdentityPath), StringUtils.join(as, "\n"));
            bus.reply(msg, new GenerateMessageIdentityCategoryReply());
        } catch (Exception e) {
            throw new CloudRuntimeException(e);
        }
    }

    private void passThrough(AccountMessage msg) {
        AccountVO vo = dbf.findByUuid(msg.getAccountUuid(), AccountVO.class);
        if (vo == null) {
            String err = String.format("unable to find account[uuid=%s]", msg.getAccountUuid());
            bus.replyErrorByMessageType((Message) msg, errf.instantiateErrorCode(SysErrors.RESOURCE_NOT_FOUND, err));
            return;
        }

        AccountBase base = new AccountBase(vo);
        base.handleMessage((Message) msg);
    }

    private void handleApiMessage(APIMessage msg) {
        if (msg instanceof APICreateAccountMsg) {
            handle((APICreateAccountMsg) msg);
        } else if (msg instanceof APIListAccountMsg) {
            handle((APIListAccountMsg) msg);
        } else if (msg instanceof APIListUserMsg) {
            handle((APIListUserMsg) msg);
        } else if (msg instanceof APIListPolicyMsg) {
            handle((APIListPolicyMsg) msg);
        } else if (msg instanceof APILogInByAccountMsg) {
            handle((APILogInByAccountMsg) msg);
        } else if (msg instanceof APILogInByUserMsg) {
            handle((APILogInByUserMsg) msg);
        } else if (msg instanceof APILogOutMsg) {
            handle((APILogOutMsg) msg);
        } else if (msg instanceof APIValidateSessionMsg) {
            handle((APIValidateSessionMsg) msg);
        } else if (msg instanceof APICheckApiPermissionMsg) {
            handle((APICheckApiPermissionMsg) msg);
        } else if (msg instanceof APIGetResourceAccountMsg) {
            handle((APIGetResourceAccountMsg) msg);
        } else if (msg instanceof APIChangeResourceOwnerMsg) {
            handle((APIChangeResourceOwnerMsg) msg);
        } else if (msg instanceof APIGetResourceNamesMsg) {
            handle((APIGetResourceNamesMsg) msg);
        } else if (msg instanceof APIIsOpensourceVersionMsg) {
            handle((APIIsOpensourceVersionMsg) msg);
        } else {
            bus.dealWithUnknownMessage(msg);
        }
    }

    private void handle(APIIsOpensourceVersionMsg msg) {
        APIIsOpensourceVersionReply reply = new APIIsOpensourceVersionReply();
        reply.setOpensource(true);
        bus.reply(msg, reply);
    }

    private void handle(APIGetResourceNamesMsg msg) {
        List<ResourceInventory> invs = new SQLBatchWithReturn<List<ResourceInventory>>() {
            @Override
            protected List<ResourceInventory> scripts() {
                Query q = dbf.getEntityManager().createNativeQuery("select uuid, resourceName, resourceType from ResourceVO where uuid in (:uuids)");
                q.setParameter("uuids", msg.getUuids());
                List<Object[]> objs = q.getResultList();

                List<ResourceVO> vos = objs.stream().map(ResourceVO::new).collect(Collectors.toList());

                return ResourceInventory.valueOf(vos);
            }
        }.execute();

        APIGetResourceNamesReply reply = new APIGetResourceNamesReply();
        reply.setInventories(invs);
        bus.reply(msg, reply);
    }

    private void handle(final APIChangeResourceOwnerMsg msg) {
        APIChangeResourceOwnerEvent evt = new APIChangeResourceOwnerEvent(msg.getId());
        evt.setInventory(changeResourceOwner(msg.getResourceUuid(), msg.getAccountUuid()));
        bus.publish(evt);
    }

    @Transactional(readOnly = true)
    private void handle(APIGetResourceAccountMsg msg) {
        String sql = "select a, ref.resourceUuid" +
                " from AccountResourceRefVO ref, AccountVO a" +
                " where a.uuid = ref.accountUuid" +
                " and ref.resourceUuid in (:uuids)";
        TypedQuery<Tuple> q = dbf.getEntityManager().createQuery(sql, Tuple.class);
        q.setParameter("uuids", msg.getResourceUuids());
        List<Tuple> tuples = q.getResultList();
        Map<String, AccountInventory> ret = new HashMap<>();
        for (Tuple t : tuples) {
            String resUuid = t.get(1, String.class);
            AccountVO vo = t.get(0, AccountVO.class);
            ret.put(resUuid, AccountInventory.valueOf(vo));
        }

        AccountVO admin = dbf.findByUuid(AccountConstant.INITIAL_SYSTEM_ADMIN_UUID, AccountVO.class);
        AccountInventory adminInv = AccountInventory.valueOf(admin);
        for (String resUuid : msg.getResourceUuids()) {
            if (!ret.containsKey(resUuid)) {
                ret.put(resUuid, adminInv);
            }
        }

        APIGetResourceAccountReply reply = new APIGetResourceAccountReply();
        reply.setInventories(ret);
        bus.reply(msg, reply);
    }

    private void handle(APICheckApiPermissionMsg msg) {
        if (msg.getUserUuid() != null) {
            SimpleQuery<AccountVO> q = dbf.createQuery(AccountVO.class);
            q.add(AccountVO_.uuid, Op.EQ, msg.getSession().getAccountUuid());
            q.add(AccountVO_.type, Op.EQ, AccountType.SystemAdmin);
            boolean isAdmin = q.isExists();

            SimpleQuery<UserVO> uq = dbf.createQuery(UserVO.class);
            uq.add(UserVO_.accountUuid, Op.EQ, msg.getSession().getAccountUuid());
            uq.add(UserVO_.uuid, Op.EQ, msg.getUserUuid());
            boolean isMine = uq.isExists();

            if (!isAdmin && !isMine) {
                throw new OperationFailureException(operr(
                        "the user specified by the userUuid[%s] does not belong to the current account, and the" +
                                " current account is not an admin account, so it has no permission to check the user's" +
                                "permissions", msg.getUserUuid()
                ));
            }
        }

        Map<String, String> ret = new HashMap<>();

        SessionInventory session = new SessionInventory();
        if (msg.getUserUuid() != null) {
            UserVO user = dbf.findByUuid(msg.getUserUuid(), UserVO.class);
            session.setAccountUuid(user.getAccountUuid());
            session.setUserUuid(user.getUuid());
        } else {
            session = msg.getSession();
        }

        for (String apiName : msg.getApiNames()) {
            try {
                Class apiClass = Class.forName(apiName);
                APIMessage api = (APIMessage) apiClass.newInstance();
                api.setSession(session);

                try {
                    new Auth().check(api);
                    ret.put(apiName, StatementEffect.Allow.toString());
                } catch (ApiMessageInterceptionException e) {
                    logger.debug(e.getMessage());
                    ret.put(apiName, StatementEffect.Deny.toString());
                }
            } catch (ClassNotFoundException e) {
                throw new OperationFailureException(argerr("%s is not an API", apiName));
            } catch (Exception e) {
                throw new CloudRuntimeException(e);
            }
        }

        APICheckApiPermissionReply reply = new APICheckApiPermissionReply();
        reply.setInventory(ret);
        bus.reply(msg, reply);
    }

    private void handle(APIValidateSessionMsg msg) {
        APIValidateSessionReply reply = new APIValidateSessionReply();

        SessionInventory s = sessions.get(msg.getSessionUuid());
        Timestamp current = dbf.getCurrentSqlTime();
        boolean valid = true;

        if (s != null) {
            if (current.after(s.getExpiredDate())) {
                valid = false;
                logOutSession(s.getUuid());
            }
        } else {
            SessionVO session = dbf.findByUuid(msg.getSessionUuid(), SessionVO.class);
            if (session != null && current.after(session.getExpiredDate())) {
                valid = false;
                logOutSession(session.getUuid());
            } else if (session == null) {
                valid = false;
            }
        }

        reply.setValidSession(valid);
        bus.reply(msg, reply);
    }

    private void handle(APILogOutMsg msg) {
        APILogOutReply reply = new APILogOutReply();
        logOutSession(msg.getSessionUuid());
        bus.reply(msg, reply);
    }

    private SessionInventory getSession(String accountUuid, String userUuid) {
        int maxLoginTimes = org.zstack.identity.IdentityGlobalConfig.MAX_CONCURRENT_SESSION.value(Integer.class);
        SimpleQuery<SessionVO> query = dbf.createQuery(SessionVO.class);
        query.add(SessionVO_.accountUuid, Op.EQ, accountUuid);
        query.add(SessionVO_.userUuid, Op.EQ, userUuid);
        long count = query.count();
        if (count >= maxLoginTimes) {
            String err = String.format("Login sessions hit limit of max allowed concurrent login sessions, max allowed: %s", maxLoginTimes);
            throw new BadCredentialsException(err);
        }

        int sessionTimeout = IdentityGlobalConfig.SESSION_TIMEOUT.value(Integer.class);
        SessionVO svo = new SessionVO();
        svo.setUuid(Platform.getUuid());
        svo.setAccountUuid(accountUuid);
        svo.setUserUuid(userUuid);
        long expiredTime = getCurrentSqlDate().getTime() + TimeUnit.SECONDS.toMillis(sessionTimeout);
        svo.setExpiredDate(new Timestamp(expiredTime));
        svo = dbf.persistAndRefresh(svo);
        SessionInventory session = SessionInventory.valueOf(svo);
        sessions.put(session.getUuid(), session);
        return session;
    }

    private void handle(APILogInByUserMsg msg) {
        APILogInReply reply = new APILogInReply();

        String accountUuid;
        if (msg.getAccountUuid() != null) {
            accountUuid = msg.getAccountUuid();
        } else {
            SimpleQuery<AccountVO> accountq = dbf.createQuery(AccountVO.class);
            accountq.select(AccountVO_.uuid);
            accountq.add(AccountVO_.name, Op.EQ, msg.getAccountName());
            accountUuid = accountq.findValue();
            if (accountUuid == null) {
                throw new OperationFailureException(argerr("account[%s] not found", msg.getAccountName()));
            }
        }

        SimpleQuery<UserVO> q = dbf.createQuery(UserVO.class);
        q.add(UserVO_.accountUuid, Op.EQ, accountUuid);
        q.add(UserVO_.password, Op.EQ, msg.getPassword());
        q.add(UserVO_.name, Op.EQ, msg.getUserName());
        UserVO user = q.find();

        if (user == null) {
            reply.setError(errf.instantiateErrorCode(IdentityErrors.AUTHENTICATION_ERROR,
                    "wrong username or password"
            ));
            bus.reply(msg, reply);
            return;
        }

        reply.setInventory(getSession(user.getAccountUuid(), user.getUuid()));
        bus.reply(msg, reply);
    }

    private void handle(APILogInByAccountMsg msg) {
        APILogInReply reply = new APILogInReply();

        SimpleQuery<AccountVO> q = dbf.createQuery(AccountVO.class);
        q.add(AccountVO_.name, Op.EQ, msg.getAccountName());
        q.add(AccountVO_.password, Op.EQ, msg.getPassword());
        AccountVO vo = q.find();
        if (vo == null) {
            reply.setError(errf.instantiateErrorCode(IdentityErrors.AUTHENTICATION_ERROR, "wrong account name or password"));
            bus.reply(msg, reply);
            return;
        }

        reply.setInventory(getSession(vo.getUuid(), vo.getUuid()));
        bus.reply(msg, reply);
    }

    private void handle(APIListPolicyMsg msg) {
        List<PolicyVO> vos = dl.listByApiMessage(msg, PolicyVO.class);
        List<PolicyInventory> invs = PolicyInventory.valueOf(vos);
        APIListPolicyReply reply = new APIListPolicyReply();
        reply.setInventories(invs);
        bus.reply(msg, reply);
    }

    private void handle(APIListUserMsg msg) {
        List<UserVO> vos = dl.listByApiMessage(msg, UserVO.class);
        List<UserInventory> invs = UserInventory.valueOf(vos);
        APIListUserReply reply = new APIListUserReply();
        reply.setInventories(invs);
        bus.reply(msg, reply);
    }

    private void handle(APIListAccountMsg msg) {
        List<AccountVO> vos = dl.listByApiMessage(msg, AccountVO.class);
        List<AccountInventory> invs = AccountInventory.valueOf(vos);
        APIListAccountReply reply = new APIListAccountReply();
        reply.setInventories(invs);
        bus.reply(msg, reply);
    }

    private void handle(APICreateAccountMsg msg) {
        final AccountInventory inv = new SQLBatchWithReturn<AccountInventory>() {
            @Override
            protected AccountInventory scripts() {
                AccountVO vo = new AccountVO();
                if (msg.getResourceUuid() != null) {
                    vo.setUuid(msg.getResourceUuid());
                } else {
                    vo.setUuid(Platform.getUuid());
                }
                vo.setName(msg.getName());
                vo.setDescription(msg.getDescription());
                vo.setPassword(msg.getPassword());
                vo.setType(msg.getType() != null ? AccountType.valueOf(msg.getType()) : AccountType.Normal);
                persist(vo);
                reload(vo);

                PolicyVO p = new PolicyVO();
                p.setUuid(Platform.getUuid());
                p.setAccountUuid(vo.getUuid());
                p.setName("DEFAULT-READ");
                Statement s = new Statement();
                s.setName(String.format("read-permission-for-account-%s", vo.getUuid()));
                s.setEffect(StatementEffect.Allow);
                s.addAction(".*:read");
                p.setData(JSONObjectUtil.toJsonString(list(s)));
                persist(p);
                reload(p);
                persist(AccountResourceRefVO.newOwn(vo.getUuid(), p.getUuid(), PolicyVO.class));

                p = new PolicyVO();
                p.setUuid(Platform.getUuid());
                p.setAccountUuid(vo.getUuid());
                p.setName("USER-RESET-PASSWORD");
                s = new Statement();
                s.setName(String.format("user-reset-password-%s", vo.getUuid()));
                s.setEffect(StatementEffect.Allow);
                s.addAction(String.format("%s:%s", AccountConstant.ACTION_CATEGORY, APIUpdateUserMsg.class.getSimpleName()));
                p.setData(JSONObjectUtil.toJsonString(list(s)));
                persist(p);
                reload(p);
                persist(AccountResourceRefVO.newOwn(vo.getUuid(), p.getUuid(), PolicyVO.class));

                List<Tuple> ts = Q.New(GlobalConfigVO.class).select(GlobalConfigVO_.name, GlobalConfigVO_.value)
                        .eq(GlobalConfigVO_.category, AccountConstant.QUOTA_GLOBAL_CONFIG_CATETORY).listTuple();

                for (Tuple t : ts) {
                    String rtype = t.get(0, String.class);
                    long quota = Long.valueOf(t.get(1, String.class));

                    QuotaVO qvo = new QuotaVO();
                    qvo.setUuid(Platform.getUuid());
                    qvo.setIdentityType(AccountVO.class.getSimpleName());
                    qvo.setIdentityUuid(vo.getUuid());
                    qvo.setName(rtype);
                    qvo.setValue(quota);
                    persist(qvo);
                    reload(qvo);
                    persist(AccountResourceRefVO.newOwn(vo.getUuid(), qvo.getUuid(), QuotaVO.class));
                }

                reload(vo);
                return AccountInventory.valueOf(vo);
            }
        }.execute();

        CollectionUtils.safeForEach(pluginRgty.getExtensionList(AfterCreateAccountExtensionPoint.class),
                arg -> arg.afterCreateAccount(inv));

        APICreateAccountEvent evt = new APICreateAccountEvent(msg.getId());
        evt.setInventory(inv);
        bus.publish(evt);
    }

    @Override
    public String getId() {
        return bus.makeLocalServiceId(AccountConstant.SERVICE_ID);
    }

    private void buildResourceTypes() throws ClassNotFoundException {
        resourceTypes = new ArrayList<>();
        for (String resrouceTypeName : resourceTypeForAccountRef) {
            Class<?> rs = Class.forName(resrouceTypeName);
            resourceTypes.add(rs);
        }
    }

    private void addResourceType() {
        for (AddtionalResourceTypeExtensionPoint ext: pluginRgty.getExtensionList(AddtionalResourceTypeExtensionPoint.class)) {
            List<String> list = ext.getAddtionalResourceType();
            if (list != null && list.size() > 0) {
                resourceTypeForAccountRef.addAll(list);
            }
        }
    }

    @Override
    public boolean start() {
        try {
            addResourceType();
            buildResourceTypes();
            buildActions();
            startExpiredSessionCollector();
            collectDefaultQuota();
            configureGlobalConfig();
            setupCanonicalEvents();
            updateResourceVONameOnEntityUpdate();

            for (ReportApiAccountControlExtensionPoint ext : pluginRgty.getExtensionList(ReportApiAccountControlExtensionPoint.class)) {
                List<Class> apis = ext.reportApiAccountControl();
                DebugUtils.Assert(apis != null, String.format("%s.reportApiAccountControl() returns null", ext.getClass()));
                accountApiControlInternal.addAll(apis);
            }

        } catch (Exception e) {
            throw new CloudRuntimeException(e);
        }
        return true;
    }

    private void updateResourceVONameOnEntityUpdate() {
        dbf.installEntityLifeCycleCallback(null, EntityEvent.PRE_UPDATE, (evt, o) -> {
            if (o instanceof ResourceVO) {
                ResourceVO rvo = (ResourceVO) o;
                if (rvo.hasNameField()) {
                    String name = rvo.getValueOfNameField();
                    if (name != null) {
                        rvo.setResourceName(name);
                    }
                }
            }
        });
    }

    private void setupCanonicalEvents() {
        evtf.on(IdentityCanonicalEvents.ACCOUNT_DELETED_PATH, new EventCallback() {
            @Override
            public void run(Map tokens, Object data) {
                AccountDeletedData d = (AccountDeletedData) data;

                SimpleQuery<SessionVO> q = dbf.createQuery(SessionVO.class);
                q.select(SessionVO_.uuid);
                q.add(SessionVO_.accountUuid, Op.EQ, d.getAccountUuid());
                List<String> suuids = q.listValue();

                for (String uuid : suuids) {
                    logOutSession(uuid);
                }

                if (!suuids.isEmpty()) {
                    logger.debug(String.format("successfully removed %s sessions for the deleted account[%s]", suuids.size(),
                            d.getAccountUuid()));
                }
            }
        });

        evtf.on(IdentityCanonicalEvents.USER_DELETED_PATH, new EventCallback() {
            @Override
            public void run(Map tokens, Object data) {
                UserDeletedData d = (UserDeletedData) data;

                SimpleQuery<SessionVO> q = dbf.createQuery(SessionVO.class);
                q.select(SessionVO_.uuid);
                q.add(SessionVO_.userUuid, Op.EQ, d.getUserUuid());
                List<String> suuids = q.listValue();

                for (String uuid : suuids) {
                    logOutSession(uuid);
                }

                if (!suuids.isEmpty()) {
                    logger.debug(String.format("successfully removed %s sessions for the deleted user[%s]", suuids.size(),
                            d.getUserUuid()));
                }
            }
        });
    }

    private void configureGlobalConfig() {
        String v = IdentityGlobalConfig.ACCOUNT_API_CONTROL.value();
        String[] classNames = v.split(",");
        for (String cn : classNames) {
            cn = cn.trim();
            try {
                Class clz = Class.forName(cn);
                accountApiControl.add(clz);
            } catch (ClassNotFoundException e) {
                throw new CloudRuntimeException(String.format("no API found for %s", cn));
            }
        }

        IdentityGlobalConfig.ACCOUNT_API_CONTROL.installValidateExtension(new GlobalConfigValidatorExtensionPoint() {
            @Override
            public void validateGlobalConfig(String category, String name, String oldValue, String newValue) throws GlobalConfigException {
                if (newValue.isEmpty()) {
                    return;
                }

                String[] classNames = newValue.split(",");
                for (String cn : classNames) {
                    cn = cn.trim();
                    try {
                        Class.forName(cn);
                    } catch (ClassNotFoundException e) {
                        throw new GlobalConfigException(String.format("no API found for %s", cn));
                    }
                }
            }
        });

        IdentityGlobalConfig.ACCOUNT_API_CONTROL.installUpdateExtension(new GlobalConfigUpdateExtensionPoint() {
            @Override
            public void updateGlobalConfig(GlobalConfig oldConfig, GlobalConfig newConfig) {
                accountApiControl.clear();

                if (newConfig.value().isEmpty()) {
                    return;
                }

                String[] classNames = newConfig.value().split(",");
                for (String name : classNames) {
                    try {
                        name = name.trim();
                        Class clz = Class.forName(name);
                        accountApiControl.add(clz);
                    } catch (ClassNotFoundException e) {
                        throw new CloudRuntimeException(e);
                    }
                }
            }
        });
    }

    private void collectDefaultQuota() {
        Map<String, Long> defaultQuota = new HashMap<>();

        // Add quota and quota checker
        for (ReportQuotaExtensionPoint ext : pluginRgty.getExtensionList(ReportQuotaExtensionPoint.class)) {
            List<Quota> quotas = ext.reportQuota();
            DebugUtils.Assert(quotas != null, String.format("%s.getQuotaPairs() returns null", ext.getClass()));

            definedQuotas.addAll(quotas);

            for (Quota quota : quotas) {
                DebugUtils.Assert(quota.getQuotaPairs() != null,
                        String.format("%s reports a quota containing a null quotaPairs", ext.getClass()));

                for (QuotaPair p : quota.getQuotaPairs()) {
                    if (defaultQuota.containsKey(p.getName())) {
                        throw new CloudRuntimeException(String.format("duplicate DefaultQuota[resourceType: %s] reported by %s", p.getName(), ext.getClass()));
                    }

                    defaultQuota.put(p.getName(), p.getValue());
                    nameQuotaMap.put(p.getName(), quota);
                }

                for (Class clz : quota.getMessagesNeedValidation()) {
                    if (messageQuotaMap.containsKey(clz)) {
                        messageQuotaMap.get(clz).add(quota);
                    } else {
                        ArrayList<Quota> quotaArrayList = new ArrayList<>();
                        quotaArrayList.add(quota);
                        messageQuotaMap.put(clz, quotaArrayList);
                    }

                }
            }
        }

        // Add additional quota checker to quota
        for (RegisterQuotaCheckerExtensionPoint ext : pluginRgty.getExtensionList(RegisterQuotaCheckerExtensionPoint.class)) {
            // Map<quota name,Set<QuotaValidator>>
            Map<String, Set<Quota.QuotaValidator>> m = ext.registerQuotaValidator();
            for (Map.Entry<String, Set<Quota.QuotaValidator>> entry : m.entrySet()) {
                Quota quota = nameQuotaMap.get(entry.getKey());
                quota.addQuotaValidators(entry.getValue());
                for (Quota.QuotaValidator q : entry.getValue()) {
                    for (Class clz : q.getMessagesNeedValidation()) {
                        if (messageQuotaMap.containsKey(clz)) {
                            messageQuotaMap.get(clz).add(quota);
                        } else {
                            ArrayList<Quota> quotaArrayList = new ArrayList<>();
                            quotaArrayList.add(quota);
                            messageQuotaMap.put(clz, quotaArrayList);
                        }
                    }
                }
            }

        }

        // complete default quota
        SimpleQuery<GlobalConfigVO> q = dbf.createQuery(GlobalConfigVO.class);
        q.select(GlobalConfigVO_.name);
        q.add(GlobalConfigVO_.category, Op.EQ, AccountConstant.QUOTA_GLOBAL_CONFIG_CATETORY);
        List<String> existingQuota = q.listValue();

        List<GlobalConfigVO> quotaConfigs = new ArrayList<>();
        for (Map.Entry<String, Long> e : defaultQuota.entrySet()) {
            String rtype = e.getKey();
            Long value = e.getValue();
            if (existingQuota.contains(rtype)) {
                continue;
            }

            GlobalConfigVO g = new GlobalConfigVO();
            g.setCategory(AccountConstant.QUOTA_GLOBAL_CONFIG_CATETORY);
            g.setDefaultValue(value.toString());
            g.setValue(g.getDefaultValue());
            g.setName(rtype);
            g.setDescription(String.format("default quota for %s", rtype));
            quotaConfigs.add(g);

            if (logger.isTraceEnabled()) {
                logger.trace(String.format("create default quota[name: %s, value: %s] global config", rtype, value));
            }
        }

        for (GlobalConfigVO vo : quotaConfigs) {
            gcf.createGlobalConfig(vo);
        }

        //
        repairAccountQuota(defaultQuota);
    }

    private void repairAccountQuota(Map<String, Long> defaultQuota) {
        SimpleQuery<AccountVO> queryAccounts = dbf.createQuery(AccountVO.class);
        queryAccounts.select(AccountVO_.uuid);
        queryAccounts.add(AccountVO_.type, Op.EQ, AccountType.Normal);
        List<String> normalAccounts = queryAccounts.listValue();

        List<QuotaVO> quotas = new ArrayList<>();
        for (String nA : normalAccounts) {
            SimpleQuery<QuotaVO> queryAccountQuotas = dbf.createQuery(QuotaVO.class);
            queryAccountQuotas.select(QuotaVO_.name);
            queryAccountQuotas.add(QuotaVO_.identityUuid, Op.EQ, nA);
            List<String> existingQuota = queryAccountQuotas.listValue();

            for (Map.Entry<String, Long> e : defaultQuota.entrySet()) {
                String rtype = e.getKey();
                Long value = e.getValue();
                if (existingQuota.contains(rtype)) {
                    continue;
                }

                QuotaVO q = new QuotaVO();
                q.setUuid(Platform.getUuid());
                q.setName(rtype);
                q.setIdentityUuid(nA);
                q.setIdentityType(AccountVO.class.getSimpleName());
                q.setValue(value);
                quotas.add(q);

                if (logger.isTraceEnabled()) {
                    logger.trace(String.format("create default quota[name: %s, value: %s] global config", rtype, value));
                }
            }
        }

        if (!quotas.isEmpty()) {
            dbf.persistCollection(quotas);
        }
    }

    private void startExpiredSessionCollector() {
        final int interval = IdentityGlobalConfig.SESSION_CLEANUP_INTERVAL.value(Integer.class);
        expiredSessionCollector = thdf.submitPeriodicTask(new PeriodicTask() {

            @Transactional
            private List<String> deleteExpiredSessions() {
                String sql = "select s.uuid from SessionVO s where CURRENT_TIMESTAMP  >= s.expiredDate";
                TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class);
                List<String> uuids = q.getResultList();
                if (!uuids.isEmpty()) {
                    String dsql = "delete from SessionVO s where s.uuid in :uuids";
                    Query dq = dbf.getEntityManager().createQuery(dsql);
                    dq.setParameter("uuids", uuids);
                    dq.executeUpdate();
                }
                return uuids;
            }

            @Override
            public void run() {
                List<String> uuids = deleteExpiredSessions();
                for (String uuid : uuids) {
                    sessions.remove(uuid);
                }
            }

            @Override
            public TimeUnit getTimeUnit() {
                return TimeUnit.SECONDS;
            }

            @Override
            public long getInterval() {
                return interval;
            }

            @Override
            public String getName() {
                return "ExpiredSessionCleanupThread";
            }

        });
    }

    private void buildActions() {
        List<Class> apiMsgClasses = BeanUtils.scanClassByType("org.zstack", APIMessage.class);
        for (Class clz : apiMsgClasses) {
            Action a = (Action) clz.getAnnotation(Action.class);
            if (a == null) {
                logger.debug(String.format("API message[%s] doesn't have annotation @Action, assume it's an admin only API", clz));
                MessageAction ma = new MessageAction();
                ma.adminOnly = true;
                ma.accountOnly = true;
                ma.accountControl = false;
                actions.put(clz, ma);
                continue;
            }

            MessageAction ma = new MessageAction();
            ma.accountOnly = a.accountOnly();
            ma.adminOnly = a.adminOnly();
            ma.category = a.category();
            ma.actions = new ArrayList<String>();
            ma.accountControl = a.accountControl();
            ma.accountCheckFields = new ArrayList<AccountCheckField>();
            for (String ac : a.names()) {
                ma.actions.add(String.format("%s:%s", ma.category, ac));
            }

            List<Field> allFields = FieldUtils.getAllFields(clz);
            for (Field f : allFields) {
                APIParam at = f.getAnnotation(APIParam.class);
                if (at == null || !at.checkAccount()) {
                    continue;
                }

                if (!String.class.isAssignableFrom(f.getType()) && !Collection.class.isAssignableFrom(f.getType())) {
                    throw new CloudRuntimeException(String.format("@APIParam of %s.%s has checkAccount = true, however," +
                                    " the type of the field is not String or Collection but %s. " +
                                    "This field must be a resource UUID or a collection(e.g. List) of UUIDs",
                            clz.getName(), f.getName(), f.getType()));
                }

                AccountCheckField af = new AccountCheckField();
                f.setAccessible(true);
                af.field = f;
                af.param = at;
                ma.accountCheckFields.add(af);
            }

            ma.actions.add(String.format("%s:%s", ma.category, clz.getName()));
            ma.actions.add(String.format("%s:%s", ma.category, clz.getSimpleName()));
            actions.put(clz, ma);
        }
    }

    @Override
    public boolean stop() {
        if (expiredSessionCollector != null) {
            expiredSessionCollector.cancel(true);
        }
        return true;
    }

    @Override
    public void prepareDbInitialValue() {
        try {
            SimpleQuery<AccountVO> q = dbf.createQuery(AccountVO.class);
            q.add(AccountVO_.name, Op.EQ, AccountConstant.INITIAL_SYSTEM_ADMIN_NAME);
            q.add(AccountVO_.type, Op.EQ, AccountType.SystemAdmin);
            if (!q.isExists()) {
                AccountVO vo = new AccountVO();
                vo.setUuid(AccountConstant.INITIAL_SYSTEM_ADMIN_UUID);
                vo.setName(AccountConstant.INITIAL_SYSTEM_ADMIN_NAME);
                vo.setPassword(AccountConstant.INITIAL_SYSTEM_ADMIN_PASSWORD);
                vo.setType(AccountType.SystemAdmin);
                dbf.persist(vo);
                logger.debug(String.format("Created initial system admin account[name:%s]", AccountConstant.INITIAL_SYSTEM_ADMIN_NAME));
            }
        } catch (Exception e) {
            throw new CloudRuntimeException("Unable to create default system admin account", e);
        }
    }

    @Override
    @Transactional
    public void createAccountResourceRef(String accountUuid, String resourceUuid, Class<?> resourceClass) {
        if (!resourceTypes.contains(resourceClass)) {
            throw new CloudRuntimeException(String.format("%s is not listed in resourceTypeForAccountRef of AccountManager.xml that is spring configuration. you forgot it???", resourceClass.getName()));
        }

        AccountResourceRefVO ref = AccountResourceRefVO.newOwn(accountUuid, resourceUuid, resourceClass);
        dbf.getEntityManager().persist(ref);
    }

    @Override
    public boolean isResourceHavingAccountReference(Class entityClass) {
        return resourceTypes.contains(entityClass);
    }

    @Override
    @Transactional(readOnly = true)
    public List<String> getResourceUuidsCanAccessByAccount(String accountUuid, Class resourceType) {
        String sql = "select a.type from AccountVO a where a.uuid = :auuid";
        TypedQuery<AccountType> q = dbf.getEntityManager().createQuery(sql, AccountType.class);
        q.setParameter("auuid", accountUuid);
        List<AccountType> types = q.getResultList();
        if (types.isEmpty()) {
            throw new OperationFailureException(argerr("cannot find the account[uuid:%s]", accountUuid));
        }

        AccountType atype = types.get(0);
        if (AccountType.SystemAdmin == atype) {
            return null;
        }

        sql = "select r.resourceUuid from AccountResourceRefVO r where r.accountUuid = :auuid" +
                " and r.resourceType = :rtype";
        TypedQuery<String> rq = dbf.getEntityManager().createQuery(sql, String.class);
        rq.setParameter("auuid", accountUuid);
        rq.setParameter("rtype", resourceType.getSimpleName());
        List<String> ownResourceUuids = rq.getResultList();

        sql = "select r.resourceUuid from SharedResourceVO r where" +
                " (r.toPublic = :toPublic or r.receiverAccountUuid = :auuid) and r.resourceType = :rtype";
        TypedQuery<String> srq = dbf.getEntityManager().createQuery(sql, String.class);
        srq.setParameter("toPublic", true);
        srq.setParameter("auuid", accountUuid);
        srq.setParameter("rtype", resourceType.getSimpleName());
        List<String> shared = srq.getResultList();
        shared.addAll(ownResourceUuids);

        return shared;
    }

    @Override
    public String getOwnerAccountUuidOfResource(String resourceUuid) {
        try {
            SimpleQuery<AccountResourceRefVO> q = dbf.createQuery(AccountResourceRefVO.class);
            q.select(AccountResourceRefVO_.ownerAccountUuid);
            q.add(AccountResourceRefVO_.resourceUuid, Op.EQ, resourceUuid);
            String ownerUuid = q.findValue();
            DebugUtils.Assert(ownerUuid != null, String.format("cannot find owner uuid for resource[uuid:%s]", resourceUuid));
            return ownerUuid;
        } catch (Exception e) {
            throw new CloudRuntimeException(e);
        }
    }

    @Override
    public List<Class> getEntityClassForSoftDeleteEntityExtension() {
        return resourceTypes;
    }

    @Override
    @Transactional
    public void postSoftDelete(Collection entityIds, Class entityClass) {
        String sql = "delete from AccountResourceRefVO ref where ref.resourceUuid in (:uuids) and ref.resourceType = :resourceType";
        Query q = dbf.getEntityManager().createQuery(sql);
        q.setParameter("uuids", entityIds);
        q.setParameter("resourceType", entityClass.getSimpleName());
        q.executeUpdate();
    }

    @Override
    public List<Class> getEntityClassForHardDeleteEntityExtension() {
        return resourceTypes;
    }

    @Override
    public void postHardDelete(Collection entityIds, Class entityClass) {
        if (resourceTypes.contains(entityClass)) {
            postSoftDelete(entityIds, entityClass);
        }
    }

    @Override
    public List<Class> getMessageClassToIntercept() {
        return null;
    }

    @Override
    public InterceptorPosition getPosition() {
        return InterceptorPosition.FRONT;
    }

    private void logOutSession(String sessionUuid) {
        SessionInventory session = sessions.get(sessionUuid);
        if (session == null) {
            SessionVO svo = dbf.findByUuid(sessionUuid, SessionVO.class);
            session = svo == null ? null : SessionInventory.valueOf(svo);
        }

        if (session == null) {
            return;
        }

        final SessionInventory finalSession = session;
        CollectionUtils.safeForEach(pluginRgty.getExtensionList(SessionLogoutExtensionPoint.class),
                new ForEachFunction<SessionLogoutExtensionPoint>() {
                    @Override
                    public void run(SessionLogoutExtensionPoint ext) {
                        ext.sessionLogout(finalSession);
                    }
                });

        sessions.remove(sessionUuid);
        dbf.removeByPrimaryKey(sessionUuid, SessionVO.class);
    }

    @Transactional(readOnly = true)
    private Timestamp getCurrentSqlDate() {
        Query query = dbf.getEntityManager().createNativeQuery("select current_timestamp()");
        return (Timestamp) query.getSingleResult();
    }

    class Auth {
        APIMessage msg;
        SessionInventory session;
        MessageAction action;
        String username;

        void validate(APIMessage msg) {
            this.msg = msg;
            if (msg.getClass().isAnnotationPresent(SuppressCredentialCheck.class)) {
                return;
            }

            action = actions.get(msg.getClass());

            sessionCheck();
            policyCheck();

            msg.setSession(session);
        }

        void check(APIMessage msg) {
            this.msg = msg;
            if (msg.getClass().isAnnotationPresent(SuppressCredentialCheck.class)) {
                return;
            }

            DebugUtils.Assert(msg.getSession() != null, "session cannot be null");
            session = msg.getSession();

            action = actions.get(msg.getClass());
            policyCheck();
        }

        private void accountFieldCheck() throws IllegalAccessException {
            Set resourceUuids = new HashSet();
            Set operationTargetResourceUuids = new HashSet();

            for (AccountCheckField af : action.accountCheckFields) {
                Object value = af.field.get(msg);
                if (value == null) {
                    continue;
                }

                if (String.class.isAssignableFrom(af.field.getType())) {
                    if (af.param.operationTarget()) {
                        operationTargetResourceUuids.add(value);
                    } else {
                        resourceUuids.add(value);
                    }
                } else if (Collection.class.isAssignableFrom(af.field.getType())) {
                    if (af.param.operationTarget()) {
                        operationTargetResourceUuids.addAll((Collection) value);
                    } else {
                        resourceUuids.addAll((Collection) value);
                    }
                }
            }

            if (resourceUuids.isEmpty() && operationTargetResourceUuids.isEmpty()) {
                return;
            }

            // if a resource uuid represents an operation target, it cannot be bypassed by
            // the shared resources, as we don't support roles for cross-account sharing.
            if (!resourceUuids.isEmpty()) {
                SimpleQuery<SharedResourceVO> sq = dbf.createQuery(SharedResourceVO.class);
                sq.select(SharedResourceVO_.receiverAccountUuid, SharedResourceVO_.toPublic, SharedResourceVO_.resourceUuid);
                sq.add(SharedResourceVO_.resourceUuid, Op.IN, resourceUuids);
                List<Tuple> ts = sq.listTuple();
                for (Tuple t : ts) {
                    String ruuid = t.get(0, String.class);
                    Boolean toPublic = t.get(1, Boolean.class);
                    String resUuid = t.get(2, String.class);
                    if (toPublic || session.getAccountUuid().equals(ruuid)) {
                        // this resource is shared to the account
                        resourceUuids.remove(resUuid);
                    }
                }
            }

            resourceUuids.addAll(operationTargetResourceUuids);
            if (resourceUuids.isEmpty()) {
                return;
            }

            List<Tuple> ts = SQL.New(
                    " select avo.name ,arrf.accountUuid ,arrf.resourceUuid ,arrf.resourceType " +
                            "from AccountResourceRefVO arrf ,AccountVO avo " +
                            "where arrf.resourceUuid in (:resourceUuids) and avo.uuid = arrf.accountUuid",Tuple.class)
                    .param("resourceUuids",resourceUuids).list();

            for (Tuple t : ts) {
                String resourceOwnerName = t.get(0, String.class);
                String resourceOwnerAccountUuid = t.get(1, String.class);
                String resourceUuid = t.get(2, String.class);
                String resourceType = t.get(3, String.class);
                if (!session.getAccountUuid().equals(resourceOwnerAccountUuid)) {
                    throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.PERMISSION_DENIED,
                            String.format("operation denied. The resource[uuid: %s, type: %s,ownerAccountName:%s, ownerAccountUuid:%s] doesn't belong to the account[uuid: %s]",
                                    resourceUuid, resourceType, resourceOwnerName, resourceOwnerAccountUuid, session.getAccountUuid())
                    ));
                } else {
                    if (logger.isTraceEnabled()) {
                        logger.trace(String.format("account-check pass. The resource[uuid: %s, type: %s] belongs to the account[uuid: %s]",
                                resourceUuid, resourceType, session.getAccountUuid()));
                    }
                }
            }
        }

        private void useDecision(Decision d, boolean userPolicy) {
            String policyCategory = userPolicy ? "user policy" : "group policy";

            if (d.effect == StatementEffect.Allow) {
                logger.debug(String.format("API[name: %s, action: %s] is approved by a %s[name: %s, uuid: %s]," +
                                " statement[name: %s, action: %s]", msg.getClass().getSimpleName(), d.action,
                        policyCategory, d.policy.getName(), d.policy.getUuid(), d.statement.getName(), d.actionRule));
            } else {
                logger.debug(String.format("API[name: %s, action: %s] is denied by a %s[name: %s, uuid: %s]," +
                                " statement[name: %s, action: %s]", msg.getClass().getSimpleName(), d.action,
                        policyCategory, d.policy.getName(), d.policy.getUuid(), d.statement.getName(), d.actionRule));

                throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.PERMISSION_DENIED,
                        String.format("%s denied. user[name: %s, uuid: %s] is denied to execute API[%s]",
                                policyCategory, username, session.getUuid(), msg.getClass().getSimpleName())
                ));
            }
        }

        private void policyCheck() {
            if (new QuotaUtil().isAdminAccount(session.getAccountUuid())) {
                return;
            }

            if (action.adminOnly) {
                throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.PERMISSION_DENIED,
                        String.format("API[%s] is admin only", msg.getClass().getSimpleName())));
            }

            if (action.accountOnly && !session.isAccountSession()) {
                throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.PERMISSION_DENIED,
                        String.format("API[%s] can only be called by an account, the current session is a user session[user uuid:%s]",
                                msg.getClass().getSimpleName(), session.getUserUuid())
                ));
            }

            if (action.accountCheckFields != null && !action.accountCheckFields.isEmpty()) {
                try {
                    accountFieldCheck();
                } catch (ApiMessageInterceptionException ae) {
                    throw ae;
                } catch (Exception e) {
                    throw new CloudRuntimeException(e);
                }
            }

            if (action.accountControl) {
                boolean allow = false;
                for (Class clz : accountApiControl) {
                    if (clz.isAssignableFrom(msg.getClass())) {
                        allow = true;
                        break;
                    }
                }

                if (!allow) {
                    for (Class clz : accountApiControlInternal) {
                        if (clz.isAssignableFrom(msg.getClass())) {
                            allow = true;
                            break;
                        }
                    }
                }

                if (!allow) {
                    throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.PERMISSION_DENIED,
                            String.format("the API[%s] is not allowed for normal accounts", msg.getClass())
                    ));
                }
            }

            if (session.isAccountSession()) {
                return;
            }

            SimpleQuery<UserVO> uq = dbf.createQuery(UserVO.class);
            uq.select(UserVO_.name);
            uq.add(UserVO_.uuid, Op.EQ, session.getUserUuid());
            username = uq.findValue();

            List<PolicyInventory> userPolicies = getUserPolicies();
            Decision d = decide(userPolicies);
            if (d != null) {
                useDecision(d, true);
                return;
            }

            List<PolicyInventory> groupPolicies = getGroupPolicies();
            d = decide(groupPolicies);
            if (d != null) {
                useDecision(d, false);
                return;
            }

            throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.PERMISSION_DENIED,
                    String.format("user[name: %s, uuid: %s] has no policy set for this operation, API[%s] is denied by default. You may either create policies for this user" +
                            " or add the user into a group with polices set", username, session.getUserUuid(), msg.getClass().getSimpleName())
            ));
        }

        @Transactional(readOnly = true)
        private List<PolicyInventory> getGroupPolicies() {
            String sql = "select p" +
                    " from PolicyVO p, UserGroupUserRefVO ref, UserGroupPolicyRefVO gref" +
                    " where p.uuid = gref.policyUuid" +
                    " and gref.groupUuid = ref.groupUuid" +
                    " and ref.userUuid = :uuid";
            TypedQuery<PolicyVO> q = dbf.getEntityManager().createQuery(sql, PolicyVO.class);
            q.setParameter("uuid", session.getUserUuid());
            return PolicyInventory.valueOf(q.getResultList());
        }

        class Decision {
            PolicyInventory policy;
            String action;
            Statement statement;
            String actionRule;
            StatementEffect effect;
        }

        private Decision decide(List<PolicyInventory> userPolicies) {
            for (String a : action.actions) {
                for (PolicyInventory p : userPolicies) {
                    for (Statement s : p.getStatements()) {
                        for (String ac : s.getActions()) {
                            Pattern pattern = Pattern.compile(ac);
                            Matcher m = pattern.matcher(a);
                            boolean ret = m.matches();
                            if (ret) {
                                Decision d = new Decision();
                                d.policy = p;
                                d.action = a;
                                d.statement = s;
                                d.actionRule = ac;
                                d.effect = s.getEffect();
                                return d;
                            }

                            if (logger.isTraceEnabled()) {
                                logger.trace(String.format("API[name: %s, action: %s] is not matched by policy[name: %s, uuid: %s" +
                                                ", statement[name: %s, action: %s, effect: %s]", msg.getClass().getSimpleName(),
                                        a, p.getName(), p.getUuid(), s.getName(), ac, s.getEffect()));
                            }
                        }
                    }
                }
            }

            return null;
        }

        @Transactional(readOnly = true)
        private List<PolicyInventory> getUserPolicies() {
            String sql = "select p from PolicyVO p, UserPolicyRefVO ref where ref.userUuid = :uuid and ref.policyUuid = p.uuid";
            TypedQuery<PolicyVO> q = dbf.getEntityManager().createQuery(sql, PolicyVO.class);
            q.setParameter("uuid", session.getUserUuid());
            return PolicyInventory.valueOf(q.getResultList());
        }

        private void sessionCheck() {
            if (msg.getSession() == null) {
                throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.INVALID_SESSION,
                        String.format("session of message[%s] is null", msg.getMessageName())));
            }

            if (msg.getSession().getUuid() == null) {
                throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.INVALID_SESSION,
                        "session uuid is null"));
            }

            SessionInventory session = sessions.get(msg.getSession().getUuid());
            if (session == null) {
                SessionVO svo = dbf.findByUuid(msg.getSession().getUuid(), SessionVO.class);
                if (svo == null) {
                    throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.INVALID_SESSION,
                            "Session expired"));
                }
                session = SessionInventory.valueOf(svo);
                sessions.put(session.getUuid(), session);
            }

            Timestamp curr = getCurrentSqlDate();
            if (curr.after(session.getExpiredDate())) {
                logger.debug(String.format("session expired[%s < %s] for account[uuid:%s]", curr,
                        session.getExpiredDate(), session.getAccountUuid()));
                logOutSession(session.getUuid());
                throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.INVALID_SESSION, "Session expired"));
            }

            this.session = session;
        }
    }

    @Override
    public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionException {
        new Auth().validate(msg);

        if (msg instanceof APIUpdateAccountMsg) {
            validate((APIUpdateAccountMsg) msg);
        } else if (msg instanceof APICreatePolicyMsg) {
            validate((APICreatePolicyMsg) msg);
        } else if (msg instanceof APIAddUserToGroupMsg) {
            validate((APIAddUserToGroupMsg) msg);
        } else if (msg instanceof APIAttachPolicyToUserGroupMsg) {
            validate((APIAttachPolicyToUserGroupMsg) msg);
        } else if (msg instanceof APIAttachPolicyToUserMsg) {
            validate((APIAttachPolicyToUserMsg) msg);
        } else if (msg instanceof APIDetachPolicyFromUserGroupMsg) {
            validate((APIDetachPolicyFromUserGroupMsg) msg);
        } else if (msg instanceof APIDetachPolicyFromUserMsg) {
            validate((APIDetachPolicyFromUserMsg) msg);
        } else if (msg instanceof APIShareResourceMsg) {
            validate((APIShareResourceMsg) msg);
        } else if (msg instanceof APIRevokeResourceSharingMsg) {
            validate((APIRevokeResourceSharingMsg) msg);
        } else if (msg instanceof APIUpdateUserMsg) {
            validate((APIUpdateUserMsg) msg);
        } else if (msg instanceof APIDeleteAccountMsg) {
            validate((APIDeleteAccountMsg) msg);
        } else if (msg instanceof APICreateAccountMsg) {
            validate((APICreateAccountMsg) msg);
        } else if (msg instanceof APICreateUserMsg) {
            validate((APICreateUserMsg) msg);
        } else if (msg instanceof APICreateUserGroupMsg) {
            validate((APICreateUserGroupMsg) msg);
        } else if (msg instanceof APILogInByUserMsg) {
            validate((APILogInByUserMsg) msg);
        } else if (msg instanceof APIGetAccountQuotaUsageMsg) {
            validate((APIGetAccountQuotaUsageMsg) msg);
        } else if (msg instanceof APIChangeResourceOwnerMsg) {
            validate((APIChangeResourceOwnerMsg) msg);
        }

        setServiceId(msg);

        return msg;
    }

    private void checkQuotaForChangeResourceOwner(APIChangeResourceOwnerMsg msg) {
        String currentAccountUuid = msg.getSession().getAccountUuid();
        String resourceTargetOwnerAccountUuid = msg.getAccountUuid();
        if (new QuotaUtil().isAdminAccount(resourceTargetOwnerAccountUuid)) {
            return;
        }
        // check if change resource owner to self
        SimpleQuery<AccountResourceRefVO> queryAccResRefVO = dbf.createQuery(AccountResourceRefVO.class);
        queryAccResRefVO.add(AccountResourceRefVO_.resourceUuid, Op.EQ, msg.getResourceUuid());
        AccountResourceRefVO accResRefVO = queryAccResRefVO.find();
        String resourceOriginalOwnerAccountUuid = accResRefVO.getOwnerAccountUuid();
        if (resourceTargetOwnerAccountUuid.equals(resourceOriginalOwnerAccountUuid)) {
            throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.QUOTA_INVALID_OP,
                    String.format("Invalid ChangeResourceOwner operation." +
                                    "Original owner is the same as target owner." +
                                    "Current account is [uuid: %s]." +
                                    "The resource target owner account[uuid: %s]." +
                                    "The resource original owner account[uuid:%s].",
                            currentAccountUuid, resourceTargetOwnerAccountUuid, resourceOriginalOwnerAccountUuid)
            ));
        }
        // check quota
        Map<String, QuotaPair> pairs = new QuotaUtil().makeQuotaPairs(msg.getAccountUuid());
        for (Quota quota : messageQuotaMap.get(APIChangeResourceOwnerMsg.class)) {
            quota.getOperator().checkQuota(msg, pairs);
        }
    }

    private void validate(APIChangeResourceOwnerMsg msg) {
        checkQuotaForChangeResourceOwner(msg);
    }

    private void validate(APIGetAccountQuotaUsageMsg msg) {
        if (msg.getUuid() == null) {
            msg.setUuid(msg.getSession().getAccountUuid());
        }
    }

    private void validate(APILogInByUserMsg msg) {
        if (msg.getAccountName() == null && msg.getAccountUuid() == null) {
            throw new ApiMessageInterceptionException(argerr(
                    "accountName and accountUuid cannot both be null, you must specify at least one"
            ));
        }
    }

    private void validate(APICreateUserGroupMsg msg) {
        SimpleQuery<UserGroupVO> q = dbf.createQuery(UserGroupVO.class);
        q.add(UserGroupVO_.accountUuid, Op.EQ, msg.getAccountUuid());
        q.add(UserGroupVO_.name, Op.EQ, msg.getName());
        if (q.isExists()) {
            throw new ApiMessageInterceptionException(argerr("unable to create a group. A group called %s is already under the account[uuid:%s]",
                            msg.getName(), msg.getAccountUuid()));
        }
    }

    private void validate(APICreateUserMsg msg) {
        SimpleQuery<UserVO> q = dbf.createQuery(UserVO.class);
        q.add(UserVO_.accountUuid, Op.EQ, msg.getAccountUuid());
        q.add(UserVO_.name, Op.EQ, msg.getName());
        if (q.isExists()) {
            throw new ApiMessageInterceptionException(argerr("unable to create a user. A user called %s is already under the account[uuid:%s]",
                            msg.getName(), msg.getAccountUuid()));
        }
    }

    private void validate(APICreateAccountMsg msg) {
        SimpleQuery<AccountVO> q = dbf.createQuery(AccountVO.class);
        q.add(AccountVO_.name, Op.EQ, msg.getName());
        if (q.isExists()) {
            throw new ApiMessageInterceptionException(argerr("unable to create an account. An account already called %s", msg.getName()));
        }
    }

    private void validate(APIDeleteAccountMsg msg) {
        if (new QuotaUtil().isAdminAccount(msg.getUuid())) {
            throw new ApiMessageInterceptionException(argerr(
                    "unable to delete an account. The account is an admin account"
            ));
        }
    }

    private void validate(APIUpdateUserMsg msg) {
        if (msg.getUuid() == null && msg.getSession().isAccountSession()) {
            throw new ApiMessageInterceptionException(argerr(
                    "the current session is an account session. You need to specify the field 'uuid' of the user" +
                            " you want to update"
            ));
        }

        if (msg.getSession().isAccountSession()) {
            return;
        }

        if (msg.getUuid() != null && !msg.getSession().getUserUuid().equals(msg.getUuid())) {
            throw new ApiMessageInterceptionException(argerr("your are login as a user, you cannot another user[uuid:%s]", msg.getUuid()));
        }

        msg.setUuid(msg.getSession().getUserUuid());
    }

    private void validate(APIRevokeResourceSharingMsg msg) {
        if (!msg.isAll() && (msg.getAccountUuids() == null || msg.getAccountUuids().isEmpty())) {
            throw new ApiMessageInterceptionException(argerr(
                    "all is set to false, accountUuids cannot be null or empty"
            ));
        }
    }

    private void validate(APIShareResourceMsg msg) {
        if (!msg.isToPublic() && (msg.getAccountUuids() == null || msg.getAccountUuids().isEmpty())) {
            throw new ApiMessageInterceptionException(argerr(
                    "toPublic is set to false, accountUuids cannot be null or empty"
            ));
        }
    }

    private void validate(APIDetachPolicyFromUserMsg msg) {
        PolicyVO policy = dbf.findByUuid(msg.getPolicyUuid(), PolicyVO.class);
        UserVO user = dbf.findByUuid(msg.getUserUuid(), UserVO.class);
        if (!policy.getAccountUuid().equals(msg.getAccountUuid())) {
            throw new ApiMessageInterceptionException(argerr("policy[name: %s, uuid: %s] doesn't belong to the account[uuid: %s]",
                            policy.getName(), policy.getUuid(), msg.getSession().getAccountUuid()));
        }
        if (!user.getAccountUuid().equals(msg.getAccountUuid())) {
            throw new ApiMessageInterceptionException(argerr("user[name: %s, uuid: %s] doesn't belong to the account[uuid: %s]",
                            user.getName(), user.getUuid(), msg.getSession().getAccountUuid()));
        }
    }

    private void validate(APIDetachPolicyFromUserGroupMsg msg) {
        PolicyVO policy = dbf.findByUuid(msg.getPolicyUuid(), PolicyVO.class);
        UserGroupVO group = dbf.findByUuid(msg.getGroupUuid(), UserGroupVO.class);
        if (!policy.getAccountUuid().equals(msg.getAccountUuid())) {
            throw new ApiMessageInterceptionException(argerr("policy[name: %s, uuid: %s] doesn't belong to the account[uuid: %s]",
                            policy.getName(), policy.getUuid(), msg.getSession().getAccountUuid()));
        }
        if (!group.getAccountUuid().equals(msg.getAccountUuid())) {
            throw new ApiMessageInterceptionException(argerr("group[name: %s, uuid: %s] doesn't belong to the account[uuid: %s]",
                            group.getName(), group.getUuid(), msg.getSession().getAccountUuid()));
        }
    }

    private void validate(APIAttachPolicyToUserMsg msg) {
        PolicyVO policy = dbf.findByUuid(msg.getPolicyUuid(), PolicyVO.class);
        UserVO user = dbf.findByUuid(msg.getUserUuid(), UserVO.class);
        if (!policy.getAccountUuid().equals(msg.getAccountUuid())) {
            throw new ApiMessageInterceptionException(argerr("policy[name: %s, uuid: %s] doesn't belong to the account[uuid: %s]",
                            policy.getName(), policy.getUuid(), msg.getSession().getAccountUuid()));
        }
        if (!user.getAccountUuid().equals(msg.getAccountUuid())) {
            throw new ApiMessageInterceptionException(argerr("user[name: %s, uuid: %s] doesn't belong to the account[uuid: %s]",
                            user.getName(), user.getUuid(), msg.getSession().getAccountUuid()));
        }
    }

    private void validate(APIAttachPolicyToUserGroupMsg msg) {
        PolicyVO policy = dbf.findByUuid(msg.getPolicyUuid(), PolicyVO.class);
        UserGroupVO group = dbf.findByUuid(msg.getGroupUuid(), UserGroupVO.class);
        if (!policy.getAccountUuid().equals(msg.getAccountUuid())) {
            throw new ApiMessageInterceptionException(argerr("policy[name: %s, uuid: %s] doesn't belong to the account[uuid: %s]",
                            policy.getName(), policy.getUuid(), msg.getSession().getAccountUuid()));
        }

        if (!group.getAccountUuid().equals(msg.getAccountUuid())) {
            throw new ApiMessageInterceptionException(argerr("group[name: %s, uuid: %s] doesn't belong to the account[uuid: %s]",
                            group.getName(), group.getUuid(), msg.getSession().getAccountUuid()));
        }
    }

    private void validate(APIAddUserToGroupMsg msg) {
        UserVO user = dbf.findByUuid(msg.getUserUuid(), UserVO.class);
        UserGroupVO group = dbf.findByUuid(msg.getGroupUuid(), UserGroupVO.class);
        if (!user.getAccountUuid().equals(msg.getAccountUuid())) {
            throw new ApiMessageInterceptionException(argerr("user[name: %s, uuid: %s] doesn't belong to the account[uuid: %s]",
                            user.getName(), user.getUuid(), msg.getSession().getAccountUuid()));
        }
        if (!group.getAccountUuid().equals(msg.getSession().getAccountUuid())) {
            throw new ApiMessageInterceptionException(argerr("group[name: %s, uuid: %s] doesn't belong to the account[uuid: %s]",
                            group.getName(), group.getUuid(), msg.getSession().getAccountUuid()));
        }
    }

    private void validate(APICreatePolicyMsg msg) {
        for (Statement s : msg.getStatements()) {
            if (s.getEffect() == null) {
                throw new ApiMessageInterceptionException(argerr("a statement must have effect field. Invalid statement[%s]", JSONObjectUtil.toJsonString(s)));
            }
            if (s.getActions() == null) {
                throw new ApiMessageInterceptionException(argerr("a statement must have action field. Invalid statement[%s]", JSONObjectUtil.toJsonString(s)));
            }
            if (s.getActions().isEmpty()) {
                throw new ApiMessageInterceptionException(argerr("a statement must have a non-empty action field. Invalid statement[%s]",
                                JSONObjectUtil.toJsonString(s)));
            }
        }
    }

    private void validate(APIUpdateAccountMsg msg) {
        AccountVO a = dbf.findByUuid(msg.getSession().getAccountUuid(), AccountVO.class);
        if (msg.getUuid() == null) {
            msg.setUuid(msg.getSession().getAccountUuid());
        }

        if (a.getType() == AccountType.SystemAdmin) {
            if (msg.getName() != null && (msg.getUuid() == null || msg.getUuid().equals(AccountConstant.INITIAL_SYSTEM_ADMIN_UUID))) {
                throw new OperationFailureException(operr(
                        "the name of admin account cannot be updated"
                ));
            }

            return;
        }

        AccountVO account = dbf.findByUuid(msg.getUuid(), AccountVO.class);
        if (!account.getUuid().equals(a.getUuid())) {
            throw new OperationFailureException(operr("account[uuid: %s, name: %s] is a normal account, it cannot reset the password of another account[uuid: %s]",
                            account.getUuid(), account.getName(), msg.getUuid()));
        }
    }

    private void setServiceId(APIMessage msg) {
        if (msg instanceof AccountMessage) {
            AccountMessage amsg = (AccountMessage) msg;
            bus.makeTargetServiceIdByResourceUuid(msg, AccountConstant.SERVICE_ID, amsg.getAccountUuid());
        }
    }

    public void setResourceTypeForAccountRef(List<String> resourceTypeForAccountRef) {
        this.resourceTypeForAccountRef = resourceTypeForAccountRef;
    }
}


text:
import React from 'react';
import { Button, FieldGroup, ButtonGroup } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';

import TriggersForm from './TriggersForm';
import { useTranslation } from '../../contexts/TranslationContext';
import { useMethod } from '../../contexts/ServerContext';
import { useForm } from '../../hooks/useForm';
import { useRoute } from '../../contexts/RouterContext';
import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';

const NewTriggerPage = ({ onSave }) => {
	const dispatchToastMessage = useToastMessageDispatch();
	const t = useTranslation();

	const router = useRoute('omnichannel-triggers');

	const save = useMethod('livechat:saveTrigger');

	const { values, handlers } = useForm({
		name: '',
		description: '',
		enabled: true,
		runOnce: false,
		conditions: {
			name: 'page-url',
			value: '',
		},
		actions: {
			name: '',
			params: {
				sender: 'queue',
				msg: '',
				name: '',
			},
		},
	});

	const handleSave = useMutableCallback(async () => {
		try {
			const { actions: { params: { sender, msg, name } }, ...restValues } = values;
			await save({
				...restValues,
				conditions: [values.conditions],
				actions: [{
					name: 'send-message',
					params: {
						sender,
						msg,
						...sender === 'custom' && { name },
					},
				}],
			});
			dispatchToastMessage({ type: 'success', message: t('Saved') });
			onSave();
			router.push({});
		} catch (error) {
			dispatchToastMessage({ type: 'error', message: error });
		}
	});

	return 	<>
		<FieldGroup>
			<TriggersForm values={values} handlers={handlers}/>
		</FieldGroup>
		<ButtonGroup align='end'>
			<Button primary onClick={handleSave}>
				{t('Save')}
			</Button>
		</ButtonGroup>
	</>;
};

export default NewTriggerPage;

redpajama_set_name: RedPajamaGithub


text:
import { IAdminClient } from '../api/admin/adminclient';
import { IAgentClient } from '../api/agent/agentclient';
import { IAuthenticationClient } from '../api/authentication/authenticationclient';
import { ICustomClient } from '../api/custom/customclient';
import { IPatronClient } from '../api/patron/patronclient';
import { IRealTimeDataClient } from '../api/realtimedata/realtimedataclient';
import { IReportingClient } from '../api/reporting/reportingclient';
/**
 * Lake Nice client interface.
 */
export interface ILakeNiceClient {
    /**
     * On event.
     * @param {string} arg0 Event name.
     * @param {Object} arg1 Event arguments.
     */
    on(arg0: string, arg1: any): any;
    /**
     * get the AdminClient interface.
     * @return {IAdminClient}    The AdminClient.
    */
    getAdminClient(): IAdminClient;
    /**
     * get the AgentClient interface.
     * @return {IAgentClient}    The AgentClient.
    */
    getAgentClient(): IAgentClient;
    /**
     * get the AuthenticationClient interface.
     * @return {IAuthenticationClient}    The AuthenticationClient.
    */
    getAuthenticationClient(): IAuthenticationClient;
    /**
     * get the CustomClient interface.
     * @return {ICustomClient}    The CustomClient.
    */
    getCustomClient(): ICustomClient;
    /**
     * get the PatronClient interface.
     * @return {IPatronClient}    The PatronClient.
    */
    getPatronClient(): IPatronClient;
    /**
     * get the RealTimeDataClient interface.
     * @return {IRealTimeDataClient}    The RealTimeDataClient.
    */
    getRealTimeDataClient(): IRealTimeDataClient;
    /**
     * get the ReportingClient interface.
     * @return {IReportingClient}    The ReportingClient.
    */
    getReportingClient(): IReportingClient;
}
/**
 * Lake Nice client implementation.
 */
export declare class LakeNiceClient implements ILakeNiceClient {
    clientOptions: any;
    adminclient: IAdminClient;
    agentclient: IAgentClient;
    authenticationclient: IAuthenticationClient;
    customclient: ICustomClient;
    patronclient: IPatronClient;
    realtimedataclient: IRealTimeDataClient;
    reportingclient: IReportingClient;
    config: any;
    logger: any;
    /**
    * Lake Nice client api implementation.
    *
    * Lake Nice client controls the interaction between the
    * web service and client-side.
    *
    * @param {Object}   clientOptions  A collection of options.
    *
    * @example
    *  options = {
    *		debug: true,
    *		domainURIPath: "https://api-a32.nice-incontact.com/inContactAPI",
    *		baseURIPath: "/services/v15.0/",
    *		authorization: "Bearer [Token Value]",
    *       timeout: 10000, // default is '0' (0 seconds timeout)
    *  }
    */
    constructor(clientOptions: any);
    /**
     * On event.
     * @param {string} arg0 Event name.
     * @param {Object} arg1 Event arguments.
     */
    on(arg0: string, arg1: any): any;
    /**
     * get the AdminClient interface.
     * @return {IAdminClient}    The AdminClient.
    */
    getAdminClient(): IAdminClient;
    /**
     * get the AgentClient interface.
     * @return {IAgentClient}    The AgentClient.
    */
    getAgentClient(): IAgentClient;
    /**
     * get the AuthenticationClient interface.
     * @return {IAuthenticationClient}    The AuthenticationClient.
    */
    getAuthenticationClient(): IAuthenticationClient;
    /**
     * get the CustomClient interface.
     * @return {ICustomClient}    The CustomClient.
    */
    getCustomClient(): ICustomClient;
    /**
     * get the PatronClient interface.
     * @return {IPatronClient}    The PatronClient.
    */
    getPatronClient(): IPatronClient;
    /**
     * get the RealTimeDataClient interface.
     * @return {IRealTimeDataClient}    The RealTimeDataClient.
    */
    getRealTimeDataClient(): IRealTimeDataClient;
    /**
     * get the ReportingClient interface.
     * @return {IReportingClient}    The ReportingClient.
    */
    getReportingClient(): IReportingClient;
}


text:
package com.projects.elad.hacklist.data.api;

import com.projects.elad.hacklist.util.Constants;

import java.util.List;
import java.util.Map;

import retrofit.RestAdapter;
import retrofit.http.GET;
import retrofit.http.Path;
import rx.Observable;

public interface HacklistService {

  @GET("/{YEAR}/{MONTH}.json")
  Observable<Map<String, List<HackEvent>>> getMonthObject(@Path("YEAR") String year, @Path("MONTH") String month);

  /******** Helper class that sets up new services *******/
  class ServiceCreator {

    public static HacklistService newHacklistService() {
      RestAdapter retrofitAdapter = new RestAdapter.Builder()
              .setLogLevel(RestAdapter.LogLevel.FULL)
              .setEndpoint(Constants.HACKALIST_BASE_URL)
              .build();

      return retrofitAdapter.create(HacklistService.class);
    }
  }

}


text:
/*
 * Written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to the
 * public domain, as explained at http://creativecommons.org/licenses/publicdomain
 */

package org.apache.geode.internal.util.concurrent;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.geode.CancelCriterion;
import org.apache.geode.internal.Assert;

/**
 * Instances of {@link java.util.concurrent.locks.ReentrantReadWriteLock} that respond to
 * cancellation
 *
 */
public class StoppableReentrantReadWriteLock implements /* ReadWriteLock, */ java.io.Serializable {
  private static final long serialVersionUID = -1185707921434766946L;

  /**
   * The underlying read lock
   */
  private final transient StoppableReadLock readLock;

  /**
   * the underlying write lock
   */
  private final transient StoppableWriteLock writeLock;

  /**
   * This is how often waiters will wake up to check for cancellation
   */
  private static final long RETRY_TIME = 15 * 1000; // milliseconds

  /**
   * Create a new instance
   *
   * @param stopper the cancellation criterion
   */
  public StoppableReentrantReadWriteLock(CancelCriterion stopper) {
    this(new ReentrantReadWriteLock(), stopper);
  }

  protected StoppableReentrantReadWriteLock(ReadWriteLock lock, CancelCriterion stopper) {
    Assert.assertTrue(stopper != null);
    this.readLock = new StoppableReadLock(lock, stopper);
    this.writeLock = new StoppableWriteLock(lock, stopper);
  }

  /**
   * @return the read lock
   */
  public StoppableReadLock readLock() {
    return readLock;
  }

  /**
   * @return the write lock
   */
  public StoppableWriteLock writeLock() {
    return writeLock;
  }

  /**
   * read locks that are stoppable
   */
  public static class StoppableReadLock {

    private final Lock lock;
    private final CancelCriterion stopper;

    /**
     * Create a new read lock from the given lock
     *
     * @param lock the lock to be used
     * @param stopper the cancellation criterion
     */
    StoppableReadLock(ReadWriteLock lock, CancelCriterion stopper) {
      this.lock = lock.readLock();
      this.stopper = stopper;
    }

    public void lock() {
      boolean interrupted = Thread.interrupted();
      try {
        for (;;) {
          try {
            lockInterruptibly();
            break;
          } catch (InterruptedException e) {
            interrupted = true;
          }
        } // for
      } finally {
        if (interrupted)
          Thread.currentThread().interrupt();
      }
    }

    public void lockInterruptibly() throws InterruptedException {
      for (;;) {
        stopper.checkCancelInProgress(null);
        if (lock.tryLock(RETRY_TIME, TimeUnit.MILLISECONDS))
          break;
      }
    }

    /**
     * @return true if the lock was acquired
     */
    public boolean tryLock() {
      stopper.checkCancelInProgress(null);
      return lock.tryLock();
    }

    /**
     * @param timeout how long to wait
     * @return true if the lock was acquired
     */
    public boolean tryLock(long timeout) throws InterruptedException {
      stopper.checkCancelInProgress(null);
      return lock.tryLock(timeout, TimeUnit.MILLISECONDS);
    }

    public void unlock() {
      lock.unlock();
    }

    // /**
    // * @return the new condition
    // */
    // public StoppableCondition newCondition() {
    // return new StoppableCondition(lock.newCondition(), stopper);
    // }
  }

  public static class StoppableWriteLock {

    /**
     * The underlying write lock
     */
    private final Lock lock;

    /**
     * the cancellation criterion
     */
    private final CancelCriterion stopper;

    /**
     * Create a new instance
     *
     * @param lock the underlying lock
     */
    public StoppableWriteLock(ReadWriteLock lock, CancelCriterion stopper) {
      this.lock = lock.writeLock();
      this.stopper = stopper;
    }

    public void lock() {
      boolean interrupted = Thread.interrupted();
      try {
        for (;;) {
          try {
            lockInterruptibly();
            break;
          } catch (InterruptedException e) {
            interrupted = true;
          }
        } // for
      } finally {
        if (interrupted)
          Thread.currentThread().interrupt();
      }
    }

    public void lockInterruptibly() throws InterruptedException {
      for (;;) {
        stopper.checkCancelInProgress(null);
        if (lock.tryLock(RETRY_TIME, TimeUnit.MILLISECONDS))
          break;
      }
    }

    /**
     * @return true if the lock was acquired
     */
    public boolean tryLock() {
      stopper.checkCancelInProgress(null);
      return lock.tryLock();
    }

    /**
     * @param timeout how long to wait
     * @return true if the lock was required
     */
    public boolean tryLock(long timeout) throws InterruptedException {
      stopper.checkCancelInProgress(null);
      return lock.tryLock(timeout, TimeUnit.MILLISECONDS);
    }

    public void unlock() {
      lock.unlock();
    }

    // /**
    // * @return the new condition
    // */
    // public StoppableCondition newCondition() {
    // return new StoppableCondition(lock.newCondition(), stopper);
    // }

  }
}


text:
package org.openo.nfvo.monitor.umc.drill.wrapper.handler.layermonitor;

import org.openo.nfvo.monitor.umc.drill.resources.bean.response.HostInformation;
import org.openo.nfvo.monitor.umc.drill.resources.bean.response.NodeInformation;
import org.openo.nfvo.monitor.umc.drill.resources.bean.response.RootNode;
import org.openo.nfvo.monitor.umc.drill.wrapper.common.exception.TopologyException;
import org.openo.nfvo.monitor.umc.drill.wrapper.handler.AbstractTopologyHandler;
import org.openo.nfvo.monitor.umc.drill.wrapper.resources.IResourceServices;
import org.openo.nfvo.monitor.umc.drill.wrapper.resources.ResourceServicesStub;
import org.openo.nfvo.monitor.umc.drill.wrapper.resources.bean.HostData;
import org.openo.nfvo.monitor.umc.drill.wrapper.resources.bean.response.RestQueryListReturnMsg;

/**
 *       The concrete handler that process the list request of the HOST layer
 */
public class HostLayerMonitorHandler extends AbstractTopologyHandler {

    /**
     * the current node is system itself,so return the RootNode
     */
    @Override
    public NodeInformation queryCurrentNode(String resourceid) throws TopologyException {
        return new RootNode();
    }

    /**
     * the parent of the system is null
     */
    @Override
    public NodeInformation[] queryParentNodes(NodeInformation currentNodeDetail)
            throws TopologyException {
        return null;
    }

    /**
     * query all host nodes info
     *
     */
    @Override
    public NodeInformation[] queryChildNodes(NodeInformation currentNodeDetail)
            throws TopologyException {
    	return ResourceServicesStub.queryAllHosts(currentNodeDetail);
    }
}


text:
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')

describe "SecureURI" do

  it "should work" do

    url = URI.parse("http://caius.name/foo?bar=sed")

    url.secure?.should == false
    url.valid?.should == false

    str = url.secure!

    str.should =~ /hash/

    url.secure?.should == true
    url.valid?.should == true

    url.query = (url.query + "&thing=nothing")

    url.secure?.should == true
    url.valid?.should == false

    url.secure!

    url.secure?.should == true
    url.valid?.should == true
  end

  before do
    @url = "http://example.com/path?query=string"
    @uri = URI.parse(@url)

    @secure_url = "http://example.com/path?query=string&hash=ZOMG-HASH"
    @secure_uri = URI.parse(@secure_url)

    # todo: change to set the salt for the SHA256Hasher
    # SecureURI.salt = "my lovely salt"
  end

  it "should be a URI" do
    @uri.should be_a_kind_of(URI)
  end

  it "should turn into a secure url" do
    @uri.query.should_not =~ /hash=/
    @uri.secure!
    @uri.query.should =~ /hash=/
  end

  it "should not be a secure url" do
    @uri.should_not be_secure
  end

  it "should be a secure url" do
    @secure_uri.should be_secure
  end

  it "should validate as a secure uri" do
    SecureURI::SHA256Hasher.should_receive(:compare).and_return(true)

    @secure_uri.should be_valid
  end

  it "should fail validation as a secure url" do
    @secure_uri.should_not be_valid
  end

  it "should update the hash if the query changes" do
    @secure_uri.query += "&foo=bar"
    @secure_uri.query.should =~ /foo=bar/
    @secure_uri.query.should =~ /hash=/
    old_hash = @secure_uri.send(:hash_string)

    @secure_uri.secure!

    @secure_uri.query.should =~ /hash=/
    @secure_uri.send(:hash_string).should_not == old_hash
  end

  it "should secure a url" do
    @uri.query.should_not =~ /hash=/
    @uri.secure!

    @uri.query.should =~ /hash=/
    @uri.should be_secure
    @uri.should be_valid
  end

  it "should update a secure url" do
    @secure_uri.query.should =~ /hash=/
    q = @secure_uri.query.dup

    @secure_uri.secure!

    @secure_uri.query.should =~ /hash=/
    q.should_not == @secure_uri.query
  end

  describe "hasher" do
    it "should use sha256 by default" do
      SecureURI.hasher.should == SecureURI::SHA256Hasher
    end

    describe "class" do
      before(:all) do
        @original_hasher = SecureURI.hasher

        class HasherSub < SecureURI::Hasher
        end
      end

      after(:all) do
        # "Naughty naughty", said the Class to the spec
        # "Neccessary evil", replied the spec.
        SecureURI.__send__(:hasher=, @original_hasher)
      end

      it "should set hasher when subclassed" do
        SecureURI.hasher.should == HasherSub
      end

      it "should raise an error when hash method isn't overridden" do
        lambda {
          SecureURI::Hasher.hash "string"
        }.should raise_error("hash method needs to be overridden")
      end

      it "should raise an error when compare method isn't overridden" do
        lambda {
          SecureURI::Hasher.compare "string", "hash"
        }.should raise_error("hash method needs to be overridden")
      end
    end
  end
end


text:
namespace IGCS::GameSpecific
{
	// Mandatory constants to define for a game
	#define GAME_NAME									"Metal Gear Solid V: The Phantom Pain v1.0.12+"
	#define CAMERA_VERSION								"1.0.3"
	#define CAMERA_CREDITS								"Otis_Inf and Jan Schatter. Special thanks to tinmantex and Hodgedogs"
	#define GAME_WINDOW_TITLE							"METAL GEAR SOLID V: THE PHANTOM PAIN"
	#define INITIAL_PITCH_RADIANS						0.0f	// around X axis	(right)
	#define INITIAL_YAW_RADIANS							0.0f	// around Y axis	(into the screen)
	#define INITIAL_ROLL_RADIANS						0.0f	// aruond Z axis	(up)
	#define CONTROLLER_Y_INVERT							false
	#define FASTER_MULTIPLIER							10.0f
	#define SLOWER_MULTIPLIER							0.1f
	#define MOUSE_SPEED_CORRECTION						0.2f	// to correct for the mouse-deltas related to normal rotation.
	#define DEFAULT_MOVEMENT_SPEED						0.01f
	#define DEFAULT_ROTATION_SPEED						0.008f
	#define DEFAULT_FOV_SPEED							0.12f
	#define DEFAULT_UP_MOVEMENT_MULTIPLIER				0.7f
	// End Mandatory constants

	// AOB Keys for interceptor's AOB scanner
	#define CAMERA_ADDRESS_INTERCEPT_KEY				"AOB_CAMERA_ADDRESS_INTERCEPT"
	#define CAMERA_CUTSCENE_ADDRESS_INTERCEPT_KEY		"AOB_CAMERA_CUTSCENE_ADDRESS_INTERCEPT"
	#define CAMERA_CUTSCENE_WRITE1_INTERCEPT_KEY		"AOB_CAMERA_CUTSCENE_WRITE1_INTERCEPT"
	#define FOV_WRITE_INTERCEPT_KEY						"AOB_FOV_WRITE_INTERCEPT"
	#define GAMESPEED_WRITE_INTERCEPT_KEY				"AOB_GAMESPEED_WRITE_INTERCEPT"
	#define	TIMESTOP_READ_INTERCEPT_KEY					"AOB_TIMESTOP_READ_INTERCEPT"
	#define FOV_WRITE1_CUTSCENE_INTERCEPT_KEY			"AOB_FOV_WRITE1_CUTSCENE_INTERCEPT"
	#define FOV_WRITE2_CUTSCENE_INTERCEPT_KEY			"AOB_FOV_WRITE2_CUTSCENE_INTERCEPT"
	#define DOF_ADDRESS_INTERCEPT_KEY					"AOB_DOF_ADDRESS_INTERCEPT"
	#define DOF_WRITE_INTERCEPT_KEY						"AOB_DOF_WRITE_INTERCEPT"
	#define WEATHER_ADDRESS_INTERCEPT_KEY				"AOB_WEATHER_ADDRESS_INTERCEPT"
	#define TOD_ADDRESS_INTERCEPT_KEY					"AOB_TOD_ADDRESS_INTERCEPT"

	// Indices in the structures read by interceptors
	#define QUATERNION_IN_STRUCT_OFFSET					0xF0
	#define COORDS_IN_STRUCT_OFFSET						0x100
	#define QUATERNION_CUTSCENE_IN_STRUCT_OFFSET		0x120
	#define COORDS_CUTSCENE_IN_STRUCT_OFFSET			0x130
	#define FOV_IN_STRUCT_OFFSET						0xC
	#define FOV_CUTSCENE_IN_STRUCT_OFFSET				0x3C
	#define GAMESPEED_IN_STRUCT_OFFSET					0xC
	#define TIMESTOP_IN_STRUCT_OFFSET					0x38
	#define DOF_FOCUS_DISTANCE_IN_STRUCT_OFFSET			0x108
	#define DOF_FOCAL_LENGTH_IN_STRUCT_OFFSET			0x110
	#define DOF_APERTURE_IN_STRUCT_OFFSET				0x124
	#define DOF_CONTROL_IN_STRUCT_OFFSET				0x50
	#define WEATHER_DIRECT_IN_STRUCT_OFFSET				0x10				// 3 dword values, on 0x10, 0x14 and 0x18. 0x10 is direct, 0x18 is transition weather
	#define WEATHER_INTENSITY_IN_STRUCT_OFFSET			0x20				// float, 0-1. transition timer is at 0x24 (x->0).
	#define TOD_IN_STRUCT_OFFSET						0x4C				// Dword, in seconds.
}


text:
var gulp = require('gulp');

gulp.task('watch', function() {
  // TODO add dependent files to watch
  //gulp.watch('./lib/index.js', ['testLocalhost']);
  gulp.watch('./lib/get-jscs-errors.js', ['testGetJscsErrors']);
});