public interface DeviceDatabaseModel {

    List<Device> getAllDevices() throws AuthDatabaseException;

    List<Device> getUserDevices(String user) throws AuthDatabaseException;

    Device addDevice(Device device) throws AuthDatabaseException;

    Device editDevice(Device device) throws AuthDatabaseException;

    void removeDevice(String string) throws AuthDatabaseException;

    Device getDevice(String name) throws AuthDatabaseException;

    String getEncryptionKey(String name) throws AuthDatabaseException;

    boolean editCommands(String deviceName, String commands) throws AuthDatabaseException;

    String getCommands(String deviceName) throws AuthDatabaseException;

    boolean setCommandsOutput(String deviceName, String commandsOutput) throws AuthDatabaseException;

    String getCommandsOutput(String deviceName) throws AuthDatabaseException;
} @Entity
public class Device {
    @Id
    @NotNull
    @Length(max = 64)
    private String name;
    @NotNull
    @Length(max = 25)
    private String ip;
    @Length(max = 10)
    private String serverPort;
    @Length(max = 25)
    private String lastConnection;
    @Length(max = 32)
    private String encryptionKey;
    @ManyToOne(fetch = FetchType.LAZY)
    @JsonBackReference
    @JoinColumn(name="user_email")
    private User associatedUser;
    @Length(max = 1000)
    private String commands;
    @Length(max = 1000)
    private String commandsOutput;
    @Length(max = 64)
    private String installationId;
    @OneToOne(mappedBy = "device", fetch = FetchType.LAZY, cascade = {CascadeType.ALL}/*, orphanRemoval = true*/)
    @PrimaryKeyJoinColumn
    @JsonBackReference
    private Location location;
    @OneToOne(mappedBy = "device", fetch = FetchType.LAZY, cascade = {CascadeType.ALL}/*, orphanRemoval = true*/)
    @PrimaryKeyJoinColumn
    @JsonBackReference
    private DeviceInfo deviceInfo;

    public Device() {
    }

    public Device(String name, String ip, String serverPort, String lastConnection, String encryptionKey, User associatedUser, String commands, String commandsOutput, String installationId, Location location, DeviceInfo deviceInfo) {
        this.name = name;
        this.ip = ip;
        this.serverPort = serverPort;
        this.lastConnection = lastConnection;
        this.encryptionKey = encryptionKey;
        this.associatedUser = associatedUser;
        this.commands = commands;
        this.commandsOutput = commandsOutput;
        this.installationId = installationId;
        this.location = location;
        if (this.location != null) {
            this.location.setId(name);
            this.location.setDevice(this);
        }
        this.deviceInfo = deviceInfo;
        if (this.deviceInfo != null) {
            this.deviceInfo.setId(name);
            this.deviceInfo.setDevice(this);
        }
    }

    public String getInstallationId() {
        return installationId;
    }

