Implemented user management
This commit is contained in:
parent
f41e8fa6b2
commit
2475784d52
46 changed files with 1124 additions and 460 deletions
|
@ -12,6 +12,7 @@ buildscript {
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "net.ltgt.apt" version "0.10"
|
id "net.ltgt.apt" version "0.10"
|
||||||
|
id 'fi.jasoft.plugin.vaadin' version '1.1.10'
|
||||||
}
|
}
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'eclipse'
|
apply plugin: 'eclipse'
|
||||||
|
@ -20,7 +21,9 @@ apply plugin: 'org.springframework.boot'
|
||||||
group 'com.faendir'
|
group 'com.faendir'
|
||||||
version = '0.1.0-SNAPSHOT'
|
version = '0.1.0-SNAPSHOT'
|
||||||
sourceCompatibility = 1.8
|
sourceCompatibility = 1.8
|
||||||
ext.debug = true;
|
ext {
|
||||||
|
debug = true
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'org.springframework.boot:spring-boot-starter-data-mongodb'
|
compile 'org.springframework.boot:spring-boot-starter-data-mongodb'
|
||||||
|
@ -31,17 +34,39 @@ dependencies {
|
||||||
compile 'org.ocpsoft.prettytime:prettytime:3.2.7.Final'
|
compile 'org.ocpsoft.prettytime:prettytime:3.2.7.Final'
|
||||||
compile 'commons-fileupload:commons-fileupload:1.3.2'
|
compile 'commons-fileupload:commons-fileupload:1.3.2'
|
||||||
compile 'net.sf.proguard:proguard-retrace:5.3.3'
|
compile 'net.sf.proguard:proguard-retrace:5.3.3'
|
||||||
|
compile 'org.vaadin.addons:popupbutton:3.0.0'
|
||||||
|
|
||||||
compileOnly project(':annotation')
|
compileOnly project(':annotation')
|
||||||
apt project(':annotationprocessor')
|
apt project(':annotationprocessor')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
'vaadin-client' {
|
||||||
|
resolutionStrategy {
|
||||||
|
dependencySubstitution {
|
||||||
|
substitute module('javax.validation:validation-api') with module('javax.validation:validation-api:1.0.0.GA')
|
||||||
|
}
|
||||||
|
//force 'javax.validation:validation-api:1.0.0.GA'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencyManagement {
|
dependencyManagement {
|
||||||
imports {
|
imports {
|
||||||
mavenBom "com.vaadin:vaadin-bom:8.0.6"
|
mavenBom "com.vaadin:vaadin-bom:8.0.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ext.debug){
|
bootRun {
|
||||||
|
// Needed by Vaadin plugin (https://github.com/johndevs/gradle-vaadin-plugin/issues/325)
|
||||||
|
classpath += configurations['vaadin-server']
|
||||||
|
classpath += configurations['vaadin-client']
|
||||||
|
|
||||||
|
dependsOn 'vaadinCompile', 'vaadinThemeCompile'
|
||||||
|
}
|
||||||
|
|
||||||
|
project.convention.getPlugin(WarPluginConvention).webAppDirName = "src/main/resources"
|
||||||
|
|
||||||
|
if (!ext.debug) {
|
||||||
apply from: 'releasepackaging.gradle'
|
apply from: 'releasepackaging.gradle'
|
||||||
}
|
}
|
|
@ -1,38 +0,0 @@
|
||||||
package com.faendir.acra.data;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.util.Base64Utils;
|
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Lukas
|
|
||||||
* @since 22.03.2017
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class AppManager {
|
|
||||||
private final SecureRandom secureRandom;
|
|
||||||
private final AppRepository appRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public AppManager(SecureRandom secureRandom, AppRepository appRepository) {
|
|
||||||
this.secureRandom = secureRandom;
|
|
||||||
this.appRepository = appRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public App createNewApp(String name){
|
|
||||||
byte[] bytes = new byte[12];
|
|
||||||
secureRandom.nextBytes(bytes);
|
|
||||||
return appRepository.save(new App(name, Base64Utils.encodeToString(bytes)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<App> getApps(){
|
|
||||||
return appRepository.findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public App getApp(String id){
|
|
||||||
return appRepository.findOne(id);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package com.faendir.acra.data;
|
|
||||||
|
|
||||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Lukas
|
|
||||||
* @since 22.03.2017
|
|
||||||
*/
|
|
||||||
public interface AppRepository extends MongoRepository<App, String> {
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package com.faendir.acra.data;
|
|
||||||
|
|
||||||
import com.mongodb.BasicDBObjectBuilder;
|
|
||||||
import com.mongodb.gridfs.GridFSDBFile;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.data.mongodb.core.query.Criteria;
|
|
||||||
import org.springframework.data.mongodb.core.query.Query;
|
|
||||||
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Lukas
|
|
||||||
* @since 19.05.2017
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class AttachmentManager {
|
|
||||||
private final GridFsTemplate gridFsTemplate;
|
|
||||||
private final Logger logger;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public AttachmentManager(GridFsTemplate gridFsTemplate) {
|
|
||||||
this.gridFsTemplate = gridFsTemplate;
|
|
||||||
logger = LoggerFactory.getLogger(AttachmentManager.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveAttachments(String report, List<MultipartFile> attachments) {
|
|
||||||
for (MultipartFile a : attachments) {
|
|
||||||
try {
|
|
||||||
gridFsTemplate.store(a.getInputStream(), a.getOriginalFilename(), a.getContentType(), new BasicDBObjectBuilder().add("reportId", report).get());
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.warn("Failed to load attachment", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<GridFSDBFile> getAttachments(String report) {
|
|
||||||
return gridFsTemplate.find(new Query(Criteria.where("metadata.reportId").is(report)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeAttachments(String report) {
|
|
||||||
gridFsTemplate.delete(new Query(Criteria.where("metadata.reportId").is(report)));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package com.faendir.acra.data;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.data.domain.Example;
|
|
||||||
import org.springframework.data.domain.ExampleMatcher;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Lukas
|
|
||||||
* @since 19.05.2017
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class MappingManager {
|
|
||||||
private final MappingRepository mappingRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public MappingManager(MappingRepository mappingRepository) {
|
|
||||||
this.mappingRepository = mappingRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addMapping(String app, int version, String mappings) {
|
|
||||||
mappingRepository.save(new ProguardMapping(app, version, mappings));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProguardMapping getMapping(String app, int version) {
|
|
||||||
return mappingRepository.findOne(new ProguardMapping.MetaData(app, version));
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ProguardMapping> getMappings(String app) {
|
|
||||||
return mappingRepository.findAll(Example.of(new ProguardMapping(app, -1, null), ExampleMatcher.matchingAny()));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package com.faendir.acra.data;
|
|
||||||
|
|
||||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Lukas
|
|
||||||
* @since 19.05.2017
|
|
||||||
*/
|
|
||||||
public interface MappingRepository extends MongoRepository<ProguardMapping, ProguardMapping.MetaData> {
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
package com.faendir.acra.data;
|
|
||||||
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.data.domain.Example;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Lukas
|
|
||||||
* @since 22.03.2017
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class ReportManager {
|
|
||||||
private final ReportRepository reportRepository;
|
|
||||||
private final AttachmentManager attachmentManager;
|
|
||||||
private final List<ChangeListener> listeners;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public ReportManager(ReportRepository reportRepository, AttachmentManager attachmentManager) {
|
|
||||||
this.reportRepository = reportRepository;
|
|
||||||
this.attachmentManager = attachmentManager;
|
|
||||||
listeners = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void newReport(JSONObject content) {
|
|
||||||
newReport(content, Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void newReport(JSONObject content, List<MultipartFile> attachments) {
|
|
||||||
Report report = reportRepository.save(new Report(content, SecurityContextHolder.getContext().getAuthentication().getName()));
|
|
||||||
attachmentManager.saveAttachments(report.getId(), attachments);
|
|
||||||
listeners.forEach(ChangeListener::onChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Report> getReports(String app) {
|
|
||||||
return reportRepository.findAll(Example.of(new Report(null, app)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Report getReport(String id) {
|
|
||||||
return reportRepository.findOne(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove(Report report){
|
|
||||||
reportRepository.delete(report);
|
|
||||||
attachmentManager.removeAttachments(report.getId());
|
|
||||||
listeners.forEach(ChangeListener::onChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean addListener(ChangeListener changeListener) {
|
|
||||||
return listeners.add(changeListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean removeListener(ChangeListener changeListener) {
|
|
||||||
return listeners.remove(changeListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ChangeListener {
|
|
||||||
void onChange();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package com.faendir.acra.data;
|
|
||||||
|
|
||||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Lukas
|
|
||||||
* @since 22.03.2017
|
|
||||||
*/
|
|
||||||
public interface ReportRepository extends MongoRepository<Report, String> {
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.faendir.acra.data;
|
package com.faendir.acra.mongod;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.faendir.acra.mongod.data;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.model.App;
|
||||||
|
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 22.03.2017
|
||||||
|
*/
|
||||||
|
interface AppRepository extends MongoRepository<App, String> {
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
package com.faendir.acra.mongod.data;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.model.App;
|
||||||
|
import com.faendir.acra.mongod.model.ProguardMapping;
|
||||||
|
import com.faendir.acra.mongod.model.Report;
|
||||||
|
import com.mongodb.BasicDBObjectBuilder;
|
||||||
|
import com.mongodb.gridfs.GridFSDBFile;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Example;
|
||||||
|
import org.springframework.data.domain.ExampleMatcher;
|
||||||
|
import org.springframework.data.mongodb.core.query.Criteria;
|
||||||
|
import org.springframework.data.mongodb.core.query.Query;
|
||||||
|
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 21.05.2017
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class DataManager {
|
||||||
|
private final MappingRepository mappingRepository;
|
||||||
|
private final ReportRepository reportRepository;
|
||||||
|
private final List<ReportChangeListener> listeners;
|
||||||
|
private final GridFsTemplate gridFsTemplate;
|
||||||
|
private final Logger logger;
|
||||||
|
private final SecureRandom secureRandom;
|
||||||
|
private final AppRepository appRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public DataManager(SecureRandom secureRandom, AppRepository appRepository, GridFsTemplate gridFsTemplate, MappingRepository mappingRepository, ReportRepository reportRepository) {
|
||||||
|
this.secureRandom = secureRandom;
|
||||||
|
this.appRepository = appRepository;
|
||||||
|
logger = LoggerFactory.getLogger(DataManager.class);
|
||||||
|
this.gridFsTemplate = gridFsTemplate;
|
||||||
|
this.mappingRepository = mappingRepository;
|
||||||
|
this.reportRepository = reportRepository;
|
||||||
|
this.listeners = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createNewApp(String name){
|
||||||
|
byte[] bytes = new byte[12];
|
||||||
|
secureRandom.nextBytes(bytes);
|
||||||
|
appRepository.save(new App(name, Base64Utils.encodeToString(bytes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<App> getApps(){
|
||||||
|
return appRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public App getApp(String id){
|
||||||
|
return appRepository.findOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteApp(String id){
|
||||||
|
appRepository.delete(id);
|
||||||
|
getReports(id).forEach(this::remove);
|
||||||
|
mappingRepository.delete(getMappings(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveAttachments(String report, List<MultipartFile> attachments) {
|
||||||
|
for (MultipartFile a : attachments) {
|
||||||
|
try {
|
||||||
|
gridFsTemplate.store(a.getInputStream(), a.getOriginalFilename(), a.getContentType(), new BasicDBObjectBuilder().add("reportId", report).get());
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.warn("Failed to load attachment", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GridFSDBFile> getAttachments(String report) {
|
||||||
|
return gridFsTemplate.find(new Query(Criteria.where("metadata.reportId").is(report)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeAttachments(String report) {
|
||||||
|
gridFsTemplate.delete(new Query(Criteria.where("metadata.reportId").is(report)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addMapping(String app, int version, String mappings) {
|
||||||
|
mappingRepository.save(new ProguardMapping(app, version, mappings));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProguardMapping getMapping(String app, int version) {
|
||||||
|
return mappingRepository.findOne(new ProguardMapping.MetaData(app, version));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ProguardMapping> getMappings(String app) {
|
||||||
|
return mappingRepository.findAll(Example.of(new ProguardMapping(app, -1, null), ExampleMatcher.matchingAny()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void newReport(JSONObject content) {
|
||||||
|
newReport(content, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void newReport(JSONObject content, List<MultipartFile> attachments) {
|
||||||
|
Report report = reportRepository.save(new Report(content, SecurityContextHolder.getContext().getAuthentication().getName()));
|
||||||
|
saveAttachments(report.getId(), attachments);
|
||||||
|
listeners.forEach(ReportChangeListener::onChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Report> getReports(String app) {
|
||||||
|
return reportRepository.findAll(Example.of(new Report(null, app)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Report getReport(String id) {
|
||||||
|
return reportRepository.findOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Report report){
|
||||||
|
reportRepository.delete(report);
|
||||||
|
removeAttachments(report.getId());
|
||||||
|
listeners.forEach(ReportChangeListener::onChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addListener(ReportChangeListener reportChangeListener) {
|
||||||
|
return listeners.add(reportChangeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean removeListener(ReportChangeListener reportChangeListener) {
|
||||||
|
return listeners.remove(reportChangeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ReportChangeListener {
|
||||||
|
void onChange();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.faendir.acra.mongod.data;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.model.ProguardMapping;
|
||||||
|
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 19.05.2017
|
||||||
|
*/
|
||||||
|
interface MappingRepository extends MongoRepository<ProguardMapping, ProguardMapping.MetaData> {
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.faendir.acra.mongod.data;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.model.Report;
|
||||||
|
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 22.03.2017
|
||||||
|
*/
|
||||||
|
interface ReportRepository extends MongoRepository<Report, String> {
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
package com.faendir.acra.data;
|
package com.faendir.acra.mongod.data;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.model.Bug;
|
||||||
|
import com.faendir.acra.mongod.model.ProguardMapping;
|
||||||
|
import com.faendir.acra.mongod.model.Report;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import proguard.retrace.ReTrace;
|
import proguard.retrace.ReTrace;
|
||||||
|
|
||||||
|
@ -35,7 +38,7 @@ public final class ReportUtils {
|
||||||
return bugs;
|
return bugs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Date getDateFromString(String s) {
|
public static Date getDateFromString(String s) {
|
||||||
try {
|
try {
|
||||||
return dateFormat.parse(s);
|
return dateFormat.parse(s);
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
|
@ -43,7 +46,7 @@ public final class ReportUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static String retrace(String stacktrace, ProguardMapping mapping) throws IOException {
|
public static String retrace(String stacktrace, ProguardMapping mapping) throws IOException {
|
||||||
File file = File.createTempFile("mapping", ".txt");
|
File file = File.createTempFile("mapping", ".txt");
|
||||||
FileUtils.writeStringToFile(file, mapping.getMappings());
|
FileUtils.writeStringToFile(file, mapping.getMappings());
|
||||||
StringWriter writer = new StringWriter();
|
StringWriter writer = new StringWriter();
|
|
@ -1,4 +1,4 @@
|
||||||
package com.faendir.acra.data;
|
package com.faendir.acra.mongod.model;
|
||||||
|
|
||||||
import org.springframework.data.mongodb.core.mapping.Document;
|
import org.springframework.data.mongodb.core.mapping.Document;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.faendir.acra.data;
|
package com.faendir.acra.mongod.model;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.faendir.acra.mongod.model;
|
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 20.05.2017
|
||||||
|
*/
|
||||||
|
public class Permission implements GrantedAuthority {
|
||||||
|
|
||||||
|
public enum Level {
|
||||||
|
NONE,
|
||||||
|
VIEW,
|
||||||
|
EDIT,
|
||||||
|
ADMIN
|
||||||
|
}
|
||||||
|
|
||||||
|
private Level level;
|
||||||
|
private String app;
|
||||||
|
|
||||||
|
public Permission() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Permission(String app, Level level) {
|
||||||
|
this.level = level;
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Level getLevel() {
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLevel(Level level) {
|
||||||
|
this.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApp() {
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthority() {
|
||||||
|
return "PERMISSION_" + level.name() + "_" + app;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
Permission that = (Permission) o;
|
||||||
|
|
||||||
|
return app.equals(that.app);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return app.hashCode();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.faendir.acra.data;
|
package com.faendir.acra.mongod.model;
|
||||||
|
|
||||||
import org.springframework.data.mongodb.core.mapping.Document;
|
import org.springframework.data.mongodb.core.mapping.Document;
|
||||||
|
|
||||||
|
@ -33,11 +33,11 @@ public class ProguardMapping {
|
||||||
return mappings;
|
return mappings;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class MetaData implements Serializable {
|
public static class MetaData implements Serializable {
|
||||||
private String app;
|
private String app;
|
||||||
private int version;
|
private int version;
|
||||||
|
|
||||||
MetaData(String app, int version) {
|
public MetaData(String app, int version) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
}
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
package com.faendir.acra.data;
|
package com.faendir.acra.mongod.model;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.data.DataManager;
|
||||||
|
import com.faendir.acra.mongod.data.ReportUtils;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.springframework.data.mongodb.core.index.Indexed;
|
import org.springframework.data.mongodb.core.index.Indexed;
|
||||||
|
@ -47,11 +49,11 @@ public class Report {
|
||||||
return getValueSafe("STACK_TRACE", content::getString, "");
|
return getValueSafe("STACK_TRACE", content::getString, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDeObfuscatedStacktrace(MappingManager mappingManager){
|
public String getDeObfuscatedStacktrace(DataManager dataManager){
|
||||||
if (deObfuscatedTrace != null) {
|
if (deObfuscatedTrace != null) {
|
||||||
return deObfuscatedTrace;
|
return deObfuscatedTrace;
|
||||||
}
|
}
|
||||||
ProguardMapping mapping = mappingManager.getMapping(app, getVersionCode());
|
ProguardMapping mapping = dataManager.getMapping(app, getVersionCode());
|
||||||
if (mapping != null) {
|
if (mapping != null) {
|
||||||
try {
|
try {
|
||||||
deObfuscatedTrace = ReportUtils.retrace(getStacktrace(), mapping);
|
deObfuscatedTrace = ReportUtils.retrace(getStacktrace(), mapping);
|
|
@ -0,0 +1,88 @@
|
||||||
|
package com.faendir.acra.mongod.model;
|
||||||
|
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.mongodb.core.mapping.Document;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 20.05.2017
|
||||||
|
*/
|
||||||
|
@Document
|
||||||
|
public class User implements UserDetails {
|
||||||
|
@Id
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
private Set<String> roles;
|
||||||
|
private Set<Permission> permissions;
|
||||||
|
|
||||||
|
public User() {
|
||||||
|
permissions = new HashSet<>();
|
||||||
|
roles = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public User(String username, String password, Collection<String> roles) {
|
||||||
|
this();
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
this.roles = new HashSet<>(roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Permission> getPermissions() {
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getRoles() {
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
List<GrantedAuthority> authorities = new ArrayList<>(permissions);
|
||||||
|
authorities.addAll(roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()));
|
||||||
|
return authorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonExpired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonLocked() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCredentialsNonExpired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
package com.faendir.acra.mongod.user;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.data.DataManager;
|
||||||
|
import com.faendir.acra.mongod.model.Permission;
|
||||||
|
import com.faendir.acra.mongod.model.User;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 20.05.2017
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class UserManager {
|
||||||
|
public static final String ROLE_ADMIN = "ROLE_ADMIN";
|
||||||
|
public static final String ROLE_USER = "ROLE_USER";
|
||||||
|
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final DataManager dataManager;
|
||||||
|
private final String defaultUser;
|
||||||
|
private final String defaultPassword;
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public UserManager(UserRepository userRepository, DataManager dataManager, @Value("${security.user.name}") String defaultUser, @Value("${security.user.password}") String defaultPassword) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.dataManager = dataManager;
|
||||||
|
this.defaultUser = defaultUser;
|
||||||
|
this.defaultPassword = defaultPassword;
|
||||||
|
passwordEncoder = new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureValidPermissions(User user) {
|
||||||
|
user.getPermissions().removeIf(permission -> dataManager.getApp(permission.getApp()) == null);
|
||||||
|
dataManager.getApps().stream().filter(app -> user.getPermissions().stream().noneMatch(permission -> permission.getApp().equals(app.getId())))
|
||||||
|
.forEach(app -> user.getPermissions().add(new Permission(app.getId(), user.getRoles().contains(ROLE_ADMIN) ? Permission.Level.ADMIN : Permission.Level.NONE)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser(String username) {
|
||||||
|
User user = userRepository.findOne(username);
|
||||||
|
if (user == null && defaultUser.equals(username)) {
|
||||||
|
return getDefaultUser();
|
||||||
|
}
|
||||||
|
ensureValidPermissions(user);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean createUser(String username, String password) {
|
||||||
|
if (userRepository.exists(username)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
User user = new User(username, passwordEncoder.encode(password), Collections.singleton(ROLE_USER));
|
||||||
|
ensureValidPermissions(user);
|
||||||
|
userRepository.save(user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkPassword(User user, String password) {
|
||||||
|
return user != null && passwordEncoder.matches(password, user.getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean changePassword(User user, String oldPassword, String newPassword) {
|
||||||
|
if (checkPassword(user, oldPassword)) {
|
||||||
|
user.setPassword(newPassword);
|
||||||
|
userRepository.save(user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAdmin(User user, boolean admin) {
|
||||||
|
if (admin) user.getRoles().add(ROLE_ADMIN);
|
||||||
|
else user.getRoles().remove(ROLE_ADMIN);
|
||||||
|
userRepository.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User setPermission(User user, String app, Permission.Level level) {
|
||||||
|
Optional<Permission> permission = user.getPermissions().stream().filter(p -> p.getApp().equals(app)).findAny();
|
||||||
|
if (permission.isPresent()) {
|
||||||
|
permission.get().setLevel(level);
|
||||||
|
} else {
|
||||||
|
user.getPermissions().add(new Permission(app, level));
|
||||||
|
}
|
||||||
|
return userRepository.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasPermission(User user, String app, Permission.Level level) {
|
||||||
|
ensureValidPermissions(user);
|
||||||
|
Optional<Permission> optional = user.getPermissions().stream().filter(permission -> permission.getApp().equals(app)).findAny();
|
||||||
|
return optional.isPresent() && optional.get().getLevel().ordinal() >= level.ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<User> getUsers() {
|
||||||
|
List<User> users = userRepository.findAll();
|
||||||
|
if (users.stream().noneMatch(user -> user.getUsername().equals(defaultUser))) {
|
||||||
|
users.add(getDefaultUser());
|
||||||
|
}
|
||||||
|
users.forEach(this::ensureValidPermissions);
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
private User getDefaultUser() {
|
||||||
|
return new User(defaultUser, passwordEncoder.encode(defaultPassword), Arrays.asList(ROLE_USER, ROLE_ADMIN));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.faendir.acra.mongod.user;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.model.User;
|
||||||
|
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 20.05.2017
|
||||||
|
*/
|
||||||
|
interface UserRepository extends MongoRepository<User, String> {
|
||||||
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
package com.faendir.acra.security;
|
|
||||||
|
|
||||||
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Lukas
|
|
||||||
* @since 13.05.2017
|
|
||||||
*/
|
|
||||||
public class SecurityInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
|
|
||||||
@Override
|
|
||||||
protected Class<?>[] getRootConfigClasses() {
|
|
||||||
return new Class[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Class<?>[] getServletConfigClasses() {
|
|
||||||
return new Class[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String[] getServletMappings() {
|
|
||||||
return new String[0];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.faendir.acra.security;
|
package com.faendir.acra.security;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.model.Permission;
|
||||||
|
import com.faendir.acra.mongod.user.UserManager;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
@ -18,4 +20,16 @@ public final class SecurityUtils {
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
return authentication != null && authentication.getAuthorities().contains(new SimpleGrantedAuthority(role));
|
return authentication != null && authentication.getAuthorities().contains(new SimpleGrantedAuthority(role));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getUsername() {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
return authentication != null ? authentication.getName() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasPermission(String app, Permission.Level level) {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
return authentication != null && authentication.getAuthorities().stream().filter(authority -> authority instanceof Permission)
|
||||||
|
.map(Permission.class::cast).filter(permission -> permission.getApp().equals(app)).findAny().map(Permission::getLevel)
|
||||||
|
.orElseGet(() -> hasRole(UserManager.ROLE_ADMIN) ? Permission.Level.ADMIN : Permission.Level.NONE).ordinal() >= level.ordinal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
package com.faendir.acra.security;
|
package com.faendir.acra.security;
|
||||||
|
|
||||||
import com.faendir.acra.data.App;
|
import com.faendir.acra.mongod.data.DataManager;
|
||||||
import com.faendir.acra.data.AppManager;
|
import com.faendir.acra.mongod.model.App;
|
||||||
|
import com.faendir.acra.mongod.model.User;
|
||||||
|
import com.faendir.acra.mongod.user.UserManager;
|
||||||
import com.vaadin.server.VaadinService;
|
import com.vaadin.server.VaadinService;
|
||||||
import com.vaadin.server.VaadinSession;
|
import com.vaadin.server.VaadinSession;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
@ -20,12 +21,15 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.context.SecurityContextImpl;
|
import org.springframework.security.core.context.SecurityContextImpl;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Lukas
|
* @author Lukas
|
||||||
* @since 22.03.2017
|
* @since 22.03.2017
|
||||||
|
@ -39,57 +43,62 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
SecurityContextHolder.setStrategyName(VaadinSessionSecurityContextHolderStrategy.class.getName());
|
SecurityContextHolder.setStrategyName(VaadinSessionSecurityContextHolderStrategy.class.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String user;
|
private final DataManager dataManager;
|
||||||
private final String password;
|
private final UserManager userManager;
|
||||||
private final AppManager appManager;
|
|
||||||
private final AuthenticationProvider authenticationProvider = new AuthenticationProvider() {
|
|
||||||
@Override
|
|
||||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
|
||||||
if (authentication instanceof UsernamePasswordAuthenticationToken) {
|
|
||||||
if (user.equals(authentication.getName())) {
|
|
||||||
if (password.equals(authentication.getCredentials())) {
|
|
||||||
return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(), AuthorityUtils.createAuthorityList("ROLE_ADMIN"));
|
|
||||||
} else {
|
|
||||||
throw new BadCredentialsException("Password mismatch for user " + authentication.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
App app = appManager.getApp(authentication.getName());
|
|
||||||
if (app != null) {
|
|
||||||
if (app.getPassword().equals(authentication.getCredentials())) {
|
|
||||||
Authentication auth = new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(), AuthorityUtils.createAuthorityList("ROLE_REPORTER"));
|
|
||||||
VaadinSession session = VaadinSession.getCurrent();
|
|
||||||
if (session == null) session = new VaadinSession(VaadinService.getCurrent());
|
|
||||||
VaadinSession.setCurrent(session);
|
|
||||||
SecurityContext securityContext = new SecurityContextImpl();
|
|
||||||
securityContext.setAuthentication(auth);
|
|
||||||
session.setAttribute(SecurityContext.class, securityContext);
|
|
||||||
return auth;
|
|
||||||
} else {
|
|
||||||
throw new BadCredentialsException("Password mismatch for user " + authentication.getName());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new UsernameNotFoundException("Username " + authentication.getName() + " not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supports(Class<?> authentication) {
|
|
||||||
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public WebSecurityConfig(@Value("${security.user.name}") String user, @Value("${security.user.password}") String password, AppManager appManager) {
|
public WebSecurityConfig(DataManager dataManager, UserManager userManager) {
|
||||||
this.user = user;
|
this.dataManager = dataManager;
|
||||||
this.password = password;
|
this.userManager = userManager;
|
||||||
this.appManager = appManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
auth.authenticationProvider(authenticationProvider);
|
auth.authenticationProvider(new AuthenticationProvider() {
|
||||||
|
@Override
|
||||||
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
|
if (authentication instanceof UsernamePasswordAuthenticationToken) {
|
||||||
|
App app = dataManager.getApp(authentication.getName());
|
||||||
|
User user;
|
||||||
|
if (app != null) {
|
||||||
|
if (app.getPassword().equals(authentication.getCredentials())) {
|
||||||
|
return getGrantedToken(app.getId(), app.getPassword(), AuthorityUtils.createAuthorityList("ROLE_REPORTER"));
|
||||||
|
} else {
|
||||||
|
throwBadCredentials(app.getId());
|
||||||
|
}
|
||||||
|
} else if ((user = userManager.getUser(authentication.getName())) != null) {
|
||||||
|
if (userManager.checkPassword(user, (String) authentication.getCredentials())) {
|
||||||
|
return getGrantedToken(user.getUsername(), user.getPassword(), user.getAuthorities());
|
||||||
|
} else {
|
||||||
|
throwBadCredentials(user.getUsername());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new UsernameNotFoundException("Username " + authentication.getName() + " not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Class<?> authentication) {
|
||||||
|
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void throwBadCredentials(String username) {
|
||||||
|
throw new BadCredentialsException("Password mismatch for user " + username);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Authentication getGrantedToken(String username, String password, Collection<? extends GrantedAuthority> authorities) {
|
||||||
|
Authentication auth = new UsernamePasswordAuthenticationToken(username, password, authorities);
|
||||||
|
VaadinSession session = VaadinSession.getCurrent();
|
||||||
|
if (session == null) session = new VaadinSession(VaadinService.getCurrent());
|
||||||
|
VaadinSession.setCurrent(session);
|
||||||
|
SecurityContext securityContext = new SecurityContextImpl();
|
||||||
|
securityContext.setAuthentication(auth);
|
||||||
|
session.setAttribute(SecurityContext.class, securityContext);
|
||||||
|
return auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.faendir.acra.service;
|
package com.faendir.acra.service;
|
||||||
|
|
||||||
import com.faendir.acra.data.ReportManager;
|
import com.faendir.acra.mongod.data.DataManager;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
@ -26,18 +26,18 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
public class ReportService {
|
public class ReportService {
|
||||||
private final ReportManager reportManager;
|
private final DataManager dataManager;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ReportService(ReportManager reportManager) {
|
public ReportService(DataManager dataManager) {
|
||||||
this.reportManager = reportManager;
|
this.dataManager = dataManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasRole('REPORTER')")
|
@PreAuthorize("hasRole('REPORTER')")
|
||||||
@RequestMapping(value = "/report", consumes = MediaType.APPLICATION_JSON_VALUE)
|
@RequestMapping(value = "/report", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||||
public void report(@RequestBody String content) throws IOException {
|
public void report(@RequestBody String content) throws IOException {
|
||||||
JSONObject jsonObject = new JSONObject(content);
|
JSONObject jsonObject = new JSONObject(content);
|
||||||
reportManager.newReport(jsonObject);
|
dataManager.newReport(jsonObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasRole('REPORTER')")
|
@PreAuthorize("hasRole('REPORTER')")
|
||||||
|
@ -56,7 +56,7 @@ public class ReportService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(jsonObject != null) {
|
if(jsonObject != null) {
|
||||||
reportManager.newReport(jsonObject, attachments);
|
dataManager.newReport(jsonObject, attachments);
|
||||||
return ResponseEntity.ok().build();
|
return ResponseEntity.ok().build();
|
||||||
}else {
|
}else {
|
||||||
return ResponseEntity.badRequest().build();
|
return ResponseEntity.badRequest().build();
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package com.faendir.acra.ui;
|
package com.faendir.acra.ui;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.user.UserManager;
|
||||||
import com.faendir.acra.security.SecurityUtils;
|
import com.faendir.acra.security.SecurityUtils;
|
||||||
|
import com.faendir.acra.ui.view.user.ChangePasswordView;
|
||||||
|
import com.faendir.acra.ui.view.user.UserManagerView;
|
||||||
import com.faendir.acra.util.Style;
|
import com.faendir.acra.util.Style;
|
||||||
import com.vaadin.annotations.Theme;
|
import com.vaadin.annotations.Theme;
|
||||||
import com.vaadin.server.VaadinRequest;
|
import com.vaadin.server.VaadinRequest;
|
||||||
import com.vaadin.server.VaadinService;
|
import com.vaadin.server.VaadinService;
|
||||||
import com.vaadin.spring.annotation.SpringUI;
|
import com.vaadin.spring.annotation.SpringUI;
|
||||||
import com.vaadin.spring.annotation.UIScope;
|
import com.vaadin.spring.annotation.UIScope;
|
||||||
|
import com.vaadin.ui.Alignment;
|
||||||
import com.vaadin.ui.Button;
|
import com.vaadin.ui.Button;
|
||||||
import com.vaadin.ui.HorizontalLayout;
|
import com.vaadin.ui.HorizontalLayout;
|
||||||
import com.vaadin.ui.LoginForm;
|
import com.vaadin.ui.LoginForm;
|
||||||
|
@ -46,9 +50,7 @@ public class BackendUI extends UI {
|
||||||
if (SecurityUtils.isLoggedIn()) {
|
if (SecurityUtils.isLoggedIn()) {
|
||||||
showMain();
|
showMain();
|
||||||
} else {
|
} else {
|
||||||
LoginForm loginForm = new LoginForm();
|
showLogin();
|
||||||
loginForm.addLoginListener(event -> login(event.getLoginParameter("username"), event.getLoginParameter("password")));
|
|
||||||
setContent(loginForm);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,10 +66,43 @@ public class BackendUI extends UI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void logout() {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
getPage().reload();
|
||||||
|
getSession().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showLogin() {
|
||||||
|
LoginForm loginForm = new LoginForm();
|
||||||
|
loginForm.addLoginListener(event -> login(event.getLoginParameter("username"), event.getLoginParameter("password")));
|
||||||
|
VerticalLayout layout = new VerticalLayout(loginForm);
|
||||||
|
layout.setSizeFull();
|
||||||
|
layout.setComponentAlignment(loginForm, Alignment.MIDDLE_CENTER);
|
||||||
|
setContent(layout);
|
||||||
|
}
|
||||||
|
|
||||||
private void showMain() {
|
private void showMain() {
|
||||||
NavigationManager navigationManager = applicationContext.getBean(NavigationManager.class);
|
NavigationManager navigationManager = applicationContext.getBean(NavigationManager.class);
|
||||||
HorizontalLayout header = new HorizontalLayout(new Button("Up", e -> navigationManager.navigateBack()));
|
Button up = new Button("Up", e -> navigationManager.navigateBack());
|
||||||
Style.apply(header, Style.MARGIN_TOP, Style.MARGIN_LEFT, Style.MARGIN_RIGHT);
|
HorizontalLayout header = new HorizontalLayout(up);
|
||||||
|
header.setExpandRatio(up, 1);
|
||||||
|
header.setWidth(100, Unit.PERCENTAGE);
|
||||||
|
|
||||||
|
if(SecurityUtils.hasRole(UserManager.ROLE_ADMIN)){
|
||||||
|
Button userManager = new Button("User Manager", e -> navigationManager.navigateTo(UserManagerView.class, ""));
|
||||||
|
header.addComponent(userManager);
|
||||||
|
header.setComponentAlignment(userManager, Alignment.MIDDLE_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
Button changePassword = new Button("Change Password", e-> navigationManager.navigateTo(ChangePasswordView.class, ""));
|
||||||
|
header.addComponent(changePassword);
|
||||||
|
header.setComponentAlignment(changePassword, Alignment.MIDDLE_RIGHT);
|
||||||
|
|
||||||
|
Button logout = new Button("Logout", e -> logout());
|
||||||
|
header.addComponent(logout);
|
||||||
|
header.setComponentAlignment(logout, Alignment.MIDDLE_RIGHT);
|
||||||
|
|
||||||
|
Style.apply(header, Style.PADDING_TOP, Style.PADDING_LEFT, Style.PADDING_RIGHT);
|
||||||
VerticalLayout root = new VerticalLayout(header, content);
|
VerticalLayout root = new VerticalLayout(header, content);
|
||||||
root.setExpandRatio(content, 1);
|
root.setExpandRatio(content, 1);
|
||||||
root.setSizeFull();
|
root.setSizeFull();
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package com.faendir.acra.ui;
|
package com.faendir.acra.ui;
|
||||||
|
|
||||||
import com.faendir.acra.gen.ViewDefinition;
|
import com.faendir.acra.gen.ViewDefinition;
|
||||||
import com.faendir.acra.ui.view.NamedView;
|
import com.faendir.acra.security.SecurityUtils;
|
||||||
|
import com.faendir.acra.ui.view.ErrorView;
|
||||||
|
import com.faendir.acra.ui.view.base.NamedView;
|
||||||
import com.vaadin.navigator.Navigator;
|
import com.vaadin.navigator.Navigator;
|
||||||
import com.vaadin.navigator.View;
|
import com.vaadin.navigator.View;
|
||||||
import com.vaadin.navigator.ViewProvider;
|
import com.vaadin.navigator.ViewProvider;
|
||||||
|
@ -37,7 +39,7 @@ public class NavigationManager {
|
||||||
@Override
|
@Override
|
||||||
public String getViewName(String viewAndParameters) {
|
public String getViewName(String viewAndParameters) {
|
||||||
String name = viewAndParameters.split("/", 2)[0];
|
String name = viewAndParameters.split("/", 2)[0];
|
||||||
if (views.stream().map(applicationContext::getBean).map(NamedView.class::cast).map(NamedView::getName).anyMatch(name::equals))
|
if (views.stream().map(applicationContext::getBean).map(NamedView.class::cast).filter(view -> SecurityUtils.hasRole(view.requiredRole())).map(NamedView::getName).anyMatch(name::equals))
|
||||||
return name;
|
return name;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -47,6 +49,7 @@ public class NavigationManager {
|
||||||
return views.stream().map(applicationContext::getBean).map(NamedView.class::cast).filter(view -> view.getName().equals(viewName)).findAny().orElse(null);
|
return views.stream().map(applicationContext::getBean).map(NamedView.class::cast).filter(view -> view.getName().equals(viewName)).findAny().orElse(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
navigator.setErrorView(ErrorView.class);
|
||||||
views = ViewDefinition.getViewClasses();
|
views = ViewDefinition.getViewClasses();
|
||||||
String target = Optional.ofNullable(ui.getPage().getLocation().getFragment()).orElse("").replace("!", "");
|
String target = Optional.ofNullable(ui.getPage().getLocation().getFragment()).orElse("").replace("!", "");
|
||||||
backStack.add(target);
|
backStack.add(target);
|
||||||
|
@ -56,8 +59,10 @@ public class NavigationManager {
|
||||||
public void navigateTo(Class<? extends NamedView> namedView, String contentId) {
|
public void navigateTo(Class<? extends NamedView> namedView, String contentId) {
|
||||||
NamedView view = applicationContext.getBean(namedView);
|
NamedView view = applicationContext.getBean(namedView);
|
||||||
String target = view.getName() + (contentId == null ? "" : "/" + contentId) + view.fragmentSuffix();
|
String target = view.getName() + (contentId == null ? "" : "/" + contentId) + view.fragmentSuffix();
|
||||||
backStack.add(0, target);
|
if (!backStack.get(0).equals(target)) {
|
||||||
navigator.navigateTo(target);
|
backStack.add(0, target);
|
||||||
|
navigator.navigateTo(target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void navigateBack() {
|
public void navigateBack() {
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
package com.faendir.acra.ui.view;
|
package com.faendir.acra.ui.view;
|
||||||
|
|
||||||
import com.faendir.acra.data.App;
|
import com.faendir.acra.mongod.data.DataManager;
|
||||||
import com.faendir.acra.data.AppManager;
|
import com.faendir.acra.mongod.model.App;
|
||||||
import com.faendir.acra.data.MappingManager;
|
import com.faendir.acra.mongod.model.Permission;
|
||||||
import com.faendir.acra.data.ReportManager;
|
import com.faendir.acra.security.SecurityUtils;
|
||||||
|
import com.faendir.acra.ui.view.base.NamedView;
|
||||||
|
import com.faendir.acra.ui.view.base.ReportList;
|
||||||
|
import com.faendir.acra.ui.view.tabs.BugTab;
|
||||||
|
import com.faendir.acra.ui.view.tabs.DeObfuscationTab;
|
||||||
|
import com.faendir.acra.ui.view.tabs.PropertiesTab;
|
||||||
import com.faendir.acra.util.Style;
|
import com.faendir.acra.util.Style;
|
||||||
import com.vaadin.navigator.ViewChangeListener;
|
import com.vaadin.navigator.ViewChangeListener;
|
||||||
import com.vaadin.spring.annotation.UIScope;
|
import com.vaadin.spring.annotation.UIScope;
|
||||||
|
@ -21,15 +26,11 @@ import org.springframework.stereotype.Component;
|
||||||
@Component
|
@Component
|
||||||
public class AppView extends NamedView {
|
public class AppView extends NamedView {
|
||||||
|
|
||||||
private final AppManager appManager;
|
private final DataManager dataManager;
|
||||||
private final ReportManager reportManager;
|
|
||||||
private final MappingManager mappingManager;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AppView(AppManager appManager, ReportManager reportManager, MappingManager mappingManager) {
|
public AppView(DataManager dataManager) {
|
||||||
this.appManager = appManager;
|
this.dataManager = dataManager;
|
||||||
this.reportManager = reportManager;
|
|
||||||
this.mappingManager = mappingManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -45,13 +46,16 @@ public class AppView extends NamedView {
|
||||||
@Override
|
@Override
|
||||||
public void enter(ViewChangeListener.ViewChangeEvent event) {
|
public void enter(ViewChangeListener.ViewChangeEvent event) {
|
||||||
String[] parameters = event.getParameters().split("/");
|
String[] parameters = event.getParameters().split("/");
|
||||||
App app = appManager.getApp(parameters[0]);
|
App app = dataManager.getApp(parameters[0]);
|
||||||
VerticalLayout statistics = new VerticalLayout(new Label("Coming soon"));
|
VerticalLayout statistics = new VerticalLayout(new Label("Coming soon"));
|
||||||
statistics.setCaption("Statistics");
|
statistics.setCaption("Statistics");
|
||||||
statistics.setSizeFull();
|
statistics.setSizeFull();
|
||||||
TabSheet tabSheet = new TabSheet(new BugTab(app.getId(), getNavigationManager(), reportManager),
|
TabSheet tabSheet = new TabSheet(new BugTab(app.getId(), getNavigationManager(), dataManager),
|
||||||
new ReportList(app.getId(), getNavigationManager(), reportManager),
|
new ReportList(app.getId(), getNavigationManager(), dataManager),
|
||||||
statistics, new DeObfuscationTab(app.getId(), mappingManager), new PropertiesTab(app));
|
statistics, new DeObfuscationTab(app.getId(), dataManager));
|
||||||
|
if(SecurityUtils.hasPermission(app.getId(), Permission.Level.ADMIN)){
|
||||||
|
tabSheet.addComponent(new PropertiesTab(app, dataManager, getNavigationManager()));
|
||||||
|
}
|
||||||
tabSheet.setSizeFull();
|
tabSheet.setSizeFull();
|
||||||
VerticalLayout content = new VerticalLayout(tabSheet);
|
VerticalLayout content = new VerticalLayout(tabSheet);
|
||||||
content.setSizeFull();
|
content.setSizeFull();
|
||||||
|
@ -66,6 +70,7 @@ public class AppView extends NamedView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tabSheet.addSelectedTabChangeListener(e -> getUI().getPage().setUriFragment(getName() + "/" + app.getId() + "/" + tabSheet.getSelectedTab().getCaption(), false));
|
tabSheet.addSelectedTabChangeListener(e -> getUI().getPage()
|
||||||
|
.setUriFragment(getName() + "/" + app.getId() + "/" + tabSheet.getSelectedTab().getCaption(), false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.faendir.acra.ui.view;
|
||||||
|
|
||||||
|
|
||||||
|
import com.vaadin.navigator.View;
|
||||||
|
import com.vaadin.navigator.ViewChangeListener;
|
||||||
|
import com.vaadin.ui.Alignment;
|
||||||
|
import com.vaadin.ui.CustomComponent;
|
||||||
|
import com.vaadin.ui.Label;
|
||||||
|
import com.vaadin.ui.VerticalLayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 21.05.2017
|
||||||
|
*/
|
||||||
|
public class ErrorView extends CustomComponent implements View {
|
||||||
|
@Override
|
||||||
|
public void enter(ViewChangeListener.ViewChangeEvent event) {
|
||||||
|
Label label = new Label("This page does not exist or you do not have the permission to view it.");
|
||||||
|
VerticalLayout layout = new VerticalLayout(label);
|
||||||
|
layout.setComponentAlignment(label, Alignment.MIDDLE_CENTER);
|
||||||
|
layout.setSizeFull();
|
||||||
|
setSizeFull();
|
||||||
|
setCompositionRoot(layout);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,12 @@
|
||||||
package com.faendir.acra.ui.view;
|
package com.faendir.acra.ui.view;
|
||||||
|
|
||||||
import com.faendir.acra.data.App;
|
import com.faendir.acra.mongod.data.DataManager;
|
||||||
import com.faendir.acra.data.AppManager;
|
import com.faendir.acra.mongod.model.App;
|
||||||
import com.faendir.acra.data.ReportManager;
|
import com.faendir.acra.mongod.model.Permission;
|
||||||
|
import com.faendir.acra.mongod.user.UserManager;
|
||||||
|
import com.faendir.acra.security.SecurityUtils;
|
||||||
|
import com.faendir.acra.ui.view.base.MyGrid;
|
||||||
|
import com.faendir.acra.ui.view.base.NamedView;
|
||||||
import com.faendir.acra.util.Style;
|
import com.faendir.acra.util.Style;
|
||||||
import com.vaadin.navigator.ViewChangeListener;
|
import com.vaadin.navigator.ViewChangeListener;
|
||||||
import com.vaadin.spring.annotation.UIScope;
|
import com.vaadin.spring.annotation.UIScope;
|
||||||
|
@ -12,10 +16,12 @@ import com.vaadin.ui.TextField;
|
||||||
import com.vaadin.ui.UI;
|
import com.vaadin.ui.UI;
|
||||||
import com.vaadin.ui.VerticalLayout;
|
import com.vaadin.ui.VerticalLayout;
|
||||||
import com.vaadin.ui.Window;
|
import com.vaadin.ui.Window;
|
||||||
import com.vaadin.ui.components.grid.FooterCell;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Lukas
|
* @author Lukas
|
||||||
* @since 23.03.2017
|
* @since 23.03.2017
|
||||||
|
@ -24,14 +30,12 @@ import org.springframework.stereotype.Component;
|
||||||
@Component
|
@Component
|
||||||
public class Overview extends NamedView {
|
public class Overview extends NamedView {
|
||||||
|
|
||||||
private final AppManager appManager;
|
private final DataManager dataManager;
|
||||||
private final ReportManager reportManager;
|
|
||||||
private MyGrid<App> grid;
|
private MyGrid<App> grid;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public Overview(AppManager appManager, ReportManager reportManager) {
|
public Overview(DataManager dataManager) {
|
||||||
this.appManager = appManager;
|
this.dataManager = dataManager;
|
||||||
this.reportManager = reportManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addApp() {
|
private void addApp() {
|
||||||
|
@ -39,9 +43,9 @@ public class Overview extends NamedView {
|
||||||
TextField name = new TextField("Name");
|
TextField name = new TextField("Name");
|
||||||
Button create = new Button("Create");
|
Button create = new Button("Create");
|
||||||
create.addClickListener(e -> {
|
create.addClickListener(e -> {
|
||||||
appManager.createNewApp(name.getValue());
|
dataManager.createNewApp(name.getValue());
|
||||||
window.close();
|
window.close();
|
||||||
grid.setItems(appManager.getApps());
|
grid.setItems(getApps());
|
||||||
|
|
||||||
});
|
});
|
||||||
VerticalLayout layout = new VerticalLayout(name, create);
|
VerticalLayout layout = new VerticalLayout(name, create);
|
||||||
|
@ -50,17 +54,22 @@ public class Overview extends NamedView {
|
||||||
UI.getCurrent().addWindow(window);
|
UI.getCurrent().addWindow(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<App> getApps(){
|
||||||
|
return dataManager.getApps().stream().filter(app -> SecurityUtils.hasPermission(app.getId(), Permission.Level.VIEW)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enter(ViewChangeListener.ViewChangeEvent event) {
|
public void enter(ViewChangeListener.ViewChangeEvent event) {
|
||||||
grid = new MyGrid<>("Apps", appManager.getApps());
|
grid = new MyGrid<>("Apps", getApps());
|
||||||
grid.setSizeFull();
|
grid.setSizeFull();
|
||||||
Grid.Column column = grid.addColumn(App::getName, "Name");
|
grid.setSelectionMode(Grid.SelectionMode.NONE);
|
||||||
grid.addColumn(app -> String.valueOf(reportManager.getReports(app.getId()).size()), "Reports");
|
grid.addColumn(App::getName, "Name");
|
||||||
FooterCell footerCell = grid.appendFooterRow().getCell(column.getId());
|
grid.addColumn(app -> String.valueOf(dataManager.getReports(app.getId()).size()), "Reports");
|
||||||
Button add = new Button("New App", e -> addApp());
|
|
||||||
add.setSizeFull();
|
|
||||||
footerCell.setComponent(add);
|
|
||||||
VerticalLayout layout = new VerticalLayout(grid);
|
VerticalLayout layout = new VerticalLayout(grid);
|
||||||
|
if(SecurityUtils.hasRole(UserManager.ROLE_ADMIN)){
|
||||||
|
Button add = new Button("New App", e -> addApp());
|
||||||
|
layout.addComponent(add);
|
||||||
|
}
|
||||||
Style.apply(layout, Style.NO_PADDING, Style.PADDING_LEFT, Style.PADDING_RIGHT, Style.PADDING_BOTTOM);
|
Style.apply(layout, Style.NO_PADDING, Style.PADDING_LEFT, Style.PADDING_RIGHT, Style.PADDING_BOTTOM);
|
||||||
setCompositionRoot(layout);
|
setCompositionRoot(layout);
|
||||||
grid.addItemClickListener(e -> getNavigationManager().navigateTo(AppView.class, e.getItem().getId()));
|
grid.addItemClickListener(e -> getNavigationManager().navigateTo(AppView.class, e.getItem().getId()));
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
package com.faendir.acra.ui.view;
|
|
||||||
|
|
||||||
import com.faendir.acra.data.App;
|
|
||||||
import com.vaadin.shared.ui.ContentMode;
|
|
||||||
import com.vaadin.ui.Label;
|
|
||||||
import com.vaadin.ui.UI;
|
|
||||||
import com.vaadin.ui.VerticalLayout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Lukas
|
|
||||||
* @since 19.05.2017
|
|
||||||
*/
|
|
||||||
public class PropertiesTab extends VerticalLayout {
|
|
||||||
public PropertiesTab(App app) {
|
|
||||||
String location = UI.getCurrent().getPage().getLocation().toASCIIString();
|
|
||||||
location = location.substring(0, location.indexOf('#'));
|
|
||||||
addComponent(new Label(String.format("Required ACRA configuration:<br><code>formUri = \"%sreport\",<br>" +
|
|
||||||
"formUriBasicAuthLogin = \"%s\",<br>formUriBasicAuthPassword = \"%s\",<br>" +
|
|
||||||
"httpMethod = HttpSender.Method.POST,<br>reportType = HttpSender.Type.JSON</code>",
|
|
||||||
location, app.getId(), app.getPassword()), ContentMode.HTML));
|
|
||||||
setCaption("Properties");
|
|
||||||
setSizeFull();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package com.faendir.acra.ui.view;
|
|
||||||
|
|
||||||
import com.faendir.acra.data.Report;
|
|
||||||
import com.faendir.acra.data.ReportManager;
|
|
||||||
import com.faendir.acra.ui.NavigationManager;
|
|
||||||
import com.faendir.acra.util.StringUtils;
|
|
||||||
import com.vaadin.ui.renderers.ButtonRenderer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Lukas
|
|
||||||
* @since 14.05.2017
|
|
||||||
*/
|
|
||||||
public class ReportList extends MyGrid<Report> implements ReportManager.ChangeListener {
|
|
||||||
private final String app;
|
|
||||||
private final ReportManager reportManager;
|
|
||||||
|
|
||||||
public ReportList(String app, NavigationManager navigationManager, ReportManager reportManager) {
|
|
||||||
super("Reports", reportManager.getReports(app));
|
|
||||||
this.app = app;
|
|
||||||
this.reportManager = reportManager;
|
|
||||||
setSizeFull();
|
|
||||||
addColumn(report -> StringUtils.distanceFromNowAsString(report.getDate()), "Date");
|
|
||||||
addColumn(report -> String.valueOf(report.getVersionCode()), "App Version");
|
|
||||||
addColumn(Report::getAndroidVersion, "Android Version");
|
|
||||||
addColumn(Report::getPhoneModel, "Device");
|
|
||||||
addColumn(report -> report.getStacktrace().split("\n", 2)[0], "Stacktrace").setExpandRatio(1);
|
|
||||||
addColumn(report -> "Delete", new ButtonRenderer<>(e -> reportManager.remove(e.getItem())));
|
|
||||||
addItemClickListener(e -> navigationManager.navigateTo(ReportView.class, e.getItem().getId()));
|
|
||||||
addAttachListener(e -> reportManager.addListener(this));
|
|
||||||
addDetachListener(e -> reportManager.removeListener(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChange() {
|
|
||||||
setItems(reportManager.getReports(app));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +1,8 @@
|
||||||
package com.faendir.acra.ui.view;
|
package com.faendir.acra.ui.view;
|
||||||
|
|
||||||
import com.faendir.acra.data.AttachmentManager;
|
import com.faendir.acra.mongod.data.DataManager;
|
||||||
import com.faendir.acra.data.MappingManager;
|
import com.faendir.acra.mongod.model.Report;
|
||||||
import com.faendir.acra.data.Report;
|
import com.faendir.acra.ui.view.base.NamedView;
|
||||||
import com.faendir.acra.data.ReportManager;
|
|
||||||
import com.faendir.acra.util.Style;
|
import com.faendir.acra.util.Style;
|
||||||
import com.mongodb.gridfs.GridFSDBFile;
|
import com.mongodb.gridfs.GridFSDBFile;
|
||||||
import com.vaadin.navigator.ViewChangeListener;
|
import com.vaadin.navigator.ViewChangeListener;
|
||||||
|
@ -35,15 +34,11 @@ import java.util.stream.Stream;
|
||||||
@org.springframework.stereotype.Component
|
@org.springframework.stereotype.Component
|
||||||
public class ReportView extends NamedView {
|
public class ReportView extends NamedView {
|
||||||
|
|
||||||
private final ReportManager reportManager;
|
private final DataManager dataManager;
|
||||||
private final AttachmentManager attachmentManager;
|
|
||||||
private final MappingManager mappingManager;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ReportView(ReportManager reportManager, AttachmentManager attachmentManager, MappingManager mappingManager) {
|
public ReportView(DataManager dataManager) {
|
||||||
this.reportManager = reportManager;
|
this.dataManager = dataManager;
|
||||||
this.attachmentManager = attachmentManager;
|
|
||||||
this.mappingManager = mappingManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Stream<Component> getLayoutForEntry(String key, Object value) {
|
private Stream<Component> getLayoutForEntry(String key, Object value) {
|
||||||
|
@ -81,8 +76,8 @@ public class ReportView extends NamedView {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enter(ViewChangeListener.ViewChangeEvent event) {
|
public void enter(ViewChangeListener.ViewChangeEvent event) {
|
||||||
Report report = reportManager.getReport(event.getParameters());
|
Report report = dataManager.getReport(event.getParameters());
|
||||||
List<GridFSDBFile> attachmentList = attachmentManager.getAttachments(report.getId());
|
List<GridFSDBFile> attachmentList = dataManager.getAttachments(report.getId());
|
||||||
HorizontalLayout attachments = new HorizontalLayout(attachmentList.stream().map(file -> {
|
HorizontalLayout attachments = new HorizontalLayout(attachmentList.stream().map(file -> {
|
||||||
Button button = new Button(file.getFilename());
|
Button button = new Button(file.getFilename());
|
||||||
new FileDownloader(new StreamResource(file::getInputStream, file.getFilename())).extend(button);
|
new FileDownloader(new StreamResource(file::getInputStream, file.getFilename())).extend(button);
|
||||||
|
@ -92,7 +87,7 @@ public class ReportView extends NamedView {
|
||||||
summaryGrid.addComponents(new Label("Version", ContentMode.PREFORMATTED), new Label(report.getVersionName(), ContentMode.PREFORMATTED));
|
summaryGrid.addComponents(new Label("Version", ContentMode.PREFORMATTED), new Label(report.getVersionName(), ContentMode.PREFORMATTED));
|
||||||
summaryGrid.addComponents(new Label("Email", ContentMode.PREFORMATTED), new Label(report.getUserEmail(), ContentMode.PREFORMATTED));
|
summaryGrid.addComponents(new Label("Email", ContentMode.PREFORMATTED), new Label(report.getUserEmail(), ContentMode.PREFORMATTED));
|
||||||
summaryGrid.addComponents(new Label("Comment", ContentMode.PREFORMATTED), new Label(report.getUserComment(), ContentMode.PREFORMATTED));
|
summaryGrid.addComponents(new Label("Comment", ContentMode.PREFORMATTED), new Label(report.getUserComment(), ContentMode.PREFORMATTED));
|
||||||
summaryGrid.addComponents(new Label("De-obfuscated Stacktrace", ContentMode.PREFORMATTED), new Label(report.getDeObfuscatedStacktrace(mappingManager), ContentMode.PREFORMATTED));
|
summaryGrid.addComponents(new Label("De-obfuscated Stacktrace", ContentMode.PREFORMATTED), new Label(report.getDeObfuscatedStacktrace(dataManager), ContentMode.PREFORMATTED));
|
||||||
summaryGrid.addComponents(new Label("Attachments", ContentMode.PREFORMATTED), attachments);
|
summaryGrid.addComponents(new Label("Attachments", ContentMode.PREFORMATTED), attachments);
|
||||||
summaryGrid.setDefaultComponentAlignment(Alignment.MIDDLE_LEFT);
|
summaryGrid.setDefaultComponentAlignment(Alignment.MIDDLE_LEFT);
|
||||||
summaryGrid.setSizeFull();
|
summaryGrid.setSizeFull();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.faendir.acra.ui.view;
|
package com.faendir.acra.ui.view.base;
|
||||||
|
|
||||||
import com.vaadin.data.ValueProvider;
|
import com.vaadin.data.ValueProvider;
|
||||||
import com.vaadin.ui.Grid;
|
import com.vaadin.ui.Grid;
|
||||||
|
@ -10,6 +10,10 @@ import java.util.Collection;
|
||||||
* @since 14.05.2017
|
* @since 14.05.2017
|
||||||
*/
|
*/
|
||||||
public class MyGrid<T> extends Grid<T> {
|
public class MyGrid<T> extends Grid<T> {
|
||||||
|
public MyGrid(String caption) {
|
||||||
|
super(caption);
|
||||||
|
}
|
||||||
|
|
||||||
public MyGrid(String caption, Collection<T> items) {
|
public MyGrid(String caption, Collection<T> items) {
|
||||||
super(caption, items);
|
super(caption, items);
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package com.faendir.acra.ui.view;
|
package com.faendir.acra.ui.view.base;
|
||||||
|
|
||||||
import com.faendir.acra.annotation.AutoDiscoverView;
|
import com.faendir.acra.annotation.AutoDiscoverView;
|
||||||
|
import com.faendir.acra.mongod.user.UserManager;
|
||||||
import com.faendir.acra.ui.NavigationManager;
|
import com.faendir.acra.ui.NavigationManager;
|
||||||
import com.vaadin.navigator.View;
|
import com.vaadin.navigator.View;
|
||||||
import com.vaadin.ui.CustomComponent;
|
import com.vaadin.ui.CustomComponent;
|
||||||
|
@ -23,7 +24,11 @@ public abstract class NamedView extends CustomComponent implements View {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationManager getNavigationManager() {
|
public String requiredRole(){
|
||||||
|
return UserManager.ROLE_USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected NavigationManager getNavigationManager() {
|
||||||
return navigationManager;
|
return navigationManager;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.faendir.acra.ui.view.base;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.data.DataManager;
|
||||||
|
import com.faendir.acra.mongod.model.Permission;
|
||||||
|
import com.faendir.acra.mongod.model.Report;
|
||||||
|
import com.faendir.acra.security.SecurityUtils;
|
||||||
|
import com.faendir.acra.ui.NavigationManager;
|
||||||
|
import com.faendir.acra.ui.view.ReportView;
|
||||||
|
import com.faendir.acra.util.StringUtils;
|
||||||
|
import com.vaadin.ui.renderers.ButtonRenderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 14.05.2017
|
||||||
|
*/
|
||||||
|
public class ReportList extends MyGrid<Report> implements DataManager.ReportChangeListener {
|
||||||
|
private final String app;
|
||||||
|
private final DataManager dataManager;
|
||||||
|
|
||||||
|
public ReportList(String app, NavigationManager navigationManager, DataManager dataManager) {
|
||||||
|
super("Reports", dataManager.getReports(app));
|
||||||
|
this.app = app;
|
||||||
|
this.dataManager = dataManager;
|
||||||
|
setSizeFull();
|
||||||
|
setSelectionMode(SelectionMode.NONE);
|
||||||
|
addColumn(report -> StringUtils.distanceFromNowAsString(report.getDate()), "Date");
|
||||||
|
addColumn(report -> String.valueOf(report.getVersionCode()), "App Version");
|
||||||
|
addColumn(Report::getAndroidVersion, "Android Version");
|
||||||
|
addColumn(Report::getPhoneModel, "Device");
|
||||||
|
addColumn(report -> report.getStacktrace().split("\n", 2)[0], "Stacktrace").setExpandRatio(1);
|
||||||
|
if(SecurityUtils.hasPermission(app, Permission.Level.EDIT)) {
|
||||||
|
addColumn(report -> "Delete", new ButtonRenderer<>(e -> dataManager.remove(e.getItem())));
|
||||||
|
}
|
||||||
|
addItemClickListener(e -> navigationManager.navigateTo(ReportView.class, e.getItem().getId()));
|
||||||
|
addAttachListener(e -> dataManager.addListener(this));
|
||||||
|
addDetachListener(e -> dataManager.removeListener(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChange() {
|
||||||
|
setItems(dataManager.getReports(app));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,11 @@
|
||||||
package com.faendir.acra.ui.view;
|
package com.faendir.acra.ui.view.tabs;
|
||||||
|
|
||||||
import com.faendir.acra.data.Bug;
|
import com.faendir.acra.mongod.data.DataManager;
|
||||||
import com.faendir.acra.data.ReportManager;
|
import com.faendir.acra.mongod.model.Bug;
|
||||||
import com.faendir.acra.data.ReportUtils;
|
import com.faendir.acra.mongod.data.ReportUtils;
|
||||||
import com.faendir.acra.ui.NavigationManager;
|
import com.faendir.acra.ui.NavigationManager;
|
||||||
|
import com.faendir.acra.ui.view.base.MyGrid;
|
||||||
|
import com.faendir.acra.ui.view.base.ReportList;
|
||||||
import com.faendir.acra.util.StringUtils;
|
import com.faendir.acra.util.StringUtils;
|
||||||
import com.faendir.acra.util.Style;
|
import com.faendir.acra.util.Style;
|
||||||
import com.vaadin.event.selection.SelectionEvent;
|
import com.vaadin.event.selection.SelectionEvent;
|
||||||
|
@ -14,19 +16,19 @@ import com.vaadin.ui.VerticalLayout;
|
||||||
* @author Lukas
|
* @author Lukas
|
||||||
* @since 17.05.2017
|
* @since 17.05.2017
|
||||||
*/
|
*/
|
||||||
public class BugTab extends CustomComponent implements ReportManager.ChangeListener {
|
public class BugTab extends CustomComponent implements DataManager.ReportChangeListener {
|
||||||
public static final String CAPTION = "Bugs";
|
public static final String CAPTION = "Bugs";
|
||||||
private final VerticalLayout root;
|
private final VerticalLayout root;
|
||||||
private final String app;
|
private final String app;
|
||||||
private final NavigationManager navigationManager;
|
private final NavigationManager navigationManager;
|
||||||
private final ReportManager reportManager;
|
private final DataManager dataManager;
|
||||||
private final MyGrid<Bug> bugs;
|
private final MyGrid<Bug> bugs;
|
||||||
|
|
||||||
public BugTab(String app, NavigationManager navigationManager, ReportManager reportManager) {
|
public BugTab(String app, NavigationManager navigationManager, DataManager dataManager) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.navigationManager = navigationManager;
|
this.navigationManager = navigationManager;
|
||||||
this.reportManager = reportManager;
|
this.dataManager = dataManager;
|
||||||
bugs = new MyGrid<>(null, ReportUtils.getBugs(reportManager.getReports(app)));
|
bugs = new MyGrid<>(null, ReportUtils.getBugs(dataManager.getReports(app)));
|
||||||
bugs.setSizeFull();
|
bugs.setSizeFull();
|
||||||
bugs.addColumn(bug -> String.valueOf(bug.getReports().size()), "Reports");
|
bugs.addColumn(bug -> String.valueOf(bug.getReports().size()), "Reports");
|
||||||
bugs.addColumn(bug -> StringUtils.distanceFromNowAsString(bug.getLastDate()), "Latest Report");
|
bugs.addColumn(bug -> StringUtils.distanceFromNowAsString(bug.getLastDate()), "Latest Report");
|
||||||
|
@ -39,19 +41,19 @@ public class BugTab extends CustomComponent implements ReportManager.ChangeListe
|
||||||
setCompositionRoot(root);
|
setCompositionRoot(root);
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
setCaption(CAPTION);
|
setCaption(CAPTION);
|
||||||
addAttachListener(e -> reportManager.addListener(this));
|
addAttachListener(e -> dataManager.addListener(this));
|
||||||
addDetachListener(e -> reportManager.removeListener(this));
|
addDetachListener(e -> dataManager.removeListener(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBugSelection(SelectionEvent<Bug> e) {
|
private void handleBugSelection(SelectionEvent<Bug> e) {
|
||||||
if(root.getComponentCount() == 2){
|
if(root.getComponentCount() == 2){
|
||||||
root.removeComponent(root.getComponent(1));
|
root.removeComponent(root.getComponent(1));
|
||||||
}
|
}
|
||||||
e.getFirstSelectedItem().ifPresent(bug -> root.addComponent(new ReportList(app, navigationManager, reportManager)));
|
e.getFirstSelectedItem().ifPresent(bug -> root.addComponent(new ReportList(app, navigationManager, dataManager)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChange() {
|
public void onChange() {
|
||||||
bugs.setItems(ReportUtils.getBugs(reportManager.getReports(app)));
|
bugs.setItems(ReportUtils.getBugs(dataManager.getReports(app)));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,13 @@
|
||||||
package com.faendir.acra.ui.view;
|
package com.faendir.acra.ui.view.tabs;
|
||||||
|
|
||||||
import com.faendir.acra.data.MappingManager;
|
import com.faendir.acra.mongod.data.DataManager;
|
||||||
import com.faendir.acra.data.ProguardMapping;
|
import com.faendir.acra.mongod.model.Permission;
|
||||||
|
import com.faendir.acra.mongod.model.ProguardMapping;
|
||||||
|
import com.faendir.acra.security.SecurityUtils;
|
||||||
|
import com.faendir.acra.ui.view.base.MyGrid;
|
||||||
|
import com.faendir.acra.util.Style;
|
||||||
import com.vaadin.ui.Button;
|
import com.vaadin.ui.Button;
|
||||||
|
import com.vaadin.ui.CustomComponent;
|
||||||
import com.vaadin.ui.ProgressBar;
|
import com.vaadin.ui.ProgressBar;
|
||||||
import com.vaadin.ui.TextField;
|
import com.vaadin.ui.TextField;
|
||||||
import com.vaadin.ui.UI;
|
import com.vaadin.ui.UI;
|
||||||
|
@ -17,21 +22,28 @@ import java.io.ByteArrayOutputStream;
|
||||||
* @author Lukas
|
* @author Lukas
|
||||||
* @since 19.05.2017
|
* @since 19.05.2017
|
||||||
*/
|
*/
|
||||||
public class DeObfuscationTab extends MyGrid<ProguardMapping> {
|
public class DeObfuscationTab extends CustomComponent {
|
||||||
private final String app;
|
private final String app;
|
||||||
private final MappingManager mappingManager;
|
private final DataManager dataManager;
|
||||||
|
private final MyGrid<ProguardMapping> grid;
|
||||||
private boolean validNumber;
|
private boolean validNumber;
|
||||||
private boolean validFile;
|
private boolean validFile;
|
||||||
|
|
||||||
public DeObfuscationTab(String app, MappingManager mappingManager) {
|
public DeObfuscationTab(String app, DataManager dataManager) {
|
||||||
super("De-Obfuscation", mappingManager.getMappings(app));
|
setCaption("De-Obfuscation");
|
||||||
|
grid = new MyGrid<>(null, dataManager.getMappings(app));
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.mappingManager = mappingManager;
|
this.dataManager = dataManager;
|
||||||
Column column = addColumn(mapping -> String.valueOf(mapping.getVersion()), "Version");
|
grid.addColumn(mapping -> String.valueOf(mapping.getVersion()), "Version");
|
||||||
setSizeFull();
|
grid.setSizeFull();
|
||||||
Button add = new Button("Add File", e -> addFile());
|
VerticalLayout layout = new VerticalLayout(grid);
|
||||||
add.setSizeFull();
|
layout.setSizeFull();
|
||||||
appendFooterRow().getCell(column).setComponent(add);
|
Style.NO_PADDING.apply(layout);
|
||||||
|
setCompositionRoot(layout);
|
||||||
|
if (SecurityUtils.hasPermission(app, Permission.Level.EDIT)) {
|
||||||
|
layout.addComponent(new Button("Add File", e -> addFile()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addFile() {
|
private void addFile() {
|
||||||
|
@ -64,8 +76,8 @@ public class DeObfuscationTab extends MyGrid<ProguardMapping> {
|
||||||
});
|
});
|
||||||
upload.setSizeFull();
|
upload.setSizeFull();
|
||||||
confirm.addClickListener(e -> {
|
confirm.addClickListener(e -> {
|
||||||
mappingManager.addMapping(app, Integer.parseInt(version.getValue()), out.toString());
|
dataManager.addMapping(app, Integer.parseInt(version.getValue()), out.toString());
|
||||||
setItems(mappingManager.getMappings(app));
|
grid.setItems(dataManager.getMappings(app));
|
||||||
window.close();
|
window.close();
|
||||||
});
|
});
|
||||||
confirm.setSizeFull();
|
confirm.setSizeFull();
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.faendir.acra.ui.view.tabs;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.data.DataManager;
|
||||||
|
import com.faendir.acra.mongod.model.App;
|
||||||
|
import com.faendir.acra.ui.NavigationManager;
|
||||||
|
import com.vaadin.shared.ui.ContentMode;
|
||||||
|
import com.vaadin.ui.Button;
|
||||||
|
import com.vaadin.ui.HorizontalLayout;
|
||||||
|
import com.vaadin.ui.Label;
|
||||||
|
import com.vaadin.ui.UI;
|
||||||
|
import com.vaadin.ui.VerticalLayout;
|
||||||
|
import com.vaadin.ui.Window;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 19.05.2017
|
||||||
|
*/
|
||||||
|
public class PropertiesTab extends VerticalLayout {
|
||||||
|
private final App app;
|
||||||
|
private final DataManager dataManager;
|
||||||
|
private final NavigationManager navigationManager;
|
||||||
|
|
||||||
|
public PropertiesTab(App app, DataManager dataManager, NavigationManager navigationManager) {
|
||||||
|
this.app = app;
|
||||||
|
this.dataManager = dataManager;
|
||||||
|
this.navigationManager = navigationManager;
|
||||||
|
String location = UI.getCurrent().getPage().getLocation().toASCIIString();
|
||||||
|
location = location.substring(0, location.indexOf('#'));
|
||||||
|
addComponent(new Label(String.format("Required ACRA configuration:<br><code>formUri = \"%sreport\",<br>" +
|
||||||
|
"formUriBasicAuthLogin = \"%s\",<br>formUriBasicAuthPassword = \"%s\",<br>" +
|
||||||
|
"httpMethod = HttpSender.Method.POST,<br>reportType = HttpSender.Type.JSON</code>",
|
||||||
|
location, app.getId(), app.getPassword()), ContentMode.HTML));
|
||||||
|
addComponent(new Button("Delete App", e -> deleteApp()));
|
||||||
|
setCaption("Properties");
|
||||||
|
setSizeUndefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteApp() {
|
||||||
|
Window window = new Window("Confirm");
|
||||||
|
Label label = new Label("Are you sure you want to delete this app and all its reports and mappings?");
|
||||||
|
Button yes = new Button("Yes", e -> {
|
||||||
|
dataManager.deleteApp(app.getId());
|
||||||
|
window.close();
|
||||||
|
navigationManager.navigateBack();
|
||||||
|
});
|
||||||
|
Button no = new Button("No", e -> window.close());
|
||||||
|
VerticalLayout layout = new VerticalLayout(label, new HorizontalLayout(yes, no));
|
||||||
|
window.setContent(layout);
|
||||||
|
window.center();
|
||||||
|
UI.getCurrent().addWindow(window);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package com.faendir.acra.ui.view.user;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.model.User;
|
||||||
|
import com.faendir.acra.mongod.user.UserManager;
|
||||||
|
import com.faendir.acra.security.SecurityUtils;
|
||||||
|
import com.faendir.acra.ui.BackendUI;
|
||||||
|
import com.faendir.acra.ui.view.base.NamedView;
|
||||||
|
import com.vaadin.navigator.ViewChangeListener;
|
||||||
|
import com.vaadin.server.UserError;
|
||||||
|
import com.vaadin.spring.annotation.UIScope;
|
||||||
|
import com.vaadin.ui.Alignment;
|
||||||
|
import com.vaadin.ui.Button;
|
||||||
|
import com.vaadin.ui.Notification;
|
||||||
|
import com.vaadin.ui.PasswordField;
|
||||||
|
import com.vaadin.ui.VerticalLayout;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 21.05.2017
|
||||||
|
*/
|
||||||
|
@UIScope
|
||||||
|
@Component
|
||||||
|
public class ChangePasswordView extends NamedView {
|
||||||
|
|
||||||
|
private final UserManager userManager;
|
||||||
|
private final BackendUI backendUI;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public ChangePasswordView(UserManager userManager, BackendUI backendUI) {
|
||||||
|
|
||||||
|
this.userManager = userManager;
|
||||||
|
this.backendUI = backendUI;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "password-editor";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enter(ViewChangeListener.ViewChangeEvent event) {
|
||||||
|
PasswordField oldPassword = new PasswordField("Old Password");
|
||||||
|
PasswordField newPassword = new PasswordField("New Password");
|
||||||
|
PasswordField repeatPassword = new PasswordField("Repeat Password");
|
||||||
|
Button confirm = new Button("Confirm", e -> {
|
||||||
|
User user = userManager.getUser(SecurityUtils.getUsername());
|
||||||
|
if(userManager.checkPassword(user, oldPassword.getValue())) {
|
||||||
|
if (newPassword.getValue().equals(repeatPassword.getValue())) {
|
||||||
|
user.setPassword(newPassword.getValue());
|
||||||
|
Notification.show("Successful!");
|
||||||
|
getNavigationManager().navigateBack();
|
||||||
|
backendUI.logout();
|
||||||
|
} else {
|
||||||
|
repeatPassword.setComponentError(new UserError("Passwords do not match"));
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
oldPassword.setComponentError(new UserError("Incorrect password"));
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
confirm.setSizeFull();
|
||||||
|
VerticalLayout layout = new VerticalLayout(oldPassword, newPassword, repeatPassword, confirm);
|
||||||
|
layout.setSizeUndefined();
|
||||||
|
VerticalLayout root = new VerticalLayout(layout);
|
||||||
|
root.setSizeFull();
|
||||||
|
root.setComponentAlignment(layout, Alignment.MIDDLE_CENTER);
|
||||||
|
setSizeFull();
|
||||||
|
setCompositionRoot(root);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package com.faendir.acra.ui.view.user;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.data.DataManager;
|
||||||
|
import com.faendir.acra.mongod.model.Permission;
|
||||||
|
import com.faendir.acra.ui.view.base.MyGrid;
|
||||||
|
import com.vaadin.data.HasValue;
|
||||||
|
import com.vaadin.shared.Registration;
|
||||||
|
import com.vaadin.ui.ComboBox;
|
||||||
|
import com.vaadin.ui.Grid;
|
||||||
|
import org.vaadin.hene.popupbutton.PopupButton;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 20.05.2017
|
||||||
|
*/
|
||||||
|
public class PermissionEditor extends PopupButton implements HasValue<Collection<Permission>> {
|
||||||
|
|
||||||
|
private Collection<Permission> items;
|
||||||
|
private MyGrid<Permission> grid;
|
||||||
|
|
||||||
|
public PermissionEditor(DataManager dataManager) {
|
||||||
|
super("Editor");
|
||||||
|
items = new ArrayList<>();
|
||||||
|
grid = new MyGrid<>(null, items);
|
||||||
|
grid.addColumn(permission -> dataManager.getApp(permission.getApp()).getName(), "App");
|
||||||
|
ComboBox<Permission.Level> levelComboBox = new ComboBox<>("", Arrays.asList(Permission.Level.values()));
|
||||||
|
grid.addColumn(permission -> permission.getLevel().name(), "Level")
|
||||||
|
.setEditorBinding(grid.getEditor().getBinder().bind(levelComboBox, Permission::getLevel, (permission, level) -> {
|
||||||
|
Collection<Permission> newValue = items.stream().map(p -> new Permission(p.getApp(), p.getLevel())).collect(Collectors.toList());
|
||||||
|
newValue.stream().filter(p -> p.getApp().equals(permission.getApp())).findAny().ifPresent(p -> p.setLevel(level));
|
||||||
|
setValue(newValue, true);
|
||||||
|
}));
|
||||||
|
grid.getEditor().setEnabled(true).setBuffered(false);
|
||||||
|
grid.setSelectionMode(Grid.SelectionMode.NONE);
|
||||||
|
setContent(grid);
|
||||||
|
setPopupVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(Collection<Permission> value) {
|
||||||
|
setValue(value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setValue(Collection<Permission> value, boolean userOriginated) {
|
||||||
|
Collection<Permission> oldValue = items;
|
||||||
|
items = value;
|
||||||
|
grid.setItems(items);
|
||||||
|
grid.setHeightByRows(items.size());
|
||||||
|
fireEvent(new ValueChangeEvent<>(this, oldValue, userOriginated));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Permission> getValue() {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRequiredIndicatorVisible(boolean requiredIndicatorVisible) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRequiredIndicatorVisible() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Registration addValueChangeListener(ValueChangeListener<Collection<Permission>> listener) {
|
||||||
|
return addListener(ValueChangeEvent.class, listener,
|
||||||
|
ValueChangeListener.VALUE_CHANGE_METHOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadOnly(boolean readOnly) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReadOnly() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
package com.faendir.acra.ui.view.user;
|
||||||
|
|
||||||
|
import com.faendir.acra.mongod.data.DataManager;
|
||||||
|
import com.faendir.acra.mongod.model.User;
|
||||||
|
import com.faendir.acra.mongod.user.UserManager;
|
||||||
|
import com.faendir.acra.security.SecurityUtils;
|
||||||
|
import com.faendir.acra.ui.view.base.MyGrid;
|
||||||
|
import com.faendir.acra.ui.view.base.NamedView;
|
||||||
|
import com.faendir.acra.util.Style;
|
||||||
|
import com.vaadin.data.Binder;
|
||||||
|
import com.vaadin.navigator.ViewChangeListener;
|
||||||
|
import com.vaadin.server.UserError;
|
||||||
|
import com.vaadin.spring.annotation.UIScope;
|
||||||
|
import com.vaadin.ui.Button;
|
||||||
|
import com.vaadin.ui.CheckBox;
|
||||||
|
import com.vaadin.ui.Grid;
|
||||||
|
import com.vaadin.ui.PasswordField;
|
||||||
|
import com.vaadin.ui.TextField;
|
||||||
|
import com.vaadin.ui.UI;
|
||||||
|
import com.vaadin.ui.VerticalLayout;
|
||||||
|
import com.vaadin.ui.Window;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukas
|
||||||
|
* @since 20.05.2017
|
||||||
|
*/
|
||||||
|
@UIScope
|
||||||
|
@Component
|
||||||
|
public class UserManagerView extends NamedView {
|
||||||
|
private final UserManager userManager;
|
||||||
|
private final DataManager dataManager;
|
||||||
|
private MyGrid<User> userGrid;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public UserManagerView(UserManager userManager, DataManager dataManager) {
|
||||||
|
this.userManager = userManager;
|
||||||
|
this.dataManager = dataManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "user-manager";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String requiredRole() {
|
||||||
|
return UserManager.ROLE_ADMIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enter(ViewChangeListener.ViewChangeEvent event) {
|
||||||
|
userGrid = new MyGrid<>("Users", userManager.getUsers());
|
||||||
|
userGrid.getEditor().setEnabled(true).setBuffered(false);
|
||||||
|
userGrid.setSelectionMode(Grid.SelectionMode.NONE);
|
||||||
|
userGrid.addColumn(User::getUsername, "Username");
|
||||||
|
Binder<User> binder = userGrid.getEditor().getBinder();
|
||||||
|
userGrid.addColumn(user -> user.getRoles().contains(UserManager.ROLE_ADMIN) ? "Yes" : "No").setCaption("Admin")
|
||||||
|
.setEditorBinding(binder.forField(new CheckBox())
|
||||||
|
.withValidator(bool -> bool || !binder.getBean().getUsername().equals(SecurityUtils.getUsername()), "Cannot revoke own admin privileges")
|
||||||
|
.bind(user -> user.getRoles().contains(UserManager.ROLE_ADMIN), userManager::setAdmin));
|
||||||
|
userGrid.addColumn(this::getPermissionString, "App Permissions")
|
||||||
|
.setEditorBinding(userGrid.getEditor().getBinder().bind(new PermissionEditor(dataManager), User::getPermissions,
|
||||||
|
((user, permissions) -> permissions.forEach(p -> userManager.setPermission(user, p.getApp(), p.getLevel())))));
|
||||||
|
Button newUser = new Button("New User", e -> newUser());
|
||||||
|
VerticalLayout layout = new VerticalLayout(userGrid, newUser);
|
||||||
|
layout.setSizeFull();
|
||||||
|
Style.NO_PADDING.apply(layout);
|
||||||
|
setCompositionRoot(layout);
|
||||||
|
userGrid.setSizeFull();
|
||||||
|
setSizeFull();
|
||||||
|
Style.apply(this, Style.PADDING_LEFT, Style.PADDING_RIGHT, Style.PADDING_BOTTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPermissionString(User user) {
|
||||||
|
return user.getPermissions().stream()
|
||||||
|
.map(permission -> dataManager.getApp(permission.getApp()).getName() + ": " + permission.getLevel().name())
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void newUser() {
|
||||||
|
Window window = new Window("New User");
|
||||||
|
TextField name = new TextField("Username");
|
||||||
|
PasswordField password = new PasswordField("Password");
|
||||||
|
PasswordField repeatPassword = new PasswordField("Repeat Password");
|
||||||
|
Button create = new Button("Create");
|
||||||
|
create.addClickListener(e -> {
|
||||||
|
if (password.getValue().equals(repeatPassword.getValue())) {
|
||||||
|
userManager.createUser(name.getValue(), password.getValue());
|
||||||
|
userGrid.setItems(userManager.getUsers());
|
||||||
|
window.close();
|
||||||
|
} else {
|
||||||
|
repeatPassword.setComponentError(new UserError("Passwords do not match"));
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
VerticalLayout layout = new VerticalLayout(name, password, repeatPassword, create);
|
||||||
|
window.setContent(layout);
|
||||||
|
window.center();
|
||||||
|
UI.getCurrent().addWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -30,8 +30,6 @@ public class Rfc1341ServletFileUpload extends ServletFileUpload {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modified copy (with appropriate cast) of {@link FileUploadBase#parseRequest(RequestContext)}
|
* Modified copy (with appropriate cast) of {@link FileUploadBase#parseRequest(RequestContext)}
|
||||||
*
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<FileItem> parseRequest(RequestContext ctx) throws FileUploadException {
|
public List<FileItem> parseRequest(RequestContext ctx) throws FileUploadException {
|
||||||
|
|
13
backend/src/main/resources/AppWidgetset.gwt.xml
Normal file
13
backend/src/main/resources/AppWidgetset.gwt.xml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE module PUBLIC "-//Vaadin//DTD Vaadin 7//EN" "https://raw.github.com/vaadin/gwt/master/distro-source/core/src/gwt-module.dtd">
|
||||||
|
<!-- WS Compiler: manually edited -->
|
||||||
|
<module>
|
||||||
|
<inherits name="com.vaadin.DefaultWidgetSet" />
|
||||||
|
<inherits name="org.vaadin.hene.popupbutton.widgetset.PopupbuttonWidgetset" />
|
||||||
|
<set-configuration-property name="devModeRedirectEnabled" value="true" />
|
||||||
|
<set-property name="user.agent" value="ie8,ie9,gecko1_8,safari,ie10" />
|
||||||
|
<source path="client" />
|
||||||
|
<source path="shared" />
|
||||||
|
<collapse-all-properties />
|
||||||
|
<set-property name="compiler.useSymbolMaps" value="true" />
|
||||||
|
</module>
|
|
@ -1,6 +1,9 @@
|
||||||
allprojects{
|
allprojects{
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
url = "http://maven.vaadin.com/vaadin-addons"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue