/** * {@inheritDoc} */ @Override public String getSubject(EventLogEvent event) { EventLogEventType type = event.getType(); String subject; switch (type) { case BUCKET_CHANGED: case BUCKET_CREATED: subject = "Experiment Bucket Update"; break; case EXPERIMENT_CHANGED: subject = "Experiment Changed"; break; case EXPERIMENT_CREATED: subject = "Experiment Created"; break; case SIMPLE_EVENT: default: throw new IllegalArgumentException("Can not handle EventType: " + type.toString()); } return subject; }
/** * The constructor uses the EventLog to register for updates on certain * events (at the moment this only listens to changes in Buckets and Experiments) * * @param eventLog the event log we want to sign up for * @param emailService the email service * @param emailTextProcessor the email text processor */ @Inject public EmailEventLogListener(final EventLog eventLog, final EmailService emailService, final EmailTextProcessor emailTextProcessor) { this.eventLog = eventLog; //so at the moment we are only interested in Bucket and Experiment Updates List<Class<? extends EventLogEvent>> abo = new ArrayList<>(); abo.add(ExperimentEvent.class); abo.add(BucketEvent.class); this.eventLog.register(this, abo); this.emailTextProcessor = emailTextProcessor; this.emailService = emailService; }
/** * {@inheritDoc} */ @Override public void erasePageData(Application.Name applicationName, Experiment.ID experimentID, UserInfo user) { pagesRepository.erasePageData(applicationName, experimentID); Experiment experiment = experiments.getExperiment(experimentID); if (experiment != null) { eventLog.postEvent(new ExperimentChangeEvent(user, experiment, "pages", "all pages", null)); } }
@Override protected void configure() { LOGGER.debug("installing module: {}", ExperimentsModule.class.getSimpleName()); install(new EventLogModule()); install(new CassandraRepositoryModule()); bind(Experiments.class).to(ExperimentsImpl.class).in(SINGLETON); bind(Buckets.class).to(BucketsImpl.class).in(SINGLETON); bind(Mutex.class).to(MutexImpl.class).in(SINGLETON); bind(Pages.class).to(PagesImpl.class).in(SINGLETON); bind(Priorities.class).to(PrioritiesImpl.class).in(SINGLETON); bind(Favorites.class).to(FavoritesImpl.class).in(SINGLETON); bind(ExperimentValidator.class).in(SINGLETON); LOGGER.debug("installed module: {}", ExperimentsModule.class.getSimpleName()); } }
@Override public void deleteUserRole(UserInfo.Username userID, Application.Name applicationName, UserInfo admin) { authorizationRepository.deleteUserRole(userID, applicationName); UserInfo user = getUserInfo(userID); eventLog.postEvent(new AuthorizationChangeEvent(admin, applicationName, user, "", "")); }
eventLog.postEvent(new ExperimentCreateEvent(user, newExperiment));
/** * {@inheritDoc} */ @Override public Bucket createBucket(Experiment.ID experimentID, Bucket newBucket, UserInfo user) { Experiment experiment = experiments.getExperiment(experimentID); if (experiment == null) { throw new ExperimentNotFoundException(experimentID); } validateExperimentState(experiment); checkBucketConstraint(experiment, newBucket); LOGGER.debug("Add Bucket: adding new bucket to running experiment" + experiment.getID()); cassandraRepository.createBucket(newBucket); try { databaseRepository.createBucket(newBucket); } catch (RepositoryException e) { cassandraRepository.deleteBucket(newBucket.getExperimentID(), newBucket.getLabel()); throw e; } //if we just created an experiment in a running experiment, update the remaining allocation percentages if (!Experiment.State.DRAFT.equals(experiment.getState())) { eventLog.postEvent(new BucketCreateEvent(user, experiment, newBucket)); BucketList updates = buckets.adjustAllocationPercentages(experiment, newBucket); buckets.updateBucketAllocBatch(experimentID, updates); } return getBucket(experimentID, newBucket.getLabel()); }
/** * {@inheritDoc} */ @Override public BucketList adjustAllocationPercentages(Experiment experiment, Bucket newBucket) { double remainingAlloc = 1. - newBucket.getAllocationPercent(); BucketList bucketList = buckets.getBuckets(experiment.getID(), false /* don't check experiment again */); BucketList newBuckets = new BucketList(); for (Bucket bucket : bucketList.getBuckets()) { if (bucket.getLabel().equals(newBucket.getLabel())) { continue; } double newAlloc = roundToTwo(remainingAlloc * bucket.getAllocationPercent()); LOGGER.debug("Add Bucket: setting allocation percentage for bucket " + bucket.getLabel() + " in experiment " + experiment.getID() + " to: " + newAlloc); Bucket.Builder builder = Bucket.from(bucket).withAllocationPercent(newAlloc); Bucket updatedBucket = builder.build(); newBuckets.addBucket(updatedBucket); if (!Experiment.State.DRAFT.equals(experiment.getState()) && Double.compare(bucket.getAllocationPercent(), updatedBucket.getAllocationPercent()) != 0) { // this is a system event, so no user needed eventLog.postEvent(new BucketChangeEvent(experiment, updatedBucket, "allocation", String.valueOf(bucket.getAllocationPercent()), String.valueOf(updatedBucket.getAllocationPercent()))); } } return newBuckets; }
eventLog.postEvent(new BucketDeleteEvent(user, experiment, bucket));
@Override protected void configure() { LOGGER.debug("installing module: {}", AuthorizationModule.class.getSimpleName()); install(new AuthenticationModule()); install(new EventLogModule()); install(new CassandraRepositoryModule()); Properties properties = create(PROPERTY_NAME, AuthorizationModule.class); String authorizationClassName = getProperty("authorization.class.name", properties, "com.intuit.wasabi.authorization.impl.DefaultAuthorization"); try { @SuppressWarnings("unchecked") Class<Authorization> authorizationClass = (Class<Authorization>) forName(authorizationClassName); bind(Authorization.class).to(authorizationClass).in(SINGLETON); } catch (ClassNotFoundException e) { throw new AuthenticationException("unable to find authorization class: " + authorizationClassName, e); } LOGGER.debug("installed module: {}", AuthorizationModule.class.getSimpleName()); } }
/** * {@inheritDoc} */ @Override public void deletePage(Experiment.ID experimentID, Page.Name pageName, UserInfo user) { Application.Name applicationName = getApplicationNameForModifyingPages(experimentID); pagesRepository.deletePage(applicationName, experimentID, pageName); Experiment experiment = experiments.getExperiment(experimentID); if (experiment != null) { eventLog.postEvent(new ExperimentChangeEvent(user, experiment, "pages", pageName.toString(), null)); } LOGGER.info("event=EXPERIMENT_METADATA_CHANGE, message=PAGE_REMOVED, applicationName={}, experimentName={}, configuration=[pageName={}, userName={}]", experiment.getApplicationName(), experiment.getLabel(), pageName, user.getUsername()); }
@Override public void assignUserToSuperAdminRole(final UserInfo candidateUserInfo, final UserInfo assigningUserInfo) { LOGGER.debug("Assigning super admin role to user={} by user={} ", candidateUserInfo, assigningUserInfo); UserRoleList userRoleList = getUserRoleList(candidateUserInfo.getUsername()); LOGGER.debug("User role list {}", userRoleList); boolean isSuperAdmin = userRoleList.getRoleList().stream().anyMatch((UserRole ur) -> ur.getRole().equals(Role.SUPERADMIN)); Preconditions.checkArgument(!isSuperAdmin, "User %s is already a superadmin", candidateUserInfo.getUsername()); authorizationRepository.assignUserToSuperAdminRole(candidateUserInfo); eventLog.postEvent(new AuthorizationChangeEvent(assigningUserInfo, null, candidateUserInfo, null, Role.SUPERADMIN.toString())); }
/** * {@inheritDoc} */ @Override public Set<String> getAddressees(EventLogEvent event) { Set<String> addressants = new HashSet<>(); EventLogEventType type = event.getType(); switch (type) { case BUCKET_CHANGED: case BUCKET_CREATED: case EXPERIMENT_CHANGED: case EXPERIMENT_CREATED: addressants.addAll(getExperimentAddressor((ExperimentEvent) event)); break; case SIMPLE_EVENT: default: throw new IllegalArgumentException("Can not handle EventType: " + type.toString()); } return addressants; }
/** * Initializes the audit log. * * @param eventLog the event log to subscribe to * @param threadPoolSizeCore the core threadpool size (java property {@code auditlog.threadpoolsize.core}) * @param threadPoolSizeMax the max threadpool size (java property {@code auditlog.threadpoolsize.max}) * @param repository the audit log repository */ @Inject public AuditLogListenerImpl(final EventLog eventLog, final @Named(AUDITLOG_THREADPOOLSIZE_CORE) int threadPoolSizeCore, final @Named(AUDITLOG_THREADPOOLSIZE_MAX) int threadPoolSizeMax, final AuditLogRepository repository) { this.repository = repository; eventLog.register(this); threadPoolExecutor = new ThreadPoolExecutor(threadPoolSizeCore, threadPoolSizeMax, 0L, MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
LOGGER.debug("installing module: {}", AuditLogModule.class.getSimpleName()); install(new EventLogModule());
/** * {@inheritDoc} */ @Override public void updateExperimentState(final Experiment experiment, final Experiment.State state) { try { cassandraRepository.updateExperimentState(experiment, state); // To maintain consistency, revert the changes made in cassandra in case the mysql update fails try { databaseRepository.updateExperimentState(experiment, state); } catch (Exception exception) { cassandraRepository.updateExperimentState(experiment, experiment.getState()); throw exception; } eventLog.postEvent(new ExperimentChangeEvent(experiment, "state", experiment.getState().toString(), state.toString())); } catch (Exception exception) { LOGGER.error("Updating experiment state for experiment:{} failed with error:", experiment, exception); throw exception; } LOGGER.info("event=EXPERIMENT_METADATA_CHANGE, message=EXPERIMENT_STATE_UPDATED, applicationName={}, configuration=[experimentName={}, oldState={}, newState={}]", experiment.getApplicationName(), experiment.getLabel(), experiment.getState(), state); } }
@Override public void removeUserFromSuperAdminRole(final UserInfo candidateUserInfo, final UserInfo assigningUserInfo) { LOGGER.debug("Removing user={} from superadmin by assigningUser={}", candidateUserInfo, assigningUserInfo); List<UserRole> allSuperAdmins = getSuperAdminRoleList(); LOGGER.debug("Current superadmins {}", allSuperAdmins); Preconditions.checkArgument(allSuperAdmins.size() > 1, "Cannot delete. SuperAdmins less than 1"); boolean isSuperAdmin = allSuperAdmins.stream().anyMatch((UserRole ur) -> ur.getRole().equals(Role.SUPERADMIN) && ur.getUserID().equals(candidateUserInfo.getUsername())); Preconditions.checkArgument(isSuperAdmin, "User %s is not a superadmin", candidateUserInfo.getUsername()); authorizationRepository.removeUserFromSuperAdminRole(candidateUserInfo); eventLog.postEvent(new AuthorizationChangeEvent(assigningUserInfo, null, candidateUserInfo, Role.SUPERADMIN.toString(), null)); }
/** * {@inheritDoc} */ @Override public String getMessage(EventLogEvent event) { EventLogEventType type = event.getType(); String message = ""; switch (type) { case BUCKET_CHANGED: message = getBucketChangedMessage((BucketChangeEvent) event); break; case BUCKET_CREATED: message = getBucketCreatedMessage((BucketCreateEvent) event); break; case EXPERIMENT_CHANGED: message = getExperimentChangedMessage((ExperimentChangeEvent) event); break; case EXPERIMENT_CREATED: message = getExperimentCreatedMessage((ExperimentCreateEvent) event); break; case SIMPLE_EVENT: default: throw new IllegalArgumentException("Can not handle EventType: " + type.toString()); } return message; }
/** * {@inheritDoc} */ @Override public void postPages(Experiment.ID experimentID, ExperimentPageList experimentPageList, UserInfo user) { Application.Name applicationName = getApplicationNameForModifyingPages(experimentID); pagesRepository.postPages(applicationName, experimentID, experimentPageList); Experiment experiment = experiments.getExperiment(experimentID); if (experiment != null) { List<String> pageNames = new ArrayList<>(); for (ExperimentPage experimentPage : experimentPageList.getPages()) { pageNames.add(experimentPage.getName().toString()); } String pageString = StringUtils.join(pageNames, ", "); eventLog.postEvent(new ExperimentChangeEvent(user, experiment, "pages", null, pageString)); LOGGER.info("event=EXPERIMENT_METADATA_CHANGE, message=PAGES_ADDED, applicationName={}, experimentName={}, configuration=[pages={}]", experiment.getApplicationName(), experiment.getLabel(), pageString); } }
/** * {@inheritDoc} */ @Override public void deleteExclusion(Experiment.ID expID_1, Experiment.ID expID_2, UserInfo user) { Experiment exp_1 = experiments.getExperiment(expID_1); Experiment exp_2 = experiments.getExperiment(expID_2); // Check that expID_1 is a valid experiment if (exp_1 == null) { throw new ExperimentNotFoundException(expID_1); } // Check that expID_2 is a valid experiment if (exp_2 == null) { throw new ExperimentNotFoundException(expID_2); } mutexRepository.deleteExclusion(expID_1, expID_2); eventLog.postEvent(new ExperimentChangeEvent(user, exp_1, "mutex", exp_2.getLabel().toString(), null)); LOGGER.info("event=EXPERIMENT_METADATA_CHANGE, message=MUTUAL_EXCLUSION_DELETED, applicationName={}, configuration=[experiment1={}, experiment2={}]", exp_1.getApplicationName(), exp_1.getLabel(), exp_2.getLabel()); }