    public void setInstallationId(String installationId) {
        this.installationId = installationId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public String getServerPort() {
        return serverPort;
    }

    public void setServerPort(String serverPort) {
        this.serverPort = serverPort;
    }

    public String getLastConnection() {
        return lastConnection;
    }

    public void setLastConnection(String lastConnection) {
        this.lastConnection = lastConnection;
    }

    public String getEncryptionKey() {
        return encryptionKey;
    }

    public void setEncryptionKey(String encryptionKey) {
        this.encryptionKey = encryptionKey;
    }

    public User getAssociatedUser() {
        return associatedUser;
    }

    public void setAssociatedUser(User associatedUser) {
        this.associatedUser = associatedUser;
    }

    public String getCommands() {
        return commands;
    }

    public void setCommands(String commands) {
        this.commands = commands;
    }

    public String getCommandsOutput() {
        return commandsOutput;
    }

    public void setCommandsOutput(String commandsOutput) {
        this.commandsOutput = commandsOutput;
    }

    public Location getLocation() {
        return location;
    }

    public void setLocation(Location location) {
        this.location = location;
    }

    public DeviceInfo getDeviceInfo() {
        return deviceInfo;
    }

    public void setDeviceInfo(DeviceInfo deviceInfo) {
        this.deviceInfo = deviceInfo;
    }

    // called by the serialization, location is not serialized due to JsonBackReference
    public String getLocationAsPosition() {
        return GeoLocation.getPositionFromCoordinates(location.getLongitude(), location.getLatitude(), location.getAltitude());
    }

    // called by the serialization, location is not serialized due to JsonBackReference
    public String getAssociatedUserEmail() {
        return associatedUser.getEmail();
    }

    // called by the serialization, location is not serialized due to JsonBackReference
    public String getDeviceInfoDevName() {
        return deviceInfo.getDevName();
    }

    @Override
    public String toString() {
        String output = "";
        output = "{" + getName() + ", "
                + getIp() + ", "
                + getServerPort() + ", "
                + getLastConnection() + ", "
                + getEncryptionKey() + ", "
                + getAssociatedUser() + ", "
                + getCommands() + ", "
                + getCommandsOutput() + ", "
                + getInstallationId() + ", "
                + getLocation() + ", "
                + getDeviceInfo()
                + "}";

        return output;
    }
} @Entity
public class DeviceInfo {
        @Id
        //@GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "device_name")
        private String id;
        @Length(max = 50)
        private String osVersion;
        private int osApiLevel;
        @Length(max = 20)
        private String devName;
        @Length(max = 25)
        private String brand;
        @Length(max = 50)
        private String modelProduct;

        @OneToOne
        @MapsId
        @JoinColumn(name = "device_name")
        @JsonManagedReference
        private Device device;

        public static final String OS_VERSION_JSON_KEY = "os_version";
        public static final String OS_API_LEVEL_JSON_KEY = "os_api_level";
        public static final String DEVICE_JSON_KEY = "device";
        public static final String BRAND_JSON_KEY = "brand";
        public static final String MODEL_PRODUCT_JSON_KEY = "model_product";

        public DeviceInfo(){}

        public DeviceInfo(String osVersion, int osApiLevel, @Length(max = 20) String deviceName, @Length(max = 25) String brand, @Length(max = 50) String modelProduct) {
                //this.id = SHA256.hash(osVersion+osApiLevel+ deviceName +brand+modelProduct);
                this.osVersion = osVersion;
                this.osApiLevel = osApiLevel;
                this.devName = deviceName;
                this.brand = brand;
                this.modelProduct = modelProduct;
        }

        public DeviceInfo(String deviceInfoJson) {
                //this.id = SHA256.hash("test");
                if (DataFormat.isJSONValid(deviceInfoJson)) {
                        JSONObject locationData = new JSONObject(deviceInfoJson);
                        //Logger.info(location);
                        this.osVersion = locationData.getString(OS_VERSION_JSON_KEY);
                        this.osApiLevel = locationData.getInt(OS_API_LEVEL_JSON_KEY);
                        this.devName = locationData.getString(DEVICE_JSON_KEY);
                        this.brand = locationData.getString(BRAND_JSON_KEY);
                        this.modelProduct = locationData.getString(MODEL_PRODUCT_JSON_KEY);

                } else {
                        String error = "DeviceInfo JSON not correct";
                        //throw new ModelException(error);
                        Logger.debug(error);
                        this.osVersion = null;
                        this.osApiLevel = 0;
                        this.devName = null;
                        this.brand = null;
                        this.modelProduct = null;
                }
        }

        public String getId() {
                return id;
        }

        public void setId(String id) {
                this.id = id;
        }

        public String getOsVersion() {
                return osVersion;
        }

        public void setOsVersion(String osVersion) {
                this.osVersion = osVersion;
        }

        public int getOsApiLevel() {
                return osApiLevel;
        }

        public void setOsApiLevel(int osApiLevel) {
                this.osApiLevel = osApiLevel;
        }

        public String getDevName() {
                return devName;
        }

        public void setDevName(String device) {
                this.devName = device;
        }

        public String getBrand() {
                return brand;
        }

        public void setBrand(String brand) {
                this.brand = brand;
        }

        public String getModelProduct() {
                return modelProduct;
        }

        public void setModelProduct(String modelProduct) {
                this.modelProduct = modelProduct;
        }

        public Device getDevice() {
                return device;
        }

