package io.robe.admin.resources;

import com.codahale.metrics.annotation.Timed;
import io.dropwizard.hibernate.UnitOfWork;
import io.robe.admin.hibernate.dao.ActionLogDao;
import io.robe.admin.hibernate.dao.UserDao;
import io.robe.admin.hibernate.entity.ActionLog;
import io.robe.admin.hibernate.entity.User;
import io.robe.admin.util.SystemParameterCache;
import io.robe.auth.AbstractAuthResource;
import io.robe.auth.Credentials;
import io.robe.auth.RobeAuth;
import io.robe.auth.token.BasicToken;
import io.robe.auth.token.jersey.TokenBasedAuthResponseFilter;
import org.hibernate.FlushMode;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import static org.hibernate.CacheMode.GET;

/**
 * Authentication Resource to provide standard Authentication services like login,change password....
 */
@Path("authentication")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)

public class AuthResource extends AbstractAuthResource<User> {

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthResource.class);

    private UserDao userDao;

    @Inject
    private ActionLogDao actionLogDao;

    @Inject
    public AuthResource(UserDao userDao) {
        super(userDao);
        this.userDao = userDao;
    }

    /**
     * check username and password for authentication
     *

     * Status Code:
     * UNAUTHORIZED no have authentication.
     * INTERNAL_SERVER_ERROR Blocked.
     *
     * @param request     HttpServletRequest
     * @param credentials username and password (SHA256)
     * @return {@link Response}
     * @throws Exception for createToken
     */

    @POST
    @UnitOfWork(flushMode = FlushMode.ALWAYS)
    @Path("login")
    @Timed
    public Response login(@Context HttpServletRequest request, Map<String, String> credentials) throws Exception {

        Optional<User> user = userDao.findByUsername(credentials.get("username"));
        if (!user.isPresent()) {
            throw new WebApplicationException(Response.Status.UNAUTHORIZED);
        } else if (user.get().getPassword().equals(credentials.get("password"))) {
            if (!user.get().isActive())
                return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("User blocked.").build();
            Map<String, String> attributes = new HashMap<>();
            attributes.put("userAgent", request.getHeader("User-Agent"));
            attributes.put("remoteAddr", request.getRemoteAddr());

            BasicToken token = new BasicToken(user.get().getUserId(), user.get().getEmail(), DateTime.now(), attributes);
            token.setExpiration(token.getMaxAge());
            credentials.remove("password");
            credentials.put("domain", TokenBasedAuthResponseFilter.getTokenSentence(null));

            user.get().setLastLoginTime(DateTime.now().toDate());
            user.get().setFailCount(0);

            logAction(new ActionLog("LOGIN", null, user.get().toString(), true, request.getRemoteAddr()));

            return Response.ok().header("Set-Cookie", TokenBasedAuthResponseFilter.getTokenSentence(token)).entity(credentials).build();
        } else {
            if (!user.get().isActive()) {
                logAction(new ActionLog("LOGIN", "Blocked", user.get().toString(), false, request.getRemoteAddr()));
                return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("User blocked.").build();
            }
            int failCount = user.get().getFailCount() + 1;
            user.get().setFailCount(failCount);
            boolean block = failCount >= Integer.valueOf((String) SystemParameterCache.get("USER_BLOCK_FAIL_LIMIT", "3"));
            if (block)
                user.get().setActive(false);

            userDao.update(user.get());
            logAction(new ActionLog("LOGIN", "Wrong Password", user.get().toString(), false, request.getRemoteAddr()));

            return Response.status(Response.Status.UNAUTHORIZED).build();
        }
    }

    private void logAction(ActionLog login) {
        login.setOid(null);
        actionLogDao.create(login);
    }

    /**
     * Log out
     *
     * @param credentials injected by {@link RobeAuth} annotation for authentication.
     * @return {@link User} when logout
     * @throws Exception for clear permission cache
     */

    @POST
    @UnitOfWork
    @Path("logout")
    @Timed
    public Response logout(@RobeAuth Credentials credentials, @Context HttpServletRequest request) throws Exception {
        Optional<User> user = userDao.findByUsername(credentials.getUsername());
        if (!user.isPresent()) {
            throw new WebApplicationException(Response.Status.UNAUTHORIZED);
        } else {
            BasicToken.clearPermissionCache(credentials.getUsername());
            user.get().setLastLogoutTime(DateTime.now().toDate());
            logAction(new ActionLog("LOGOUT", null, user.get().toString(), true, request.getRemoteAddr()));
            return Response.ok().header("Set-Cookie", TokenBasedAuthResponseFilter.getTokenSentence(null)).build();
        }
    }

    /**
     * User Information Returns
     *

     * Status Code:
     * UNAUTHORIZED no have authentication.
     *
     * @param credentials injected by {@link RobeAuth} annotation for authentication.
     * @return {@link User}
     * @throws Exception for not found user
     */
    @Path("profile")
    @GET
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    public User getProfile(@RobeAuth Credentials credentials) {
        Optional<User> user = userDao.findByUsername(credentials.getUsername());
        if (!user.isPresent()) {
            throw new WebApplicationException(Response.Status.UNAUTHORIZED);
        } else
            return user.get();
    }

    /**
     * User Change Password
     *

     * Status Code:
     * NOT_FOUND not found user.
     * PRECONDITION_FAILED change passsword error.
     *
     * @param request     HttpServletRequest
     * @param passwords   password(SHA256),newpassword(SHA256) and newpasswordrpt(SHA256)
     * @param credentials injected by {@link RobeAuth} annotation for authentication.
     * @return {@link Response}
     * @throws Exception for not found user
     */
    @POST
    @UnitOfWork
    @Path("password")
    @Timed
    public Response changePassword(@Context HttpServletRequest request, @RobeAuth Credentials credentials, Map<String, String> passwords) {

        Optional<User> user = userDao.findByUsername(credentials.getUsername());
        if (!user.isPresent()) {
            throw new WebApplicationException(Response.Status.NOT_FOUND);
        } else if (user.get().getPassword().equals(passwords.get("password"))) {
            if (passwords.get("newPassword").equals(passwords.get("newPasswordRepeat"))) {
                user.get().setPassword(passwords.get("newPassword"));
                return Response.status(Response.Status.OK).entity("Your password has been updated").build();
            } else {
                return Response.status(Response.Status.PRECONDITION_FAILED).entity("Your new password does not match.").build();
            }

        } else {
            return Response.status(Response.Status.PRECONDITION_FAILED).entity("Your password is incorrect.").build();
        }

    }

}


package io.robe.auth.token;

import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
import io.robe.auth.data.entry.*;
import io.robe.auth.data.store.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

/**
 * Authenticator implementation for token based authentication.
 */
public class TokenAuthenticator implements Authenticator<String, BasicToken> {

    private static final Logger LOGGER = LoggerFactory.getLogger(TokenAuthenticator.class);

    private final ServiceStore serviceStore;
    private final UserStore userStore;
    private final RoleStore roleStore;
    private final PermissionStore permissionStore;
    private final RoleGroupStore roleGroupStore;

    /**
     * Creates an instance of TokenBasedAuthenticator with the store classes.
     *
     * @param userStore    Store for getting user.
     * @param serviceStore Store for getting service info.
     */
    public TokenAuthenticator(UserStore userStore, ServiceStore serviceStore, RoleStore roleStore, PermissionStore permissionStore, RoleGroupStore roleGroupStore) {
        this.userStore = userStore;
        this.serviceStore = serviceStore;
        this.roleStore = roleStore;
        this.permissionStore = permissionStore;
        this.roleGroupStore = roleGroupStore;
    }

    /**
     * Creates {@link Optional} {@link io.robe.auth.Credentials} instance from provided tokenString
     *
     * @param tokenString tokenString to decode.
     * @return Optional instance of a {@link io.robe.auth.Credentials} which created from tokenString
     * @throws AuthenticationException
     */
    @Override
    public Optional<BasicToken> authenticate(String tokenString) throws AuthenticationException {
        tokenString = tokenString.replaceAll("\"", "");
        LOGGER.debug("Authenticating from database:  " + tokenString);
        try {
            // Decode tokenString and get user
            BasicToken token = new BasicToken(tokenString);

            Optional<UserEntry> user = (Optional<UserEntry>) userStore.findByUsername(token.getUsername());
            if (!user.isPresent()) {
                LOGGER.warn("User is not available: " + tokenString);
                return Optional.empty();
            }
            // If user exists and active than check Service Permissions for authorization controls
            if (user.get().isActive()) {

                if (token.getPermissions() == null) {
                    LOGGER.debug("Loading Permissions from DB: " + tokenString);
                    Set<String> permissions = new HashSet<String>();
                    Set<PermissionEntry> rolePermissions = new HashSet<PermissionEntry>();

                    //If user role is a group than add sub role permissions to group

                    Optional<RoleEntry> role = (Optional<RoleEntry>) roleStore.findByRoleId(user.get().getRoleId());

                    getAllRolePermissions(role.get(), rolePermissions);

                    for (PermissionEntry permission : rolePermissions) {
                        if (permission.getType().equals(PermissionEntry.Type.SERVICE)) {
                            Optional<? extends ServiceEntry> service = serviceStore.findByCode(permission.getRestrictedItemId());
                            if (service.isPresent()) {
                                permissions.add(service.get().getPath() + ":" + service.get().getMethod());
                            }
                        }
                    }
                    // Create credentials with user info and permission list
                    token.setPermissions(Collections.unmodifiableSet(permissions));
                } else {
                    LOGGER.debug("Loading Permissions from Cache: " + tokenString);
                }

                return Optional.ofNullable(token);
            }
        } catch (Exception e) {
            LOGGER.error(tokenString, e);
        }
        return Optional.empty();

    }

    /**
     * Fill  permission list  with role and sub-role permissions recursively.
     *
     * @param parent          Role to traverse.
     * @param rolePermissions list of all permissions of the given role.
     */
    private void getAllRolePermissions(RoleEntry parent, Set<PermissionEntry> rolePermissions) {
        rolePermissions.addAll(permissionStore.findByRoleId(parent.getId()));
        Set<RoleGroupEntry> roleGroupEntries = (Set<RoleGroupEntry>) roleGroupStore.findByGroupId(parent.getId());
        for (RoleGroupEntry entry : roleGroupEntries) {
            Optional<RoleEntry> role = (Optional<RoleEntry>) roleStore.findByRoleId(entry.getRoleId());
            getAllRolePermissions(role.get(), rolePermissions);
        }
    }

}