        public void setDevice(Device device) {
                this.device = device;
        }

        @Override
        public String toString() {
                String output = "";
                output = "{" + getOsVersion() + ", "
                        + getOsApiLevel() + ", "
                        + getDevName() + ", "
                        + getBrand() + ", "
                        + getModelProduct()
                        + "}";

                return output;
        }
} @Entity
public class Location {
    @Id
    //@GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "device_name")
    private String id;
    private double latitude;
    private double longitude;
    private double accuracy;
    private double altitude;
    @Length(max = 20)
    private String provider;
    private boolean isMock;

    @OneToOne
    @MapsId
    @JoinColumn(name = "device_name")
    @JsonManagedReference
    private Device device;

    public static final String LATITUDE_JSON_KEY = "latitude";
    public static final String LONGITUDE_JSON_KEY = "longitude";
    public static final String ACCURACY_JSON_KEY = "accuracy";
    public static final String ALTITUDE_JSON_KEY = "altitude";
    public static final String PROVIDER_JSON_KEY = "provider";
    public static final String IS_MOCK_JSON_KEY = "isMock";

    public Location() {
    }

    public Location(double latitude, double longitude, double accuracy, double altitude, @Length(max = 20) String provider, boolean isMock) {
        this.latitude = latitude;
        this.longitude = longitude;
        this.accuracy = accuracy;
        this.altitude = altitude;
        this.provider = provider;
        this.isMock = isMock;
    }

    public Location(String locationJson) throws ModelException {
        if (DataFormat.isJSONValid(locationJson)) {
            JSONObject locationData = new JSONObject(locationJson);
            //Logger.info(location);
            this.latitude = locationData.getDouble(LATITUDE_JSON_KEY);
            this.longitude = locationData.getDouble(LONGITUDE_JSON_KEY);
            this.accuracy = locationData.getDouble(ACCURACY_JSON_KEY);
            this.altitude = locationData.getDouble(ALTITUDE_JSON_KEY);
            this.provider = locationData.getString(PROVIDER_JSON_KEY);
            this.isMock = locationData.getBoolean(IS_MOCK_JSON_KEY);

        } else {
            String error = "Location JSON not correct";
            //throw new ModelException(error);
            Logger.debug(error);
            this.latitude = 0;
            this.longitude = 0;
            this.accuracy = 0;
            this.altitude = 0;
            this.provider = null;
            this.isMock = false;
        }
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public double getLatitude() {
        return latitude;
    }

    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }

    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }

    public double getAccuracy() {
        return accuracy;
    }

    public void setAccuracy(double accuracy) {
        this.accuracy = accuracy;
    }

    public double getAltitude() {
        return altitude;
    }

    public void setAltitude(double altitude) {
        this.altitude = altitude;
    }

    public String getProvider() {
        return provider;
    }

    public void setProvider(String provider) {
        this.provider = provider;
    }

    public boolean isMock() {
        return isMock;
    }

    public void setMock(boolean mock) {
        isMock = mock;
    }

    public Device getDevice() {
        return device;
    }

    public void setDevice(Device device) {
        this.device = device;
    }

    @Override
    public String toString() {
        String output = "";
        output = "{" + getLatitude() + ", "
                + getLongitude() + ", "
                + getAccuracy() + ", "
                + getAltitude() + ", "
                + getProvider() + ", "
                + isMock()
                + "}";

        return output;
    }
} @Component
public class AuthDatabaseSpringManager implements AuthDatabaseModel {
    private static UserRepository userRepository;

    @Autowired
    public AuthDatabaseSpringManager(UserRepository userRepository) throws AuthDatabaseException {
        this.userRepository = userRepository;
    }

    public static AuthDatabaseSpringManager getInstance() throws AuthDatabaseException {
        return new AuthDatabaseSpringManager(userRepository);
    }

//    public it.richkmeli.jframework.auth.model.User findUserByEmail(String email) throws AuthDatabaseException {
//        User user = userRepository.findUserByEmail(email);
//        try {
//            return new it.richkmeli.jframework.auth.model.User(user.getEmail(), user.getPassword(), user.getAdmin());
//        } catch (ModelException e) {
//            throw new AuthDatabaseException(e);
//        }
//    }

    public User findUserByEmail(String email) throws AuthDatabaseException {
        return userRepository.findUserByEmail(email);
    }

    @Override
    public List<it.richkmeli.jframework.auth.model.User> getAllUsers() throws AuthDatabaseException {
        List<User> users = userRepository.findAll();
        List<it.richkmeli.jframework.auth.model.User> users1 = new ArrayList<>();
        for (User u : users) {
            try {
                users1.add(new it.richkmeli.jframework.auth.model.User(u.getEmail(), u.getPassword(), u.getAdmin()));
            } catch (ModelException e) {
                throw new AuthDatabaseException(e);
            }
        }
        return users1;
    }

    @Override
    public boolean addUser(it.richkmeli.jframework.auth.model.User user) throws AuthDatabaseException {
        try {
            userRepository.save(new User(
                    user.getEmail(),
                    Crypto.hashPassword(user.getPassword(), false),
                    user.getAdmin()));
            return true;
        } catch (ModelException e) {
            throw new AuthDatabaseException(e);
        }
    }

    public User addUser(User user) throws AuthDatabaseException {
        try {
            return userRepository.save(new User(
                    user.getEmail(),
                    Crypto.hashPassword(user.getPassword(), false),
                    user.getAdmin()));
        } catch (ModelException e) {
            throw new AuthDatabaseException(e);
        }
    }

    @Override
    public boolean removeUser(String s) throws AuthDatabaseException, ModelException {
        User user = findUserByEmail(s);
        if(user != null) {
            userRepository.delete(user);
            return true;
        }
        return false;
    }

    @Override
    public boolean isUserPresent(String s) throws AuthDatabaseException, ModelException {
        return userRepository.existsUserByEmail(s);
    }