package io.robe.admin.resources;

import io.dropwizard.hibernate.UnitOfWork;
import io.robe.admin.dto.JobInfoDTO;
import io.robe.admin.dto.TriggerInfoDTO;
import io.robe.admin.hibernate.dao.JobDao;
import io.robe.admin.hibernate.dao.TriggerDao;
import io.robe.admin.hibernate.entity.HJobInfo;
import io.robe.admin.hibernate.entity.HTriggerInfo;
import io.robe.admin.quartz.HibernateJobInfoProvider;
import io.robe.auth.Credentials;
import io.robe.auth.RobeAuth;
import io.robe.common.service.RobeService;
import io.robe.common.service.search.SearchParam;
import io.robe.common.service.search.model.SearchModel;
import io.robe.quartz.JobManager;
import io.robe.quartz.info.JobInfo;
import io.robe.quartz.info.TriggerInfo;
import org.hibernate.FlushMode;
import org.quartz.JobDetail;
import org.quartz.SchedulerException;
import org.quartz.Trigger;

import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.*;

import static org.hibernate.CacheMode.GET;

@Path("quartzjobs")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class JobResource {

    @Inject
    private TriggerDao triggerDao;

    @Inject
    private JobDao jobDao;

    /**
     * Return all HJobInfo as a collection
     *
     * @param credentials auto fill by {@link RobeAuth} annotation for authentication.
     * @return all {@link HJobInfo} as a collection
     */
    @RobeService(group = "HJobInfo", description = "Returns all HJobInfo as a collection.")
    @GET
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    public Collection<JobInfoDTO> getAll(@RobeAuth Credentials credentials, @SearchParam SearchModel search) {
        List<JobInfoDTO> dtoList = new LinkedList<>();
        for (HJobInfo info : jobDao.findAllStrict(search)) {
            JobInfoDTO dto = new JobInfoDTO(info);
            try {
                if (!JobManager.getInstance().isScheduledJob(dto.getName(), dto.getGroup())) {
                    dto.setStatus(JobInfoDTO.Status.UNSCHEDULED);
                } else {
                    if (JobManager.getInstance().isPausedJob(dto.getName(), dto.getGroup())) {
                        dto.setStatus(JobInfoDTO.Status.PAUSED);
                    } else {
                        dto.setStatus(JobInfoDTO.Status.ACTIVE);
                    }
                }
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
            dtoList.add(dto);
        }
        return dtoList;
    }

    /**
     * Return a HJobInfo resource  with the matches given id.
     * <p>
     * Status Code:
     * Not Found  404
     *
     * @param credentials auto fill by @{@link RobeAuth} annotation for authentication.
     * @param id          This is  the oid of {@link HJobInfo}
     * @return a  {@link HJobInfo} resource with the matches given id.
     */
    @RobeService(group = "HJobInfo", description = "Returns a HJobInfo resource with the matches given id.")
    @Path("{id}")
    @GET
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    public HJobInfo get(@RobeAuth Credentials credentials, @PathParam("id") String id) {
        HJobInfo entity = jobDao.findById(id);
        if (entity == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        return entity;
    }

    /**
     * Returns all HTriggerInfo as a collection with the matches given job id.
     *
     * @param credentials auto fill by {@link RobeAuth} annotation for authentication.
     * @return all {@link HTriggerInfo} as a collection
     */
    @RobeService(group = "HJobInfo", description = "Returns all HTriggerInfo as a collection with the matches given job id.")
    @GET
    @Path("{id}/triggers")
    @UnitOfWork
    public List<TriggerInfoDTO> getJobTriggers(@RobeAuth Credentials credentials, @PathParam("id") String id) {
        List<TriggerInfoDTO> dtos = new LinkedList<>();
        for (HTriggerInfo info : triggerDao.findByJobOid(id)) {
            TriggerInfoDTO dto = new TriggerInfoDTO(info);
            try {
                if (!JobManager.getInstance().isScheduledTrigger(dto.getName(), dto.getGroup())) {
                    dto.setStatus(JobInfoDTO.Status.UNSCHEDULED);
                } else {
                    if (JobManager.getInstance().isPausedTrigger(dto.getName(), dto.getGroup())) {
                        dto.setStatus(JobInfoDTO.Status.PAUSED);
                    } else {
                        dto.setStatus(JobInfoDTO.Status.ACTIVE);
                    }
                }
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
            dtos.add(dto);
        }
        return dtos;
    }

    /**
     * Returns all HTriggerInfo as a collection with the matches given job id.
     *
     * @param credentials auto fill by {@link RobeAuth} annotation for authentication.
     * @return all {@link HTriggerInfo} as a collection
     */
    @RobeService(group = "HJobInfo", description = "Returns all HTriggerInfo as a collection with the matches given job id.")
    @PUT
    @Path("{id}/schedule")
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    public boolean schedule(@RobeAuth Credentials credentials, @PathParam("id") String id) {
        HJobInfo info = jobDao.findById(id);
        if (info == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        try {
            JobInfo dto = new HibernateJobInfoProvider().getJob(info.getJobClass());
            JobDetail detail = HibernateJobInfoProvider.convert2JobDetail(dto);
            Set<Trigger> triggers = new HashSet<>(dto.getTriggers().size());
            for (TriggerInfo triggerInfo : dto.getTriggers()) {
                if (triggerInfo.getType().equals(TriggerInfo.Type.CRON) ||
                        triggerInfo.getType().equals(TriggerInfo.Type.SIMPLE)) {
                    triggers.add(HibernateJobInfoProvider.convert2Trigger(triggerInfo, dto));
                }
            }
            JobManager.getInstance().scheduleJob(detail, triggers, false);
            return true;
        } catch (SchedulerException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Returns all HTriggerInfo as a collection with the matches given job id.
     *
     * @param credentials auto fill by {@link RobeAuth} annotation for authentication.
     * @return all {@link HTriggerInfo} as a collection
     */
    @RobeService(group = "HJobInfo", description = "Returns all HTriggerInfo as a collection with the matches given job id.")
    @PUT
    @Path("{id}/unschedule")
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    public boolean unschedule(@RobeAuth Credentials credentials, @PathParam("id") String id) {
        HJobInfo info = jobDao.findById(id);
        if (info == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        try {
            return JobManager.getInstance().unScheduleJob(info.getName(), info.getGroup());
        } catch (SchedulerException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Returns all HTriggerInfo as a collection with the matches given job id.
     *
     * @param credentials auto fill by {@link RobeAuth} annotation for authentication.
     * @return all {@link HTriggerInfo} as a collection
     */
    @RobeService(group = "HJobInfo", description = "Returns all HTriggerInfo as a collection with the matches given job id.")
    @PUT
    @Path("{id}/pause")
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    public boolean pause(@RobeAuth Credentials credentials, @PathParam("id") String id) {
        HJobInfo info = jobDao.findById(id);
        if (info == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        try {
            JobManager.getInstance().pauseJob(info.getName(), info.getGroup());
        } catch (SchedulerException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * Returns all HTriggerInfo as a collection with the matches given job id.
     *
     * @param credentials auto fill by {@link RobeAuth} annotation for authentication.
     * @return all {@link HTriggerInfo} as a collection
     */
    @RobeService(group = "HJobInfo", description = "Returns all HTriggerInfo as a collection with the matches given job id.")
    @PUT
    @Path("{id}/resume")
    @UnitOfWork
    public boolean resume(@RobeAuth Credentials credentials, @PathParam("id") String id) {
        HJobInfo info = jobDao.findById(id);
        if (info == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        try {
            JobManager.getInstance().resumeJob(info.getName(), info.getGroup());
        } catch (SchedulerException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}


package io.robe.servlet;

import io.dropwizard.hibernate.UnitOfWork;
import io.robe.guice.GuiceBundle;
import org.hibernate.*;
import org.hibernate.context.internal.ManagedSessionContext;
import org.hibernate.resource.transaction.spi.TransactionStatus;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 * A Wrapper servlet class to manage method calls, session, injection and transactions.
 * Uses standard Servlet methods and forwards these calls to the {@link ResourceServlet} implementation.
 * {@link io.dropwizard.hibernate.UnitOfWork} annotation properties works exactly the same with Dropwizard Resources methods.
 */
public class WrapperServlet extends HttpServlet {

    private final Class<ResourceServlet> servletClass;
    private ResourceServlet singleton;
    private final Method doGet;
    private final UnitOfWork uowDoGet;
    private final Method doPut;
    private final UnitOfWork uowDoPut;
    private final Method doPost;
    private final UnitOfWork uowDoPost;
    private final Method doDelete;
    private final UnitOfWork uowDoDelete;
    private final Method doOptions;
    private final UnitOfWork uowDoOptions;
    private final Method doHead;
    private final UnitOfWork uowDoHead;
    private final Method doTrace;
    private final UnitOfWork uowDoTrace;

    /**
     * Contstructor to get
     *
     * @param servletClass {@link ResourceServlet} implementation class and cache all methods and
     *                     {@link io.dropwizard.hibernate.UnitOfWork} annotations of methods.
     * @throws NoSuchMethodException
     */
    public WrapperServlet(Class<ResourceServlet> servletClass, boolean singleInstance) throws NoSuchMethodException {
        this.servletClass = servletClass;
        this.singleton = null;
        try {
            if (singleInstance)
                singleton = GuiceBundle.getInjector().getInstance(servletClass);
            doGet = servletClass.getMethod("doGet", HttpServletRequest.class, HttpServletResponse.class);
            doPut = servletClass.getMethod("doPut", HttpServletRequest.class, HttpServletResponse.class);
            doPost = servletClass.getMethod("doPost", HttpServletRequest.class, HttpServletResponse.class);
            doDelete = servletClass.getMethod("doDelete", HttpServletRequest.class, HttpServletResponse.class);
            doOptions = servletClass.getMethod("doOptions", HttpServletRequest.class, HttpServletResponse.class);
            doHead = servletClass.getMethod("doHead", HttpServletRequest.class, HttpServletResponse.class);
            doTrace = servletClass.getMethod("doTrace", HttpServletRequest.class, HttpServletResponse.class);

            uowDoGet = doGet.getAnnotation(UnitOfWork.class);
            uowDoPut = doPut.getAnnotation(UnitOfWork.class);
            uowDoPost = doPost.getAnnotation(UnitOfWork.class);
            uowDoDelete = doDelete.getAnnotation(UnitOfWork.class);
            uowDoOptions = doOptions.getAnnotation(UnitOfWork.class);
            uowDoHead = doHead.getAnnotation(UnitOfWork.class);
            uowDoTrace = doTrace.getAnnotation(UnitOfWork.class);
        } catch (NoSuchMethodException e) {
            throw e; // Never
        }

    }

    @Override
    protected final void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        dispatch(doGet, uowDoGet, req, resp);
    }

    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        dispatch(doPut, uowDoPut, req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        dispatch(doPost, uowDoPost, req, resp);
    }

    @Override
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        dispatch(doDelete, uowDoDelete, req, resp);
    }

    @Override
    protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        dispatch(doOptions, uowDoOptions, req, resp);
    }

    @Override
    protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        dispatch(doHead, uowDoHead, req, resp);
    }

    @Override
    protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        dispatch(doTrace, uowDoTrace, req, resp);
    }

    /**
     * Executes the desired method call with a new instance of the {@link ResourceServlet} implementation.
     * Transaction management is done here.
     *
     * @param call
     * @param uow
     * @param req
     * @param resp
     */
    private final void dispatch(Method call, UnitOfWork uow, HttpServletRequest req, HttpServletResponse resp) {
        if (uow == null) {
            try {
                ResourceServlet servlet = GuiceBundle.getInjector().getInstance(servletClass);
                call.invoke(servlet, req, resp);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            SessionFactory sessionFactory = GuiceBundle.getInjector().getInstance(SessionFactory.class);
            Session session = sessionFactory.openSession();
            try {
                configureSession(session, uow);
                ManagedSessionContext.bind(session);
                beginTransaction(session, uow);
                try {
                    ResourceServlet servlet = (singleton == null) ? singleton : GuiceBundle.getInjector().getInstance(servletClass);
                    call.invoke(servlet, req, resp);
                    commitTransaction(session, uow);
                } catch (Exception e) {
                    rollbackTransaction(session, uow);
                    throw new RuntimeException(e);
                }
            } finally {
                session.close();
                ManagedSessionContext.unbind(sessionFactory);
            }
        }
    }

    private void beginTransaction(Session session, UnitOfWork uow) {
        if (uow.transactional()) {
            session.beginTransaction();
        }

    }

    private void configureSession(Session session, UnitOfWork uow) {
        session.setDefaultReadOnly(uow.readOnly());
        session.setCacheMode(uow.cacheMode());
        session.setFlushMode(uow.flushMode());
    }

    private void rollbackTransaction(Session session, UnitOfWork uow) {
        if (uow.transactional()) {
            Transaction txn = session.getTransaction();
            if (txn != null && session.getTransaction().getStatus() != TransactionStatus.ACTIVE) {
                txn.rollback();
            }
        }

    }

    private void commitTransaction(Session session, UnitOfWork uow) {
        if (uow.transactional()) {
            Transaction txn = session.getTransaction();
            if (txn != null && session.getTransaction().getStatus() != TransactionStatus.ACTIVE) {
                txn.commit();
            }
        }

    }
}


package io.robe.hibernate.criteria.api.projection;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by kamilbukum on 13/01/2017.
 */
public class ProjectionList implements Projection {

    private List<Projection> elements = new ArrayList<>();

    /**
     * Constructs a ProjectionList
     *
     * @see org.hibernate.criterion.Projections#projectionList()
     */
    protected ProjectionList() {
    }

     /**
     * Add a projection to this list of projections
     *
     * @param projection The projection to add
     * @return {@code this}, for method chaining
     */
    public ProjectionList add(Projection projection) {
        elements.add(projection);
        return this;
    }

    /**
     * Adds a projection to this list of projections after wrapping it with an alias
     *
     * @param projection The projection to add
     * @param alias      The alias to apply to the projection
     * @return {@code this}, for method chaining
     * @see org.hibernate.criterion.Projections#alias
     */
    public ProjectionList add(Projection projection, String alias) {
        return add(Projections.alias(projection, alias));
    }

    public boolean isGrouped() {
        for (Projection projection : elements) {
            if (projection.isGrouped()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Access a wrapped projection by index
     *
     * @param i The index of the projection to return
     * @return The projection
     */
    @SuppressWarnings("UnusedDeclaration")
    public Projection getProjection(int i) {
        return elements.get(i);
    }

    public int getLength() {
        return elements.size();
    }

    @Override
    public String toString() {
        return elements.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof ProjectionList)) return false;

        ProjectionList that = (ProjectionList) o;

        return elements != null ? elements.equals(that.elements) : that.elements == null;
    }

    @Override
    public int hashCode() {
        return elements != null ? elements.hashCode() : 0;
    }
}


package io.robe.admin.resources;

import io.dropwizard.hibernate.UnitOfWork;
import io.dropwizard.jersey.PATCH;
import io.dropwizard.jersey.caching.CacheControl;
import io.robe.admin.dto.MenuItem;
import io.robe.admin.hibernate.dao.*;
import io.robe.admin.hibernate.entity.*;
import io.robe.auth.Credentials;
import io.robe.auth.RobeAuth;
import io.robe.common.service.RobeService;
import io.robe.common.service.search.SearchParam;
import io.robe.common.service.search.model.SearchModel;
import io.robe.common.utils.reflection.Fields;
import org.hibernate.FlushMode;

import javax.inject.Inject;
import javax.validation.Valid;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.*;

import static org.hibernate.CacheMode.GET;

@Path("menus")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class MenuResource {

    @Inject
    private MenuDao menuDao;

    @Inject
    private RoleDao roleDao;

    @Inject
    private PermissionDao permissionDao;

    @Inject
    private RoleGroupDao roleGroupDao;

    @Inject
    private UserDao userDao;

    /**
     * Returns all {@link Menu}s as a collection.
     *
     * @param credentials injected by {@link RobeAuth} annotation for authentication.
     * @return all {@link Menu}s as a collection with.
     */

    @RobeService(group = "Menu", description = "Returns all Menu's as a collection.")
    @GET
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    public List<Menu> getAll(@RobeAuth Credentials credentials, @SearchParam SearchModel search) {
        return menuDao.findAllStrict(search);
    }

    private void getAllRolePermissions(Role parent, Set<Permission> rolePermissions) {
        rolePermissions.addAll(permissionDao.findByRoleOId(parent.getId()));
        List<RoleGroup> roleGroupEntries = roleGroupDao.findByGroupOId(parent.getId());
        for (RoleGroup entry : roleGroupEntries) {
            Role role = roleDao.findById(entry.getRoleOid());
            getAllRolePermissions(role, rolePermissions);
        }
    }

    /**
     * get menu for logged user
     *
     * @param credentials injected by {@link RobeAuth} annotation for authentication.
     * @return user {@link MenuItem} as collection
     */

    @RobeService(group = "Menu", description = "Get menu for logged user")
    @Path("user")
    @GET
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    @CacheControl(noCache = true)
    public List<MenuItem> getUserHierarchicalMenu(@RobeAuth Credentials credentials) {
        Optional<User> user = userDao.findByUsername(credentials.getUsername());
        Set<Permission> permissions = new HashSet<Permission>();

        Role parent = roleDao.findById(user.get().getRoleOid());
        getAllRolePermissions(parent, permissions);
        Set<String> menuOids = new HashSet<String>();

        List<MenuItem> items = convertMenuToMenuItem(menuDao.findHierarchicalMenu());
        items = readMenuHierarchical(items);

        for (Permission permission : permissions) {
            if (permission.getType().equals(Permission.Type.MENU)) {
                menuOids.add(permission.getRestrictedItemOid());
            }
        }
        List<MenuItem> permittedItems = new LinkedList<MenuItem>();

        createMenuWithPermissions(menuOids, items, permittedItems);

        return permittedItems;
    }

    private List<MenuItem> readMenuHierarchical(List<MenuItem> items) {
        for (MenuItem item : items) {
            List<Menu> menus = menuDao.findByParentOid(item.getOid());
            item.setItems(convertMenuToMenuItem(menus));
            readMenuHierarchical(item.getItems());
        }

        return items;
    }

    private List<MenuItem> convertMenuToMenuItem(List<Menu> menus) {

        List<MenuItem> items = new ArrayList<>();

        for (Menu menu : menus) {
            items.add(new MenuItem(menu));
        }

        return items;
    }

    private void createMenuWithPermissions(Set<String> permissions, List<MenuItem> items, List<MenuItem> permittedItems) {
        for (MenuItem item : items) {
            MenuItem permittedItem = new MenuItem(item.getText(), item.getPath(), item.getModule(), item.getIndex(), item.getIcon());
            if (permissions.contains(item.getOid())) {
                permittedItems.add(permittedItem);
            }
            createMenuWithPermissions(permissions, item.getItems(), permittedItem.getItems());
            //If any sub menu permitted add parent menu also.
            if (!permittedItem.getItems().isEmpty() && !permittedItems.contains(permittedItem)) {
                permittedItems.add(permittedItem);
            }
        }
    }

    /**
     * Returns a single Menu matches with the given id.
     *

     * Status Code:
     * Not Found  404
     *
     * @param credentials injected by {@link RobeAuth} annotation for authentication.
     * @param id          This is the oid of {@link Menu}
     * @return a {@link Menu} resource matches with the given id.
     */
    @RobeService(group = "Menu", description = "Returns a Menu resource matches with the given id.")
    @Path("{id}")
    @GET
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    public Menu get(@RobeAuth Credentials credentials, @PathParam("id") String id) {
        Menu entity = menuDao.findById(id);
        if (entity == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        return entity;
    }

    /**
     * Create a {@link Menu} resource.
     *
     * @param credentials injected by {@link RobeAuth} annotation for authentication.
     * @param model       data of {@link Menu}
     * @return Create as a {@link Menu} resource.
     */
    @RobeService(group = "Menu", description = "Create a Menu resource.")
    @POST
    @UnitOfWork
    public Menu create(@RobeAuth Credentials credentials, @Valid Menu model) {
        return menuDao.create(model);
    }

    /**
     * Updates a single {@link Menu} matches with the given id.
     *

     * Status Code:
     * Not Found  404
     * Not Matches 412
     *
     * @param credentials injected by {@link RobeAuth} annotation for authentication.
     * @param id          This is the oid of {@link Menu}
     * @param model       data of {@link Menu}
     * @return Updates a single {@link Menu} matches with the given id.
     */
    @RobeService(group = "Menu", description = "Updates a single Menu matches with the given id.")
    @Path("{id}")
    @PUT
    @UnitOfWork
    public Menu update(@RobeAuth Credentials credentials, @PathParam("id") String id, @Valid Menu model) {
        if (!id.equals((model.getOid()))) {
            throw new WebApplicationException(Response.status(412).build());
        }
        Menu entity = menuDao.findById(id);

        if (entity == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        menuDao.detach(entity);
        return menuDao.update(model);
    }

    /**
     * Updates a single {@link Menu} matches with the given id.
     *

     * Status Code:
     * Not Found  404
     * Not Matches 412
     *
     * @param credentials injected by {@link RobeAuth} annotation for authentication.
     * @param id          This is the oid of {@link Menu}
     * @param model       data of {@link Menu}
     * @return Updates a single {@link Menu} matches with the given id.
     */
    @RobeService(group = "Menu", description = "Updates a single Menu matches with the given id.")
    @PATCH
    @UnitOfWork
    @Path("{id}")
    public Menu merge(@RobeAuth Credentials credentials, @PathParam("id") String id, Menu model) {
        if (!id.equals(model.getOid()))
            throw new WebApplicationException(Response.status(412).build());
        Menu dest = menuDao.findById(id);
        if (dest == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        Fields.mergeRight(model, dest);
        return menuDao.update(dest);
    }

    /**
     * Deletes a single {@link Menu} matches with the given id.
     *

     * Status Code:
     * Not Found  404
     * Not Matches 412
     *
     * @param credentials injected by {@link RobeAuth} annotation for authentication.
     * @param id          This is the oid of {@link Menu}
     * @param model       data of {@link Menu}
     * @return delete a single {@link Menu} matches with the given id.
     */
    @RobeService(group = "Menu", description = "Deletes a single Menu matches with the given id.")
    @Path("{id}")
    @DELETE
    @UnitOfWork
    public Menu delete(@RobeAuth Credentials credentials, @PathParam("id") String id, @Valid Menu model) {

        if (!id.equals(model.getOid())) {
            throw new WebApplicationException(Response.status(412).build());
        }
        Menu entity = menuDao.findById(id);
        if (entity == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        return menuDao.delete(entity);
    }
}


package io.robe.quartz.info;

import org.quartz.*;

import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

public abstract class JobInfoProvider {

    public abstract JobInfo getJob(Class<? extends Job> clazz);

    /**
     * Creates an identical JobDetail instance from the given parameters.
     *
     * @param info job annotation
     * @return
     */
    public static JobDetail convert2JobDetail(JobInfo info) {
        JobKey jobKey = JobKey.jobKey(info.getName());
        JobDetail jobDetail = newJob(info.getJobClass()).
                withIdentity(jobKey).
                build();
        return jobDetail;
    }

    public List<Trigger> convert2Triggers(JobInfo dto) {
        List<Trigger> triggers = new LinkedList<>();
        for (TriggerInfo trigDto : dto.getTriggers()) {
            triggers.add(convert2Trigger(trigDto, dto));
        }
        return triggers;
    }

    /**
     * Creates an identical Trigger instance from the given annotation.
     *
     * @param trig
     * @return
     */
    public static Trigger convert2Trigger(TriggerInfo trig, JobInfo job) {
        TriggerBuilder<Trigger> builder = newTrigger();
        builder.withIdentity(trig.getName(), trig.getGroup());
        builder.forJob(job.getName(), job.getGroup());

        switch (trig.getType()) {
            case CRON:
                setStartEndTime(trig, builder);

                if (!trig.getCron().isEmpty())
                    builder.withSchedule(CronScheduleBuilder.cronSchedule(trig.getCron()));
                break;
            case SIMPLE:
                setStartEndTime(trig, builder);
                setCountIntervalValues(trig, builder);
                break;
        }

        return builder.build();
    }

    /**
     * Helps to set count and intervals
     *
     * @param dto
     * @param builder
     */
    private static void setCountIntervalValues(TriggerInfo dto, TriggerBuilder<org.quartz.Trigger> builder) {
        SimpleScheduleBuilder builderSc = SimpleScheduleBuilder.simpleSchedule();
        if (dto.getRepeatCount() != 0)
            builderSc.withRepeatCount(dto.getRepeatCount());

        if (dto.getRepeatInterval() > 0)
            builderSc.withIntervalInMilliseconds(dto.getRepeatInterval());
        builder.withSchedule(builderSc);
    }

    /**
     * Helps to set start and end times
     *
     * @param dto
     * @param builder
     */
    private static void setStartEndTime(TriggerInfo dto, TriggerBuilder<org.quartz.Trigger> builder) {
        if (dto.getStartTime() > -1)
            builder.startAt(new Date(dto.getStartTime()));
        else
            builder.startNow();

        if (dto.getEndTime() > -1)
            builder.endAt(new Date(dto.getEndTime()));
    }
}


package io.robe.mail;

import io.robe.mail.queue.InMemoryMailQueue;
import io.robe.mail.queue.MailQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A queue and consumer thread implementation.
 * Sender thread wakes up on every item insert and works until queue goes empty.
 * Every item in queue will be send only once. If any error occurs it is the developers responsibility to handle it. {@link io.robe.mail.MailEvent}.
 */
public class MailManager {
    private static final Logger LOGGER = LoggerFactory.getLogger(MailManager.class);
    private static final MailQueue<MailItem> queue = new InMemoryMailQueue();
    private static MailSender sender;
    private static Thread consumerThread = new Thread();
    private static Runnable consumer = new Runnable() {
        @Override
        public void run() {
            LOGGER.debug("Starting mail thread.");

            // Check queue and continue until it is empty.
            while (!queue.isEmpty()) {

                // Take first and remove from queue
                MailItem item = queue.poll();
                LOGGER.info("Sending: " + item.getId() + " (" + queue.size() + ")");

                // Decide to call events.
                boolean hasEvent = item.getEvent() != null;
                if (hasEvent) {
                    item.getEvent().before(item);
                }
                try {
                    // Send it!
                    sender.sendMessage(item);
                    LOGGER.info("Success: " + item.getId() + " (" + queue.size() + ")");
                    if (hasEvent) {
                        item.getEvent().success(item);
                    }
                } catch (Exception e) {
                    LOGGER.info("Failed: " + item.getId() + " (" + queue.size() + ")");
                    LOGGER.debug(item.toString(), e);
                    if (hasEvent) {
                        item.getEvent().error(item, e);
                    }
                }
            }
            LOGGER.debug("Stopping mail thread.");

        }
    };

    static void setSender(MailSender sender) {
        MailManager.sender = sender;
    }

    public static boolean hasConfiguration() {
        try {
            return sender.getConfiguration() != null;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * Takes the mail item into the queue and manages the mail sender thread.
     * If thread is alive it will send the mail at the end of current thread queue.
     * Else a new thread will be created and started.
     *
     * @param item MailItem to send.
     * @return true if item added to the queue successfully.
     */
    public static boolean sendMail(MailItem item) {
        LOGGER.debug("Mail : " + item.toString());
        boolean result = queue.add(item);
        LOGGER.info("Adding mail to queue. Queue size: " + queue.size());

        // If thread is alive leave the job to it
        // Else create new thread and start.
        if (!consumerThread.isAlive()) {
            consumerThread = new Thread(consumer);
            consumerThread.start();
        }
        return result;
    }
}


package io.robe.common.service.stream;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.StreamingOutput;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * A buffered {@link javax.ws.rs.core.StreamingOutput} implementation.
 */
public class BufferedStreamingOutput implements StreamingOutput {
    private final BufferedInputStream bufferedInputStream;
    private byte[] buffer;

    /**
     * Creates an instance with the given parameters.
     *
     * @param bufferedInputStream input to stream.
     * @param bufferSize          size of the buffer.
     */
    public BufferedStreamingOutput(BufferedInputStream bufferedInputStream, int bufferSize) {
        this.bufferedInputStream = bufferedInputStream;
        buffer = new byte[bufferSize];//1mb
    }

    /**
     * Called to write the message body.
     *
     * @param output the OutputStream to write to.
     * @throws java.io.IOException                 if an IO error is encountered
     * @throws javax.ws.rs.WebApplicationException if a specific
     *                                             HTTP error response needs to be produced. Only effective if thrown prior
     *                                             to any bytes being written to output.
     */
    @Override
    public void write(OutputStream output) throws IOException, WebApplicationException {
        //Write until available bytes are less than buffer.
        while (bufferedInputStream.available() > buffer.length) {
            bufferedInputStream.read(buffer);
            output.write(buffer);
        }
        //Write one more time to finish and exit.
        buffer = new byte[bufferedInputStream.available()];
        bufferedInputStream.read(buffer);
        output.write(buffer);
        bufferedInputStream.close();
    }
}


package io.robe.quartz;

import io.dropwizard.Configuration;
import io.dropwizard.ConfiguredBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.robe.quartz.configuration.HasQuartzConfiguration;
import io.robe.quartz.configuration.QuartzConfiguration;
import io.robe.quartz.info.JobInfo;
import io.robe.quartz.info.JobInfoProvider;
import io.robe.quartz.info.TriggerInfo;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Configures  Quartz.
 * Collect {@link JobInfoProvider} classes
 * Initializes scheduler
 * Collects all subtypes of {@link org.quartz.Job} annotated with {@link RobeJob} including {@link @RobeTrigger}'s
 * * Collects additional triggers from providers
 * * Registers them all for future control.
 * Holds application start and end triggered jobs for managed access.
 * Provides Job & Trigger Registery
 */
public class QuartzBundle<T extends Configuration & HasQuartzConfiguration> implements ConfiguredBundle<T> {
    private static final Logger LOGGER = LoggerFactory.getLogger(QuartzBundle.class);
    private Set<JobDetail> onStartJobs = null;
    private Set<JobDetail> onStopJobs = null;
    public static final ConcurrentHashMap<String, JobInfo> JOBS = new ConcurrentHashMap<>();

    public QuartzBundle() {
    }

    /**
     * Initializes the environment. Forwards the configuration to Quartz.
     * Collect {@link JobInfoProvider} classes
     * Initializes scheduler
     * Collects all subtypes of {@link org.quartz.Job} annotated with {@link RobeJob} including {@link @RobeTrigger}'s
     * * Collects additional triggers from providers
     * * Registers them all for future control.
     *
     * @param configuration the configuration object
     * @param environment   the service's {@link io.dropwizard.setup.Environment}
     * @throws Exception if something goes wrong
     */
    @Override
    public void run(T configuration, Environment environment) {
        QuartzConfiguration qConf = configuration.getQuartz();
        try {

            initializeScheduler(qConf.getProperties());
            collectAndScheduleJobs(qConf.getScanPackages());

            environment.lifecycle().manage(new ManagedQuartz(getOnStartJobs(), getOnStopJobs()));

        } catch (SchedulerException e) {
            LOGGER.error("SchedulerException:", e);
        }
    }

    /**
     * Initialize scheduler and start JobManager
     *
     * @param properties
     * @throws SchedulerException
     */
    private void initializeScheduler(Properties properties) throws SchedulerException {
        SchedulerFactory factory = new StdSchedulerFactory(properties);
        Scheduler scheduler = factory.getScheduler();
        scheduler.start();
        JobManager.initialize(scheduler);
    }

    private void collectAndScheduleJobs(String[] packages) throws SchedulerException {
        Set<Class<? extends Job>> quartzJobs;

        onStartJobs = new HashSet<>();
        onStopJobs = new HashSet<>();

        for (String pkg : packages) {
            LOGGER.info("Scanning Jobs package : " + pkg);
            Reflections reflections = new Reflections(pkg);
            quartzJobs = reflections.getSubTypesOf(Job.class);

            for (Class<? extends Job> clazz : quartzJobs) {
                RobeJob infoAnn = clazz.getDeclaredAnnotation(RobeJob.class);
                if (infoAnn == null)
                    continue;

                JobInfoProvider infoProvider;
                try {
                    infoProvider = infoAnn.provider().newInstance();
                } catch (Exception e) {
                    e.printStackTrace();
                    continue;
                }
                Set<Trigger> triggers = new HashSet<>();

                JobInfo info = infoProvider.getJob(clazz);
                if (info == null || JOBS.containsKey(info.getJobClass().getName()))
                    continue;

                JobDetail detail = JobInfoProvider.convert2JobDetail(info);

                StringBuilder logBuilder = new StringBuilder();
                //Collect all triggers
                for (TriggerInfo tInfo : info.getTriggers()) {
                    logBuilder.append("\n\t\tTrigger: ")
                            .append(clazz.getName())
                            .append("\tName: ")
                            .append(tInfo.getName())
                            .append("\tType:")
                            .append(tInfo.getType().name());
                    switch (tInfo.getType()) {
                        case ON_APP_START:
                            onStartJobs.add(detail);
                            break;
                        case ON_APP_STOP:
                            onStopJobs.add(detail);
                            break;
                        default:
                            triggers.add(JobInfoProvider.convert2Trigger(tInfo, info));
                    }
                }
                LOGGER.info("\n\tClass {} \n\tName: {} \n\tDesc: {} \n\t Triggers: {}",
                        info.getJobClass().getName(),
                        info.getName(),
                        info.getDescription(),
                        logBuilder.toString()
                );
                JobManager.getInstance().scheduleJob(detail, triggers, true);
                JOBS.put(info.getJobClass().getName(), info);
            }
        }
    }

    /**
     * Initializes the service bootstrap.
     *
     * @param bootstrap the service bootstrap
     */
    @Override
    public void initialize(Bootstrap<?> bootstrap) {
        LOGGER.info("Initializing QuartzBundle");
    }

    public Set<JobDetail> getOnStartJobs() {
        return onStartJobs;
    }

    public Set<JobDetail> getOnStopJobs() {
        return onStopJobs;
    }
}


package io.robe.convert.common;

import io.robe.convert.common.annotation.Convert;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

/**
 * Abstract Converter class to include common concepts and util methods
 */
public abstract class Converter {

    /**
     * Holds the class of data type.
     */
    private final Class dataClass;

    public Converter(Class dataClass) {
        this.dataClass = dataClass;
    }

    /**
     * Returns the class of data type  which was given at constructor.
     *
     * @return data class.
     */
    public Class getDataClass() {
        return dataClass;
    }

    protected final boolean isSuitable(Convert ann) {
        return ((ann != null) && !ann.ignore());
    }

    /**
     * Returns an ordered list of the fields which belongs to the given class.
     *
     * @param clazz class to get types.
     * @return ordered list of fields.
     */
    protected final Collection<FieldEntry> getFields(Class clazz) {
        LinkedList<FieldEntry> fieldList = getAllFields(clazz);

        Collections.sort(fieldList, new Comparator<FieldEntry>() {
            public int compare(FieldEntry o1, FieldEntry o2) {
                return o1.compareTo(o2);
            }
        });
        return fieldList;
    }

    protected final LinkedList<FieldEntry> getAllFields(Class clazz) {
        LinkedList<FieldEntry> fieldList = new LinkedList<>();

        if (clazz.getSuperclass() != null) {
            fieldList = getAllFields(clazz.getSuperclass());
        }
        for (Field field : clazz.getDeclaredFields()) {
            Convert cfAnn = field.getAnnotation(Convert.class);
            if (isSuitable(cfAnn)) {
                fieldList.add(new FieldEntry(cfAnn.order(), field));
            }
        }

        return fieldList;
    }

    /**
     * Returns a map of the fields which belongs to the given class with field name as key.
     *
     * @param clazz class to get types.
     * @return ordered list of fields.
     */
    protected final Map<String, Field> getFieldMap(Class clazz) {
        Map<String, Field> fieldList = new HashMap<String, Field>();
        for (FieldEntry entry : getAllFields(clazz)) {
            Field field = entry.getValue();
            fieldList.put(field.getName(), field);
        }
        return fieldList;

    }

    public static class FieldEntry implements Map.Entry<Integer, Field>, Comparable<FieldEntry> {
        private Integer key = null;
        private Field value = null;

        public FieldEntry(Integer key, Field value) {
            this.key = key;
            this.value = value;
        }

        @Override
        public Integer getKey() {
            return this.key;
        }

        @Override
        public Field getValue() {
            return this.value;
        }

        @Override
        public Field setValue(Field value) {
            this.value = value;
            return value;
        }

        @Override
        public int compareTo(FieldEntry o) {
            return this.getKey().compareTo(o.getKey());
        }
    }

}


package io.robe.admin;

import com.google.inject.Module;
import io.dropwizard.Application;
import io.dropwizard.hibernate.UnitOfWork;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.robe.admin.cli.InitializeCommand;
import io.robe.admin.guice.module.HibernateModule;
import io.robe.admin.hibernate.dao.*;
import io.robe.admin.quartz.JobPersister;
import io.robe.admin.recaptcha.ReCaptchaValidation;
import io.robe.assets.AdvancedAssetBundle;
import io.robe.auth.token.TokenAuthBundle;
import io.robe.auth.token.TokenAuthenticator;
import io.robe.auth.token.jersey.TokenFactory;
import io.robe.common.exception.ExceptionMapperBinder;
import io.robe.common.exception.RobeExceptionMapper;
import io.robe.common.exception.RobeRuntimeException;
import io.robe.common.service.search.SearchFactoryProvider;
import io.robe.guice.GuiceBundle;
import io.robe.hibernate.RobeHibernateBundle;
import io.robe.mail.MailBundle;
import io.robe.quartz.QuartzBundle;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.LinkedList;
import java.util.List;

/**
 * Default io.robe.admin class of Robe.
 * If you extend this class on your applications io.robe.admin class and call super methods at
 * overridden methods you will still benefit of robe souse.
 */
public class RobeApplication<T extends RobeConfiguration> extends Application<T> {

    private static final Logger LOGGER = LoggerFactory.getLogger(RobeApplication.class);
    private static String configurationPath;
    private T config;

    public static void main(String[] args) throws Exception {
        RobeApplication application = new RobeApplication();
        application.run(args);
    }

    @Override
    public void run(String... args) throws Exception {
        if (args.length < 2) {
            LOGGER.error("Give a config yml path.");
        } else {
            configurationPath = args[1];
        }
        super.run(args);
    }

    /**
     * Adds
     * Hibernate bundle for PROVIDER connection
     * Asset bundle for io.robe.admin screens and
     * Class scanners for
     *

     *
Entities

     *
HealthChecks

     *
Providers

     *
InjectableProviders

     *
Resources

     *
Tasks

     *
Managed objects

     *

     *
     * @param bootstrap
     */
    @Override
    public void initialize(Bootstrap<T> bootstrap) {
        T config = loadConfiguration(bootstrap);
        RobeHibernateBundle<T> hibernateBundle = RobeHibernateBundle.createInstance(
                config.getHibernate().getScanPackages(),
                config.getHibernate().getEntities());

        List<Module> modules = new LinkedList<>();
        modules.add(new HibernateModule(hibernateBundle));
        bootstrap.addBundle(new GuiceBundle<T>(modules, bootstrap.getApplication().getConfigurationClass()));

        bootstrap.addBundle(hibernateBundle);
        bootstrap.addBundle(new TokenAuthBundle<T>());
        bootstrap.addCommand(new InitializeCommand(this, hibernateBundle));
        bootstrap.addBundle(new QuartzBundle<T>());
        bootstrap.addBundle(new MailBundle<T>());
        bootstrap.addBundle(new AdvancedAssetBundle<T>());

    }

    public T getConfiguration() {
        return config;
    }

    protected T loadConfiguration(Bootstrap bootstrap) {
        if (config == null) {
            try {

                config = (T) bootstrap.getConfigurationFactoryFactory().create(
                        bootstrap.getApplication().getConfigurationClass(),
                        bootstrap.getValidatorFactory().getValidator(),
                        bootstrap.getObjectMapper(), "")
                        .build(new File(configurationPath));
            } catch (Exception e) {
                throw new RobeRuntimeException("Can't load configuration :" + configurationPath, e);
            }
        }
        return config;

    }

    /**
     * {@inheritDoc}
     * In addition adds exception mapper.
     *
     * @param configuration
     * @param environment
     * @throws Exception
     */
    @UnitOfWork
    @Override
    public void run(T configuration, Environment environment) throws Exception {
        TokenFactory.authenticator = new TokenAuthenticator(
                GuiceBundle.getInjector().getInstance(UserDao.class),
                GuiceBundle.getInjector().getInstance(ServiceDao.class),
                GuiceBundle.getInjector().getInstance(RoleDao.class),
                GuiceBundle.getInjector().getInstance(PermissionDao.class),
                GuiceBundle.getInjector().getInstance(RoleGroupDao.class));

        TokenFactory.tokenKey = configuration.getAuth().getTokenKey();

        environment.jersey().register(RobeExceptionMapper.class);
        environment.jersey().register(new ExceptionMapperBinder(true));

        environment.jersey().register(new SearchFactoryProvider.Binder());
        environment.jersey().register(MultiPartFeature.class);

        if (configuration.getRecaptcha() != null) {
            new ReCaptchaValidation(configuration.getRecaptcha());
        }

        JobPersister jobPersister = new JobPersister(QuartzBundle.JOBS);
    }

}


package io.robe.admin.resources;

import io.dropwizard.hibernate.UnitOfWork;
import io.dropwizard.jersey.PATCH;
import io.robe.admin.hibernate.dao.*;
import io.robe.admin.hibernate.entity.*;
import io.robe.auth.Credentials;
import io.robe.auth.RobeAuth;
import io.robe.auth.data.entry.PermissionEntry;
import io.robe.common.service.RobeService;
import io.robe.common.service.search.SearchParam;
import io.robe.common.service.search.model.SearchModel;
import io.robe.common.utils.reflection.Fields;
import org.hibernate.FlushMode;

import javax.inject.Inject;
import javax.validation.Valid;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.hibernate.CacheMode.GET;

@Path("roles")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class RoleResource {

    @Inject
    private RoleDao roleDao;

    @Inject
    private PermissionDao permissionDao;

    @Inject
    private RoleGroupDao roleGroupDao;

    @Inject
    private ServiceDao serviceDao;

    @Inject
    private MenuDao menuDao;

    /**
     * Returns all services and menus collection with the matches given Role id.
     *
     * @param credentials Injected by {@link RobeAuth} annotation for authentication.
     * @param id          This is Role oid
     * @return JSONObject (MENU and SERVICE)
     */

    @RobeService(group = "Permission", description = "Returns all services and menus collection with the matches given Role id.")
    @GET
    @Path("{id}/permissions")
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    public Map<String, Object> getRolePermissions(@RobeAuth Credentials credentials, @PathParam("id") String id) {

        List<Permission> permissions = new ArrayList<>();

        List<Service> services = new ArrayList<>();
        List<Menu> menus = new ArrayList<>();

        Role role = roleDao.findById(id);

        getAllRolePermissions(role, permissions);

        for (Permission permission : permissions) {
            if (permission.getType().equals(PermissionEntry.Type.SERVICE)) {
                Service service = serviceDao.findById(permission.getRestrictedItemOid());
                if (service != null) {
                    if (services.indexOf(service) == -1) {
                        services.add(service);
                    }
                }

            } else if (permission.getType().equals(PermissionEntry.Type.MENU)) {

                Menu menu = menuDao.findById(permission.getRestrictedItemOid());
                if (menu != null) {
                    if (menus.indexOf(menu) == -1) {
                        menus.add(menu);
                    }
                }
            }
        }

        Map<String, Object> response = new HashMap<>();
        response.put("menu", menus);
        response.put("service", services);

        return response;
    }

    private void getAllRolePermissions(Role parent, List<Permission> rolePermissions) {
        rolePermissions.addAll(permissionDao.findByRoleOId(parent.getId()));
        List<RoleGroup> roleGroupEntries = roleGroupDao.findByGroupOId(parent.getId());
        for (RoleGroup entry : roleGroupEntries) {
            Role role = roleDao.findById(entry.getRoleId());
            getAllRolePermissions(role, rolePermissions);
        }
    }

    /**
     * Return all Role as a collection
     *
     * @param credentials auto fill by {@link RobeAuth} annotation for authentication.
     * @return all {@link Role} as a collection
     */
    @RobeService(group = "Role", description = "Returns all Role as a collection.")
    @GET
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    public List<Role> getAll(@RobeAuth Credentials credentials, @SearchParam SearchModel search) {
        return roleDao.findAllStrict(search);
    }

    /**
     * Return a Role resource  with the matches given id.
     *

     * Status Code:
     * Not Found  404
     *
     * @param credentials auto fill by @{@link RobeAuth} annotation for authentication.
     * @param id          This is  the oid of {@link Role}
     * @return a  {@link Role} resource with the matches given id.
     */
    @RobeService(group = "Role", description = "Returns a Role resource with the matches given id.")
    @Path("{id}")
    @GET
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    public Role get(@RobeAuth Credentials credentials, @PathParam("id") String id) {
        Role entity = roleDao.findById(id);
        if (entity == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        return entity;
    }

    /**
     * Create a {@link Role} resource.
     *
     * @param credentials auto fill by @{@link RobeAuth} annotation for authentication.
     * @param model       This is the one model of {@link Role}
     * @return create a {@link Role} resource.
     */
    @RobeService(group = "Role", description = "Create a Role resource.")
    @POST
    @UnitOfWork
    public Role create(@RobeAuth Credentials credentials, @Valid Role model) {
        return roleDao.create(model);
    }

    /**
     * Update a Role resource  with the matches given id.
     *

     * Status Code:
     * Not Found  404
     * Not Matches 412
     *
     * @param credentials auto fill by @{@link RobeAuth} annotation for authentication.
     * @param id          This is  the oid of {@link Role}
     * @param model       This is the one model of {@link Role}
     * @return Update a  {@link Role} resource with the matches given id.
     */
    @RobeService(group = "Role", description = "Update a Role resource with the matches given id.")
    @Path("{id}")
    @PUT
    @UnitOfWork
    public Role update(@RobeAuth Credentials credentials, @PathParam("id") String id, @Valid Role model) {

        if (!id.equals(model.getOid())) {
            throw new WebApplicationException(Response.status(412).build());
        }
        Role entity = roleDao.findById(id);
        if (entity == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        roleDao.detach(entity);
        return roleDao.update(model);
    }

    /**
     * Update a Role resource with the matches given id.
     *

     * Status Code:
     * Not Found  404
     * Not Matches 412
     *
     * @param credentials auto fill by @{@link RobeAuth} annotation for authentication.
     * @param id          This is  the oid of {@link Role}
     * @param model       This is the one model of {@link Role}
     * @return Updates a  {@link Role} resource with the matches given id.
     */
    @RobeService(group = "Role", description = "Update a Role resource with the matches given id.")
    @Path("{id}")
    @PATCH
    @UnitOfWork
    public Role merge(@RobeAuth Credentials credentials, @PathParam("id") String id, Role model) {

        if (!id.equals(model.getOid()))
            throw new WebApplicationException(Response.status(412).build());
        Role dest = roleDao.findById(id);
        if (dest == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        Fields.mergeRight(model, dest);
        return roleDao.update(dest);
    }

    /**
     * Delete a Role resource  with the matches given id.
     *

     * Status Code:
     * Not Found  404
     * Not Matches 412
     *
     * @param credentials auto fill by @{@link RobeAuth} annotation for authentication.
     * @param id          This is  the oid of {@link Role}
     * @param model       This is the one model of {@link Role}
     * @return Delete a  {@link Role} resource  with the matches given id.
     */
    @RobeService(group = "Role", description = "Delete a Role resource with the matches given id.")
    @Path("{id}")
    @DELETE
    @UnitOfWork
    public Role delete(@RobeAuth Credentials credentials, @PathParam("id") String id, @Valid Role model) {
        if (!id.equals(model.getOid())) {
            throw new WebApplicationException(Response.status(412).build());
        }
        Role entity = roleDao.findById(id);
        if (entity == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        return roleDao.delete(entity);
    }

}


package io.robe.admin.resources;

import io.dropwizard.hibernate.UnitOfWork;
import io.dropwizard.jersey.PATCH;
import io.robe.admin.hibernate.dao.ServiceDao;
import io.robe.admin.hibernate.entity.Service;
import io.robe.auth.Credentials;
import io.robe.auth.RobeAuth;
import io.robe.auth.data.entry.ServiceEntry;
import io.robe.common.service.RobeService;
import io.robe.common.service.search.SearchParam;
import io.robe.common.service.search.model.SearchModel;
import io.robe.common.utils.reflection.Fields;
import io.robe.guice.GuiceBundle;
import io.robe.guice.GuiceConfiguration;
import org.hibernate.FlushMode;
import org.reflections.Reflections;

import javax.inject.Inject;
import javax.validation.Valid;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;

import static org.hibernate.CacheMode.GET;

@Path("services")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ServiceResource {

    @Inject
    private ServiceDao serviceDao;

    /**
     * Returns all {@link Service ) as a collection.
     *
     * @param credentials Injected by {@link RobeAuth } annotation for authentication.
     * @return all {@link Service} as a collection.
     */
    @RobeService(group = "Service", description = "Returns all Services as a collection.")
    @GET
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    public List<Service> getAll(@RobeAuth Credentials credentials, @SearchParam SearchModel search) {
        return serviceDao.findAllStrict(search);
    }

    /**
     * Return {@link Service) resource and matches with the given id.
     *

     * Status Code:
     * Not Found  404
     *
     * @param credentials Injected by {@link RobeAuth} annotation for authentication.
     * @param id          This is  the oid of {@link Service}
     * @return {@link Service} resource matches with the given id.
     */
    @RobeService(group = "Service", description = "Return Service resource.")
    @Path("{id}")
    @GET
    @UnitOfWork(readOnly = true, cacheMode = GET, flushMode = FlushMode.MANUAL)
    public Service get(@RobeAuth Credentials credentials, @PathParam("id") String id) {
        Service entity = serviceDao.findById(id);
        if (entity == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        return entity;
    }

    /**
     * Create {@link Service) resource and matches with the given id.
     *
     * @param credentials Injected by {@link RobeAuth} annotation for authentication.
     * @param model       This is the one model of {@link Service}
     * @return Create {@link Service) resource and return given Service path link at header Location=example/{id].
     */
    @RobeService(group = "Service", description = "Create Service resource and return given Service path link at header Location=example/{id].")
    @POST
    @UnitOfWork
    public Service create(@RobeAuth Credentials credentials, @Valid Service model) {
        return serviceDao.create(model);
    }

    /**
     * Update {@link Service) resource and matches with the given id.
     *

     * Status Code:
     * Not Found  404
     * Not Matches 412
     *
     * @param credentials Injected by {@link RobeAuth} annotation for authentication.
     * @param id          This is  the oid of {@link Service}
     * @param model       This is the one model of {@link Service}
     * @return Update {@link Service} resource and matches with the given id.
     */
    @RobeService(group = "Service", description = "Update Service resource and matches with the given id.")
    @Path("{id}")
    @PUT
    @UnitOfWork
    public Service update(@RobeAuth Credentials credentials, @PathParam("id") String id, @Valid Service model) {
        if (!id.equals((model.getOid()))) {
            throw new WebApplicationException(Response.status(412).build());
        }
        Service entity = serviceDao.findById(id);
        if (entity == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        serviceDao.detach(entity);
        return serviceDao.update(model);
    }

    /**
     * Update {@link Service) resource and matches with the given id.
     *

     * Status Code:
     * Not Found  404
     * Not Matches 412
     *
     * @param credentials Injected by {@link RobeAuth} annotation for authentication.
     * @param id          This is  the oid of {@link Service}
     * @param model       This is the one model of {@link Service}
     * @return Update {@link Service) resource and matches with the given id.
     */
    @RobeService(group = "Service", description = "Update Service resource.")
    @PATCH
    @UnitOfWork
    @Path("{id}")
    public Service merge(@RobeAuth Credentials credentials, @PathParam("id") String id, Service model) {
        if (!id.equals(model.getOid()))
            throw new WebApplicationException(Response.status(412).build());
        Service dest = serviceDao.findById(id);
        if (dest == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        Fields.mergeRight(model, dest);
        return serviceDao.update(dest);
    }

    /**
     * Delete {@link Service) resource and matches with the given id.
     *

     * Status Code:
     * Not Found  404
     * Not Matches 412
     *
     * @param credentials Injected by {@link RobeAuth} annotation for authentication.
     * @param id          This is  the oid of {@link Service}
     * @param model       This is the one model of {@link Service}
     * @return Delete {@link Service) resource.
     */
    @RobeService(group = "Service", description = "Delete Service resource.")
    @Path("{id}")
    @DELETE
    @UnitOfWork
    public Service delete(@RobeAuth Credentials credentials, @PathParam("id") String id, @Valid Service model) {

        if (!id.equals(model.getOid())) {
            throw new WebApplicationException(Response.status(412).build());
        }
        Service entity = serviceDao.findById(id);
        if (entity == null) {
            throw new WebApplicationException(Response.status(404).build());
        }
        return serviceDao.delete(entity);
    }

    /**
     * @param credentials Injected by {@link RobeAuth} annotation for authentication.
     * @return collection of {@link Service} groups
     */
    @RobeService(group = "Permission", description = "Get all services group for permission")
    @Path("groups")
    @GET
    @UnitOfWork
    public List<Service> listGroups(@RobeAuth Credentials credentials) {
        return serviceDao.findServiceByGroups();
    }

    /**
     * refreshing service with description
     *

     * TODO exception handler
     *
     * @param credentials Injected by {@link RobeAuth} annotation for authentication.
     * @return Response.OK
     */

    @Path("refresh")
    @GET
    @UnitOfWork
    public Response refreshServices(@RobeAuth Credentials credentials) {

        GuiceConfiguration configuration = GuiceBundle.getConfiguration();

        Reflections reflections = new Reflections(configuration.getScanPackages(), this.getClass().getClassLoader());
        Set<Class<?>> services = reflections.getTypesAnnotatedWith(Path.class);
        int count = 0;
        for (Class service : services) {
            String parentPath = "/" + ((Path) service.getAnnotation(Path.class)).value();
            for (Method method : service.getMethods()) {
                String httpMethod = ifServiceGetHttpMethod(method);
                if (httpMethod == null) {
                    continue;
                }
                String path = parentPath;
                path = extractPath(method, path);

                io.robe.admin.hibernate.entity.Service entity = serviceDao.findByPathAndMethod(path, ServiceEntry.Method.valueOf(httpMethod));
                RobeService robeService = (RobeService) method.getAnnotation(RobeService.class);

                if (entity != null) {
                    if (robeService != null) {
                        entity.setDescription(robeService.description());
                        entity.setGroup(robeService.group());
                        serviceDao.update(entity);
                    }
                    continue;
                }
                entity = new io.robe.admin.hibernate.entity.Service();
                entity.setPath(path);
                entity.setMethod(io.robe.admin.hibernate.entity.Service.Method.valueOf(httpMethod));
                if (robeService != null) {
                    entity.setDescription(robeService.description());
                    entity.setGroup(robeService.group());
                } else {
                    entity.setGroup("UNGROUPED");
                    entity.setDescription("");

                }
                entity.setDescription(entity.getDescription() + " (" + entity.getMethod() + " " + entity.getPath() + ")");
                serviceDao.create(entity);
                count++;

            }
        }
        return Response.ok(count).build();
    }

    private String extractPath(Method method, String path) {
        if (method.getAnnotation(Path.class) != null) {
            path += "/" + method.getAnnotation(Path.class).value();
            path = path.replaceAll("//", "/");
        }
        return path;
    }

    private String ifServiceGetHttpMethod(Method method) {
        if (method.getAnnotation(GET.class) != null)
            return "GET";
        if (method.getAnnotation(PUT.class) != null)
            return "PUT";
        if (method.getAnnotation(POST.class) != null)
            return "POST";
        if (method.getAnnotation(DELETE.class) != null)
            return "DELETE";
        if (method.getAnnotation(PATCH.class) != null)
            return "PATCH";
        if (method.getAnnotation(OPTIONS.class) != null)
            return "OPTIONS";

        return null;
    }
}


package io.robe.common.exception;

import io.robe.common.dto.RobeMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

/**
 * Exception mapper for all exceptions thrown from application.
 * Aim of this mapper is to show  standardized JSON format for errors to the user..
 */
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class RobeExceptionMapper implements ExceptionMapper<Exception> {
    private static final Logger LOGGER = LoggerFactory.getLogger(RobeExceptionMapper.class);

    /**
     * Parses or wraps the exception and transforms it to a human readable json loaded response.
     *
     * @param e Exception to transform
     * @return error response
     */
    @Override
    public Response toResponse(Exception e) {

        String id = System.nanoTime() + "";
        LOGGER.error(id, e);

        if (e instanceof RobeRuntimeException) {
            return ((RobeRuntimeException) e).getResponse(id);
        } else if (e instanceof ConstraintViolationException) {
            ConstraintViolationException exception = (ConstraintViolationException) e;
            RobeMessage[] errors = new RobeMessage[exception.getConstraintViolations().size()];
            int i = 0;
            for (ConstraintViolation error : exception.getConstraintViolations()) {
                errors[i++] = new RobeMessage.Builder().message(error.getMessage()).status(422).id(id).build();
            }
            return Response.status(422).entity(errors).type(MediaType.APPLICATION_JSON).build();
        } else if (e instanceof WebApplicationException) {
            WebApplicationException we = (WebApplicationException) e;
            RobeMessage error = new RobeMessage.Builder().id(id).message(we.getMessage()).status(we.getResponse().getStatus()).build();
            return Response.fromResponse(we.getResponse()).entity(error).type(MediaType.APPLICATION_JSON).build();
        } else {
            if (e.getClass().getName().equals("org.hibernate.exception.ConstraintViolationException")) {
                if (e.getCause() != null && e.getCause().getMessage() != null) {
                    RobeMessage error = new RobeMessage.Builder().message(e.getCause().getMessage().split("for")[0]).status(Response.Status.CONFLICT.getStatusCode()).id(id).build();
                    return Response.status(Response.Status.CONFLICT).entity(error).type(MediaType.APPLICATION_JSON).build();
                }
            }
            RobeMessage error = new RobeMessage.Builder().message(e.getMessage()).id(id).build();
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(error).type(MediaType.APPLICATION_JSON).build();
        }
    }
}


package io.robe.auth.token;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.hash.Hashing;
import com.google.common.io.BaseEncoding;
import io.robe.auth.Credentials;
import io.robe.auth.token.configuration.TokenBasedAuthConfiguration;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.joda.time.DateTime;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * A basic token implementation. Uses jasypt for encrypt & decrypt operations.
 * Takes all properties from configuration. Uses Guava for permission caching.
 * All cached permission entries will live with token.
 */
public class BasicToken implements Credentials {

    private static final PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
    private static final String SEPARATOR = "--";
    private static int defaultMaxAge;

    private static Cache<String, Set<String>> cache;

    private String userId;
    private String username;
    private DateTime expireAt;
    private String attributesHash;
    private String tokenString;
    private int maxAge;

    /**
     * Creates an access token with the given parameters.
     *
     * @param username   Username
     * @param expireAt   expiration time of token
     * @param attributes extra attributes to customize token
     * @return an instance
     */
    public BasicToken(String userId, String username, DateTime expireAt, Map<String, String> attributes) {
        this.userId = userId;
        this.username = username;
        this.expireAt = expireAt;
        this.maxAge = defaultMaxAge;
        generateAttributesHash(attributes);
    }

    /**
     * Creates an access token with the given tokenString.
     *
     * @param tokenString to parse
     * @return and instance
     * @throws Exception throws in case of failing opening token
     */
    public BasicToken(String tokenString) throws Exception {
        tokenString = tokenString.replaceAll("\"", "");
        tokenString = new String(BaseEncoding.base16().decode(tokenString));
        String[] parts = encryptor.decrypt(tokenString).split(SEPARATOR);
        this.userId = parts[0];
        this.username = parts[1];
        this.expireAt = new DateTime(Long.valueOf(parts[2]));
        this.attributesHash = parts[3];
    }

    /**
     * Configure method for Token generation configurations and encryptor configure
     *
     * @param configuration confiuration for auth bundle
     */
    public static void configure(TokenBasedAuthConfiguration configuration) {
        encryptor.setPoolSize(configuration.getPoolSize());          // This would be a good value for a 4-core system
        if (configuration.getServerPassword().equals("auto")) {
            encryptor.setPassword(UUID.randomUUID().toString());
        } else {
            encryptor.setPassword(configuration.getServerPassword());
        }
        encryptor.setAlgorithm(configuration.getAlgorithm());
        encryptor.initialize();
        BasicToken.defaultMaxAge = configuration.getMaxage();

        //Create cache for permissions.
        cache = CacheBuilder.newBuilder()
                .expireAfterAccess(defaultMaxAge, TimeUnit.SECONDS)
                .expireAfterWrite(defaultMaxAge, TimeUnit.SECONDS)
                .build();

    }

    public static void clearPermissionCache(String username) {
        cache.invalidate(username);
        cache.cleanUp();
    }

    public static void clearAllPermissionCache() {
        cache.invalidateAll();
        cache.cleanUp();
    }

    public static Set<String> getCurrentUsernames() {
        cache.cleanUp();
        return cache.asMap().keySet();
    }

    @Override
    public String getUserId() {
        return userId;
    }

    @Override
    public String getUsername() {
        return username;
    }

    public boolean isExpired() {
        return !expireAt.isAfterNow();
    }

    public void setExpiration(int durationInSeconds) {
        expireAt = DateTime.now().plusSeconds(durationInSeconds);
        resetTokenString();
    }

    public Date getExpirationDate() {
        return expireAt.toDate();
    }

    public String getAttributesHash() {
        return attributesHash;
    }

    /**
     * Generates attribute has with 'userAgent', 'remoteAddr' keys.
     * Combines them and hashes with SHA256 and sets the variable.
     *
     * @param attributes
     */
    private void generateAttributesHash(Map<String, String> attributes) {
        StringBuilder attr = new StringBuilder();
        attr.append(attributes.get("userAgent"));
//        attr.append(attributes.get("remoteAddr")); TODO: add remote ip address after you find how to get remote IP from HttpContext
        attributesHash = Hashing.sha256().hashString(attr.toString(), StandardCharsets.UTF_8).toString();
        resetTokenString();
    }

    public String getTokenString() throws Exception {
        return tokenString == null ? generateTokenString() : tokenString;
    }

    /**
     * Generates a tokenString with a new expiration date and assigns it.
     *
     * @return new tokenString
     * @throws Exception
     */
    private String generateTokenString() throws Exception {
        //Renew age
        //Stringify token data
        StringBuilder dataString = new StringBuilder();
        dataString
                .append(getUserId())
                .append(SEPARATOR)
                .append(getUsername())
                .append(SEPARATOR)
                .append(getExpirationDate().getTime())
                .append(SEPARATOR)
                .append(attributesHash);

        // Encrypt token data string
        String newTokenString = encryptor.encrypt(dataString.toString());
        newTokenString = BaseEncoding.base16().encode(newTokenString.getBytes());
        tokenString = newTokenString;
        return newTokenString;
    }

    public int getMaxAge() {
        return maxAge < 1 ? defaultMaxAge : maxAge;
    }

    public void setMaxAge(int maxAge) {
        if (maxAge < 1) maxAge = defaultMaxAge;
        this.maxAge = maxAge;
    }

    public Set<String> getPermissions() {
        return cache.getIfPresent(getUsername());
    }

    /**
     * Sets permissions to the cache with current username
     *
     * @param permissions permission list for the current user.
     */
    public void setPermissions(Set<String> permissions) {
        cache.put(getUsername(), permissions);
    }

    private void resetTokenString() {
        tokenString = null;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof BasicToken)) return false;

        BasicToken that = (BasicToken) o;

        if (!userId.equals(that.userId)) return false;
        if (!username.equals(that.username)) return false;
        return attributesHash.equals(that.attributesHash);

    }

    @Override
    public int hashCode() {
        int result = userId.hashCode();
        result = 31 * result + username.hashCode();
        result = 31 * result + attributesHash.hashCode();
        return result;
    }

    public String getName() {
        return "BasicToken";
    }

    public static PooledPBEStringEncryptor getEncryptor() {
        return encryptor;
    }
}


package io.robe.mail;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.util.Map;
import java.util.Properties;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * A mail sender class.
 */
class MailSender {

    private static final Logger LOGGER = LoggerFactory.getLogger(MailSender.class);
    private static final Properties PROPERTIES = new Properties();
    private final MailConfiguration configuration;
    private Session session;

    /**
     * @param configuration mail configuration
     */
    public MailSender(MailConfiguration configuration) {
        checkNotNull(configuration);
        this.configuration = configuration;
        setProperties();
    }

    public MailConfiguration getConfiguration() {
        return configuration;
    }

    private void setProperties() {
        LOGGER.debug("Setting configuration.");
        LOGGER.debug(configuration.toString());

        PROPERTIES.putAll(configuration.getProperties());

        session = Session.getDefaultInstance(PROPERTIES, new javax.mail.Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(
                        PROPERTIES.get(configuration.getUsernameKey()).toString()
                        , PROPERTIES.get(configuration.getPasswordKey()).toString());
            }
        });
    }

    /**
     * Sends a mail with the given item.
     *
     * @param item MailItem to be send.
     */
    public void sendMessage(MailItem item) throws MessagingException {
        checkNotNull(item.getReceivers());
        checkNotNull(item.getReceivers().get(0));
        checkNotNull(item.getTitle());
        checkNotNull(item.getBody());

        //If sender is empty send with the account sender.
        Message msg = new MimeMessage(session);
        if (item.getSender() == null || item.getSender().length() == 0) {
            item.setSender(configuration.getProperties().get(configuration.getUsernameKey()).toString());
        }
        InternetAddress from = new InternetAddress(item.getSender());
        msg.setFrom(from);

        InternetAddress[] to = new InternetAddress[item.getReceivers().size()];
        for (int i = 0; i < item.getReceivers().size(); i++) {
            to[i] = new InternetAddress(item.getReceivers().get(i));
        }
        msg.setRecipients(Message.RecipientType.TO, to);

        msg.setSubject(item.getTitle());

        MimeBodyPart body = new MimeBodyPart();
        body.setContent(item.getBody(), "text/html; charset=UTF-8");
        Multipart content = new MimeMultipart();
        content.addBodyPart(body);

        if (item.getAttachments() != null && item.getAttachments().size() > 0) {
            for (DataSource attachment : item.getAttachments()) {
                BodyPart itemBodyPart = new MimeBodyPart();
                itemBodyPart.setDataHandler(new DataHandler(attachment));
                itemBodyPart.setFileName(attachment.getName());
                content.addBodyPart(itemBodyPart);
            }
        }

        msg.setContent(content);

        //update headers
        msg.saveChanges();

        Transport.send(msg);

        // set header value
        for (Map.Entry<String, String[]> entry : item.getHeaders().entrySet()) {
            String[] value = msg.getHeader(entry.getKey());
            if (value != null) {
                entry.setValue(value);
            }
        }

    }

}


package io.robe.hibernate;

import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import com.google.common.collect.ImmutableList;
import io.dropwizard.Configuration;
import io.dropwizard.db.PooledDataSourceFactory;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.hibernate.SessionFactoryFactory;
import io.robe.hibernate.entity.BaseEntity;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.Entity;
import java.util.HashSet;
import java.util.Set;

/**
 * Hibernate bundle with Entity classpath scanner.
 * Takes scannable paths from configuration yml from entityPackage element as array separated by ','
 */
public class RobeHibernateBundle<T extends Configuration & HasHibernateConfiguration> extends HibernateBundle<T> {
    private static final Logger LOGGER = LoggerFactory.getLogger(RobeHibernateBundle.class);

    protected static RobeHibernateBundle instance;
    private org.hibernate.cfg.Configuration configuration;

    protected RobeHibernateBundle(ImmutableList<Class<?>> entities, SessionFactoryFactory sessionFactoryFactory) {
        super(entities, sessionFactoryFactory);
    }

    public static RobeHibernateBundle createInstance(String[] packages, String[] entities) {
        if (instance == null)
            instance = new RobeHibernateBundle(loadEntities(packages, entities), new RobeSessionFactoryFactory());
        else
            throw new RuntimeException("HibernateBundle is already created.");
        return instance;
    }

    public static final RobeHibernateBundle getInstance() {
        if (instance != null)
            return instance;
        else
            throw new RuntimeException("HibernateBundle is not created. Please call createInstance first.");
    }

    protected static final ImmutableList<Class<?>> loadEntities(String[] packages, String[] entities) {
        Set<Class<?>> classes = new HashSet<>();
        if (packages != null) {
            for (String packageName : packages) {
                LOGGER.info("Loading Package: " + packageName);
                Reflections reflections = new Reflections(packageName);
                classes.addAll(reflections.getTypesAnnotatedWith(Entity.class));
            }
        }
        if (entities != null) {
            for (String entity : entities) {
                try {
                    LOGGER.info("Loading Entity: " + entity);
                    Class entityClass = Class.forName(entity);
                    if (entityClass.isAnnotationPresent(Entity.class)) {
                        classes.add(entityClass);
                    } else {
                        LOGGER.warn("Class is not annotated with Entity: " + entity);
                    }
                } catch (ClassNotFoundException e) {
                    LOGGER.warn("Can't load class: " + entity, e);
                }
            }
        }
        return ImmutableList.<Class<?>>builder().add(BaseEntity.class).addAll(classes).build();
    }

    /**
     * read more https://github.com/dropwizard/dropwizard/issues/932
     *
     * @return Hibernate4Module
     */

    @Override
    protected Hibernate5Module createHibernate5Module() {
        Hibernate5Module module = new Hibernate5Module();
        module.disable(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION);
        return module;
    }

    public HibernateConfiguration getDatabaseConfiguration(T configuration) {
        return configuration.getHibernate();
    }

    @Override
    public PooledDataSourceFactory getDataSourceFactory(T configuration) {
        return configuration.getHibernate().getDataSourceFactory(configuration);
    }

    protected void configure(org.hibernate.cfg.Configuration configuration) {
        this.configuration = configuration;
    }

    public org.hibernate.cfg.Configuration getConfiguration() {
        return configuration;
    }

}