    @Override
    public boolean editPassword(String email, String password) throws AuthDatabaseException, ModelException {
        Optional<User> user = userRepository.findById(email);
        if (user.isPresent()) {
            User user1 = user.get();
            user1.setPassword(Crypto.hashPassword(password, false));
            userRepository.save(user1);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean editAdmin(String email, Boolean aBoolean) throws AuthDatabaseException, ModelException {
        Optional<User> user = userRepository.findById(email);
        if (user.isPresent()) {
            User user1 = user.get();
            user1.setAdmin(aBoolean);
            userRepository.save(user1);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean checkPassword(String email, String password) throws AuthDatabaseException, ModelException {
        User user = userRepository.findUserByEmail(email);
        if (user != null) {
            Crypto.verifyPassword(user.getPassword(), password);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean isAdmin(String email) throws AuthDatabaseException, ModelException {
        User user = userRepository.findUserByEmail(email);
        if (user != null) {
            return user.getAdmin();
        } else {
            return false;
        }
    }
} @Entity
public class User{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(unique = true)
    @NotNull
    @Length(max = 50)
    private String email;
    @NotNull
    @Length(max = 100)
    private String password;
    @NotNull
    private Boolean admin;

    @OneToMany(mappedBy = "associatedUser", cascade = {CascadeType.REMOVE}, fetch = FetchType.LAZY)
    // OR if you want in json object also the device list when it isn't load at that time
    //@OneToMany(mappedBy = "associatedUser", cascade={CascadeType.REMOVE}, fetch = FetchType.EAGER)
    @JsonManagedReference
    private Set<Device> devices;

    @OneToMany(mappedBy = "associatedUser", cascade = {CascadeType.REMOVE}, fetch = FetchType.LAZY)
    @JsonManagedReference
    private Set<Rmc> rmcs;

    public User() {
    }

    public User(String email, String password) throws ModelException {
        this(email, password, false);
    }

    public User(@Length(max = 50) String email, @NotNull @Length(max = 100) String password, @NotNull Boolean admin) throws ModelException {
        checkUserIntegrity(email, password, admin);
        this.email = email;
        this.password = password;
        this.admin = admin;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Boolean getAdmin() {
        return admin;
    }

    public void setAdmin(Boolean admin) {
        this.admin = admin;
    }

    public Set<Device> getDevices() {
        return devices;
    }

    public void setDevices(Set<Device> devices) {
        this.devices = devices;
    }

    public Set<Rmc> getRmcs() {
        return rmcs;
    }

    public void setRmcs(Set<Rmc> rmcs) {
        this.rmcs = rmcs;
    }

    public static void checkUserIntegrity(String email, String password, Boolean admin) throws ModelException {
        try {
            RegexManager.checkEmailIntegrity(email);
        } catch (RegexException e) {
            throw new ModelException("Email is not valid");
        }
        // ...
    }

    @Override
    public String toString() {
        String output = "";
        output = "{" + getEmail() + ", "
                + getPassword() + ", "
                + getAdmin()
                + "}";

        return output;
    }
} public class GeoLocation {

    public static final String POSITION_NOT_FOUND = "position not found";

    public static String getPositionFromCoordinates(double longitude, double latitude, double altitude) {

        String mapboxAccessToken = ConfigurationManager.getValue(ConfigurationEnum.MAPBOX_ACCESS_TOKEN);

        MapboxGeocoding reverseGeocode = new MapboxGeocoding.Builder()
                .setAccessToken(mapboxAccessToken)
                .setCoordinates(Position.fromCoordinates(longitude, latitude, altitude))
                .setGeocodingType(GeocodingCriteria.TYPE_ADDRESS)
                .build();
        try {
            GeocodingResponse response = reverseGeocode.executeCall().body();
            List<CarmenFeature> results = response.getFeatures();
            if (results.size() > 0) {
                // Get the first Feature from the successful geocoding response
                CarmenFeature feature = results.get(0);
                // Get the address string from the CarmenFeature
                String place = feature.getPlaceName();
                //Logger.info(place);
                return place;
            } else {
                return POSITION_NOT_FOUND;
            }
        } catch (IOException e) {
            Logger.error(e);
            return POSITION_NOT_FOUND;
        }

    }
} public class RMSServletManager extends AuthServletManager {
    public static final String ERROR_JSP = "JSP/error.jsp";
    public static final String DEVICES_HTML = "devices.html";
    public static final String LOGIN_HTML = "login.html";
    public static final String HTTP_RMS_SESSION_NAME = "rms_session";
    private static RMSSession rmsSession;
    private String password;

    public RMSServletManager(HttpServletRequest request, HttpServletResponse response) {
        super(request, response);
        try {
            rmsSession = getRMSServerSession();
            password = ResourceBundle.getBundle("configuration").getString("encryptionkey");
        } catch (JServletException e) {
            //e.printStackTrace();
            Logger.error(e);
        }
    }

    public RMSServletManager(AuthServletManager authServletManager) {
        super(authServletManager);
        try {
            rmsSession = getRMSServerSession();
            password = ResourceBundle.getBundle("configuration").getString("encryptionkey");
        } catch (JServletException e) {
            //e.printStackTrace();
            Logger.error(e);
        }
    }

    public static void setRMSServerSession(RMSSession rmsSession, HttpServletRequest request) {
        // http session
        HttpSession httpSession = request.getSession();
        httpSession.setAttribute(HTTP_RMS_SESSION_NAME, rmsSession);
    }

    @Override
    public void doSpecificProcessRequestAuth() throws JServletException {
        // check channel: rmc or webapp, if rmc secureconnection first (set something in session)
        if (attribMap.containsKey(Channel.CHANNEL)) {
            String channel = attribMap.get(Channel.CHANNEL);
            switch (channel) {
                case Channel.RMC:
                    rmsSession.setChannel(Channel.RMC);
                    // Extract encrypted data from map
                    String payload = attribMap.get(ENCRYPTED_PAYLOAD_KEY.DATA);
                    if (payload != null && !"".equalsIgnoreCase(payload)) {
                        String decryptedPayload = null;
                        try {
                            decryptedPayload = session.getCryptoServer().decrypt(payload);
                        } catch (CryptoException e) {
                            throw new JServletException(e);
                        }

                        jsonToMap(decryptedPayload);

                        // remove encrypted data from map
                        attribMap.remove(ENCRYPTED_PAYLOAD_KEY.DATA);
                    }
                    break;
                case Channel.WEBAPP:
                    rmsSession.setChannel(Channel.WEBAPP);
                    // all attributes are already in map
                    break;
                case Channel.RICHKWARE:
                    try {
                        rmsSession.setChannel(Channel.RICHKWARE);

                        // key "data" is encrypted with a specific key for the device identified from the field "data0"
                        // data0: contains deviceID (always encrypted with preshared key)
                        // data: contains serverPort, associatedUser, contains additional info for mobile device, ...
                        if (attribMap.containsKey(ENCRYPTED_PAYLOAD_KEY.DEVICE_ID) &&
                                attribMap.containsKey(ENCRYPTED_PAYLOAD_KEY.DATA)) {
                            String nameEnc = attribMap.get(ENCRYPTED_PAYLOAD_KEY.DEVICE_ID);
                            String dataEnc = attribMap.get(ENCRYPTED_PAYLOAD_KEY.DATA);
                            Logger.info("data0: " + nameEnc + " data: " + dataEnc);

                            String name = Crypto.decryptRC4(nameEnc, password);
                            attribMap.put(PAYLOAD_KEY.NAME, name);

                            // check in the DB if there is an entry with that name
                            DeviceDatabaseModel deviceDatabaseSpringManager = rmsSession.getDeviceDatabaseManager();
                            Device oldDevice = deviceDatabaseSpringManager.getDevice(name);

                            // if this entry exists, then it's used to decrypt the encryption key in the DB
                            String data = null;
                            // at the first call is encrypted with preshared key, at the following with server-side generated key
                            if (oldDevice == null) {
                                attribMap.put(PAYLOAD_KEY.STATE_IN_RMS, "new");
                                data = Crypto.decryptRC4(dataEnc, password);
                            } else {
                                attribMap.put(PAYLOAD_KEY.STATE_IN_RMS, "present");
                                try {
                                    data = Crypto.decryptRC4(dataEnc, oldDevice.getEncryptionKey());
                                } catch (CryptoException ce) {
                                    if (ce.getMessage().contains("Key is not correct.")) {
                                        Logger.info("Probable deletion of the key saved locally. Decrypting using default key.");
                                        data = Crypto.decryptRC4(dataEnc, password);
                                    }
                                }
                            }
                            jsonToMap(data);
                            // remove encrypted data from map
                            attribMap.remove(ENCRYPTED_PAYLOAD_KEY.DEVICE_ID);
                            attribMap.remove(ENCRYPTED_PAYLOAD_KEY.DATA);
                        }

                    } catch (CryptoException e) {
                        Logger.error("ServletManager, servlet: " + servletPath + ". ", e);
                        throw new JServletException(new KoResponse(RMSStatusCode.GENERIC_ERROR, e.getMessage()));

                    } catch (AuthDatabaseException e) {
                        Logger.error("ServletManager, servlet: " + servletPath + ". ", e);
                        throw new JServletException(new KoResponse(RMSStatusCode.DB_ERROR, e.getMessage()));

                    }
                    break;
                default:
                    Logger.error("ServletManager, servlet: " + servletPath + ". channel " + channel + " unknown");
                    throw new JServletException(new KoResponse(RMSStatusCode.CHANNEL_UNKNOWN, "channel " + channel + " unknown"));
            }
        } else {
            Logger.error("ServletManager, servlet: " + servletPath + ". channel key is not present.");
            throw new JServletException(new KoResponse(RMSStatusCode.CHANNEL_UNKNOWN, "channel key is not present"));
        }
    }

    private void jsonToMap(String data) {
        if (data != null && !"".equalsIgnoreCase(data)) {
            JSONObject decryptedPayloadJSON = new JSONObject(data);
            // add each attribute inside encrypted data to map
            for (String key : decryptedPayloadJSON.keySet()) {
                String value = decryptedPayloadJSON.getString(key);
                attribMap.put(key, value);
            }
        }
    }

    @Override
    public String doSpecificProcessResponseAuth(String input) throws JServletException {
        // default: input as output
        String output = input;

        String channel = rmsSession.getChannel();
        if (channel != null) {
            switch (channel) {
                case Channel.RMC:
                    rmsSession.setChannel(Channel.RMC);
                    try {
                        output = session.getCryptoServer().encrypt(input);
                    } catch (CryptoException e) {
                        e.printStackTrace();
                        throw new JServletException(e);
                    }
                    break;
                case Channel.WEBAPP:
                    rmsSession.setChannel(Channel.WEBAPP);
                    break;
                case Channel.RICHKWARE:
                    rmsSession.setChannel(Channel.RICHKWARE);
                    break;
                default:
                    Logger.error("ServletManager, servlet: " + servletPath + ". + channel " + channel + " unknown");
                    throw new JServletException(new KoResponse(RMSStatusCode.CHANNEL_UNKNOWN, "channel " + channel + " unknown"));
            }
        } else {
            Logger.error("ServletManager, servlet: " + servletPath + ". + channel(server session) is null");
            throw new JServletException(new KoResponse(RMSStatusCode.CHANNEL_UNKNOWN, "channel(server session) is null"));
        }
        setRMSServerSession(rmsSession, httpServletRequest);
        return output;
    }

    @Override
    protected AuthDatabaseModel getAuthDatabaseManagerInstance() throws AuthDatabaseException {
        return AuthDatabaseSpringManager.getInstance();
    }

    public RMSSession getRMSServerSession() throws JServletException {
        //try {
        return getRMSServerSession(
                DeviceDatabaseSpringManager.getInstance(),
                RmcDatabaseSpringManager.getInstance()
                //new DeviceDatabaseJframeworkManager()
        );
        /*} catch (AuthDatabaseException e) {
            throw new JServletException(e);
        }*/
    }

    public RMSSession getRMSServerSession(DeviceDatabaseModel deviceDatabaseModel, RmcDatabaseModel rmcDatabaseModel) throws JServletException {
        // http session
        HttpSession httpSession = httpServletRequest.getSession();
        // server session
        RMSSession rmsSession1 = (RMSSession) httpSession.getAttribute(HTTP_RMS_SESSION_NAME);
        authSession = getAuthServerSession();
        if (rmsSession1 == null) {
            try {
                rmsSession = new RMSSession(authSession, deviceDatabaseModel, rmcDatabaseModel);
                setRMSServerSession(rmsSession, httpServletRequest);
            } catch (AuthDatabaseException e) {
                throw new JServletException(e);
            }
        } else {
            if (rmsSession1.getUserID() == null) {
                Logger.info("RMSSession OK, AuthSession initializing (Login)");
                rmsSession = new RMSSession(rmsSession, authSession);
                setRMSServerSession(rmsSession, httpServletRequest);
            } else {
                rmsSession = rmsSession1;
            }
        }
        return rmsSession;
    }

    public static class Channel {
        public static final String CHANNEL = "channel";
        public static final String WEBAPP = "webapp";
        public static final String RMC = "rmc";
        public static final String RICHKWARE = "richkware";
    }

    public static class ENCRYPTED_PAYLOAD_KEY {
        public static final String DEVICE_ID = "data0";
        public static final String DATA = "data";
    }

    public static class PAYLOAD_KEY {
        public static final String NAME = "name";
        public static final String STATE_IN_RMS = "stateInRMS";
        public static final String ASSOCIATED_USER = "associatedUser";
        public static final String SERVER_PORT = "serverPort";
        public static final String LOCATION = "location";
        public static final String INSTALLATION_ID = "installationId";
        public static final String DEVICE_INFO = "deviceInfo";
    }
} public class RMSSession extends AuthSession {
    private DeviceDatabaseModel deviceDatabaseModel;
    private RmcDatabaseModel rmcDatabaseModel;
    private String rmcID;       //client id from RichkwareSchema
    private String channel;


    public RMSSession(AuthDatabaseModel authDatabaseModel, DeviceDatabaseModel deviceDatabaseModel, RmcDatabaseModel rmcDatabaseModel) throws AuthDatabaseException {
        super(authDatabaseModel);
        this.deviceDatabaseModel = deviceDatabaseModel;
        this.rmcDatabaseModel = rmcDatabaseModel;
        channel = null;
        rmcID = null;
    }

    public RMSSession(AuthSession authSession, DeviceDatabaseModel deviceDatabaseModel, RmcDatabaseModel rmcDatabaseModel) throws AuthDatabaseException {
        super(authSession);
        this.deviceDatabaseModel = deviceDatabaseModel;
        this.rmcDatabaseModel = rmcDatabaseModel;
        channel = null;
        rmcID = null;
    }

    public RMSSession(RMSSession rmsSession, AuthSession authSession) {
        super(authSession);
        this.deviceDatabaseModel = rmsSession.deviceDatabaseModel;
        this.rmcDatabaseModel = rmsSession.rmcDatabaseModel;
        channel = rmsSession.channel;
        rmcID = rmsSession.rmcID;
    }

    public DeviceDatabaseModel getDeviceDatabaseManager() throws AuthDatabaseException {
        //Logger.i("deviceDatabaseManager" + deviceDatabaseManager);
        if (deviceDatabaseModel == null) {
            Logger.info("init deviceDatabaseModel");
            //deviceDatabaseManager = new DeviceDatabaseManager();
        }
        return deviceDatabaseModel;
    }


    public RmcDatabaseModel getRmcDatabaseManager() throws AuthDatabaseException {
        if (rmcDatabaseModel == null) {
            Logger.info("init rmcDatabaseManager");
            //rmcDatabaseManager = new _RmcDatabaseManager();
        }
        return rmcDatabaseModel;
    }

    public String getRmcID() {
        return rmcID;
    }

    public void setRmcID(String rmcID) {
        this.rmcID = rmcID;
    }

    public String getChannel() {
        return channel;
    }

    public void setChannel(String channel) {
        this.channel = channel;
    }


} public class RMSStatusCode extends AuthStatusCode {
  public static final StatusCode CHANNEL_UNKNOWN = new StatusCode(2401, "Channel Unknown");

}
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import it.richkmeli.jframework.auth.data.exception.AuthDatabaseException;
import it.richkmeli.jframework.auth.web.util.AuthServletManager;
import it.richkmeli.jframework.crypto.Crypto;
import it.richkmeli.jframework.network.tcp.server.http.payload.response.KoResponse;
import it.richkmeli.jframework.network.tcp.server.http.payload.response.OkResponse;
import it.richkmeli.jframework.network.tcp.server.http.util.JServletException;
import it.richkmeli.jframework.util.RandomStringGenerator;
import it.richkmeli.jframework.util.log.Logger;
import it.richkmeli.rms.data.entity.device.DeviceDatabaseModel;
import it.richkmeli.rms.data.entity.device.model.Device;
import it.richkmeli.rms.data.entity.device.model.DeviceInfo;
import it.richkmeli.rms.data.entity.device.model.Location;
import it.richkmeli.rms.data.entity.user.AuthDatabaseSpringManager;
import it.richkmeli.rms.data.entity.user.model.User;
import it.richkmeli.rms.util.GeoLocation;
import it.richkmeli.rms.web.util.RMSServletManager;
import it.richkmeli.rms.web.util.RMSSession;
import it.richkmeli.rms.web.util.RMSStatusCode;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.*;
package it.richkmeli.rms.web.v2.entity;



@RestController
public class DeviceController {
    private static final long serialVersionUID = 1L;
    private static final int keyLength = 32;
    //private final UserRepository userRepository;
    private final String password;

    DeviceController(/*UserRepository userRepository*/) {
        //this.userRepository = userRepository;
        password = ResourceBundle.getBundle("configuration").getString("encryptionkey");
    }

    /**
     * PUT
     * put device to RMS, if for the device is te first call, all the payload is encrypted
     * with the preshared key, otherwise the first key (data0) is encrypted with preshared
     * and others with server-side generated key for specific device (returned at the first
     * call)
     */
    @PutMapping(name = "device", path = {"/device", "/Richkware-Manager-Server/device"})
    public void putDevice() {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        HttpServletResponse response = servletRequestAttributes.getResponse();

        try {
            RMSServletManager rmsServletManager = new RMSServletManager(request, response);
            Map<String, String> attribMap = rmsServletManager.doDefaultProcessRequest(false);

            // server session
            RMSSession rmsSession = rmsServletManager.getRMSServerSession();