@Entity
@JsonInclude
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"id", "company_hjid"})})
public class NegotiationSettings implements Serializable {

    @Id
    @JsonIgnore
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
    @JoinColumn(unique = true)
    private PartyType company;

    @JoinTable
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Range> deliveryPeriodRanges = new ArrayList<>();

    @ElementCollection(targetClass = String.class)
    private List<String> deliveryPeriodUnits = new ArrayList<>();

    @JoinTable
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Range> warrantyPeriodRanges = new ArrayList<>();

    @ElementCollection(targetClass = String.class)
    private List<String> warrantyPeriodUnits = new ArrayList<>();

    @ElementCollection(targetClass = String.class)
    private List<String> incoterms = new ArrayList<>();

    @ElementCollection(targetClass = String.class)
    private List<String> paymentTerms = new ArrayList<>();

    @ElementCollection(targetClass = String.class)
    private List<String> paymentMeans = new ArrayList<>();

    private String serviceLevel;

    @JoinTable
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<CompanySensor> sensors = new ArrayList<>();


    public NegotiationSettings(PartyType company, List<Range> deliveryPeriodRanges, List<String> deliveryPeriodUnits, List<Range> warrantyPeriodRange,
                               List<String> warrantyPeriodUnits, List<String> incoterms, List<String> paymentTerms, List<String> paymentMeans,
                               List<CompanySensor> sensors, String serviceLevel) {
        this.company = company;
        this.deliveryPeriodRanges = deliveryPeriodRanges;
        this.deliveryPeriodUnits = deliveryPeriodUnits;
        this.warrantyPeriodRanges = warrantyPeriodRange;
        this.warrantyPeriodUnits = warrantyPeriodUnits;
        this.incoterms = incoterms;
        this.paymentTerms = paymentTerms;
        this.paymentMeans = paymentMeans;
        this.serviceLevel = serviceLevel;
        this.sensors = sensors;
    }

    public NegotiationSettings() {
    }

    public Long getId() {
        return id;
    }

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

    public PartyType getCompany() {
        return company;
    }

    public void setCompany(PartyType company) {
        this.company = company;
    }

    public List<Range> getDeliveryPeriodRanges() {
        return deliveryPeriodRanges;
    }

    public void setDeliveryPeriodRanges(List<Range> deliveryPeriodRanges) {
        this.deliveryPeriodRanges = deliveryPeriodRanges;
    }

    public List<String> getDeliveryPeriodUnits() {
        return deliveryPeriodUnits;
    }

    public void setDeliveryPeriodUnits(List<String> deliveryPeriodUnits) {
        this.deliveryPeriodUnits = deliveryPeriodUnits;
    }

    public List<Range> getWarrantyPeriodRanges() {
        return warrantyPeriodRanges;
    }

    public void setWarrantyPeriodRanges(List<Range> warrantyPeriodRanges) {
        this.warrantyPeriodRanges = warrantyPeriodRanges;
    }

    public List<String> getWarrantyPeriodUnits() {
        return warrantyPeriodUnits;
    }

    public void setWarrantyPeriodUnits(List<String> warrantyPeriodUnits) {
        this.warrantyPeriodUnits = warrantyPeriodUnits;
    }

    public List<String> getIncoterms() {
        return incoterms;
    }

    public void setIncoterms(List<String> incoterms) {
        this.incoterms = incoterms;
    }

    public List<String> getPaymentTerms() {
        return paymentTerms;
    }

    public void setPaymentTerms(List<String> paymentTerms) {
        this.paymentTerms = paymentTerms;
    }

    public List<String> getPaymentMeans() {
        return paymentMeans;
    }

    public void setPaymentMeans(List<String> paymentMeans) {
        this.paymentMeans = paymentMeans;
    }

    public List<CompanySensor> getSensors() {
        return sensors;
    }

    public void setSensors(List<CompanySensor> sensors) {
        this.sensors = sensors;
    }

    public String getServiceLevel() {
        return serviceLevel;
    }

    public void setServiceLevel(String serviceLevel) {
        this.serviceLevel = serviceLevel;
    }

    public void update(NegotiationSettings newSettings) {
        this.deliveryPeriodRanges.clear();
        this.deliveryPeriodRanges.addAll(newSettings.getDeliveryPeriodRanges());

        this.deliveryPeriodUnits.clear();
        this.deliveryPeriodUnits.addAll(newSettings.getDeliveryPeriodUnits());

        this.warrantyPeriodRanges.clear();
        this.warrantyPeriodRanges.addAll(newSettings.getWarrantyPeriodRanges());

        this.warrantyPeriodUnits.clear();
        this.warrantyPeriodUnits.addAll(newSettings.getWarrantyPeriodUnits());

        this.incoterms.clear();
        this.incoterms.addAll(newSettings.getIncoterms());

        this.paymentMeans.clear();
        this.paymentMeans.addAll(newSettings.getPaymentMeans());

        this.paymentTerms.clear();
        this.paymentTerms.addAll(newSettings.getPaymentTerms());

        if (null != newSettings.company) {
            this.company.setSalesTerms(newSettings.company.getSalesTerms());
            this.company.getProcessIDItems().clear();
            this.company.setProcessID(newSettings.company.getProcessID());

            this.company.getNetwork().clear();
            this.company.getNetwork().addAll(newSettings.company.getNetwork());
        }
    }

    @Entity
    public static class CompanySensor {

        @Id
        @JsonIgnore
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;

        @Column
        @JsonProperty("machine")
        @SerializedName("machine")
        private String machine;

        @Column
        @JsonProperty("sensor")
        @SerializedName("sensor")
        private String sensor;

        @Column
        @JsonProperty("format")
        @SerializedName("format")
        private String format;
    }

    @Entity
    public static class Range {

        @Id
        @JsonIgnore
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;

        @Column
        @JsonProperty("start")
        @SerializedName("start")
        private Long rangeStart;

        @Column
        @JsonProperty("end")
        @SerializedName("end")
        private Long rangeEnd;

        public Range(long start, long end) {
            this.rangeStart = start;
            this.rangeEnd = end;
        }

        public Range() {
        }

        public long getRangeStart() {
            return rangeStart;
        }

        public long getRangeEnd() {
            return rangeEnd;
        }
    }
} @Entity
public class UaaUser implements Serializable {

    @Id
    @Column(nullable = false, unique = true)
    private String externalID;

    private String username;

    @OneToOne
    private PersonType ublPerson;

    @ColumnDefault("false")
    private Boolean showWelcomeInfo = false;

    public UaaUser() {
    }

    public UaaUser(String username, PersonType ublPerson, String externalID) {
        this.username = username;
        this.ublPerson = ublPerson;
        this.externalID = externalID;
        this.showWelcomeInfo = true;
    }

    public String getUsername() {
        return username;
    }

    public String getExternalID() {
        return externalID;
    }

    public PersonType getUBLPerson() {
        return this.ublPerson;
    }

    public Boolean getShowWelcomeInfo() {
        return showWelcomeInfo;
    }

    public void setShowWelcomeInfo(Boolean showWelcomeInfo) {
        this.showWelcomeInfo = showWelcomeInfo;
    }
} public class Address {

    @ApiModelProperty(value = "Name of the street")
    private String streetName;
    @ApiModelProperty(value = "Number of the building")
    private String buildingNumber;
    @ApiModelProperty(value = "Name of the city")
    private String cityName;
    @ApiModelProperty(value = "Postal code of the city")
    private String postalCode;
    @ApiModelProperty(value = "Iso code of the country")
    private String country;
    @ApiModelProperty(value = "Name of the district")
    private String district;
    @ApiModelProperty(value = "Name of the region")
    private String region;
    @ApiModelProperty(value = "Longitude of address")
    private BigDecimal locationLongitude;
    @ApiModelProperty(value = "Latitude of address")
    private BigDecimal locationLatitude;

    public Address() {
    }

    public Address(String streetName, String buildingNumber, String cityName, String postalCode, String country) {
        this.streetName = streetName;
        this.buildingNumber = buildingNumber;
        this.cityName = cityName;
        this.postalCode = postalCode;
        this.country = country;
    }

    public Address(String streetName, String buildingNumber, String cityName, String postalCode, String country, String district, String region) {
        this.streetName = streetName;
        this.buildingNumber = buildingNumber;
        this.cityName = cityName;
        this.postalCode = postalCode;
        this.country = country;
        this.district = district;
        this.region = region;
    }

    public String getStreetName() {
        return streetName;
    }

    public void setStreetName(String streetName) {
        this.streetName = streetName;
    }

    public String getBuildingNumber() {
        return buildingNumber;
    }

    public void setBuildingNumber(String buildingNumber) {
        this.buildingNumber = buildingNumber;
    }

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String cityName) {
        this.cityName = cityName;
    }

    public String getPostalCode() {
        return postalCode;
    }

    public void setPostalCode(String postalCode) {
        this.postalCode = postalCode;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getDistrict() {
        return district;
    }

    public void setDistrict(String district) {
        this.district = district;
    }

    public BigDecimal getLocationLongitude() {
        return locationLongitude;
    }

    public void setLocationLongitude(BigDecimal locationLongitude) {
        this.locationLongitude = locationLongitude;
    }

    public BigDecimal getLocationLatitude() {
        return locationLatitude;
    }

    public void setLocationLatitude(BigDecimal locationLatitude) {
        this.locationLatitude = locationLatitude;
    }

    public String getRegion() {
        return region;
    }

    public void setRegion(String region) {
        this.region = region;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return Objects.equals(streetName, address.streetName) &&
                Objects.equals(buildingNumber, address.buildingNumber) &&
                Objects.equals(cityName, address.cityName) &&
                Objects.equals(postalCode, address.postalCode) &&
                Objects.equals(country, address.country) &&
                Objects.equals(district, address.district) &&
                Objects.equals(region, address.region) &&
                Objects.equals(locationLongitude, address.locationLongitude) &&
                Objects.equals(locationLatitude, address.locationLatitude);
    }

    @Override
    public int hashCode() {
        return Objects.hash(streetName, buildingNumber, cityName, postalCode, country, district, region, locationLongitude, locationLatitude);
    }
}
 @ApiModel(value = "CompanyDescription")
public class CompanyDescription {

    @ApiModelProperty(value = "Statement of the company")
    private Map<NimbleConfigurationProperties.LanguageID, String> companyStatement = new HashMap<>();

    @ApiModelProperty(value = "Main website of the company")
    private String website = null;

    @ApiModelProperty(value = "Identifiers of company photos")
    private List<String> companyPhotoList = new ArrayList<>();

    @ApiModelProperty(value = "Identifiers of company logo image")
    private String logoImageId = null;

    @ApiModelProperty(value = "List of social media references (e.g. Facebook)")
    private List<String> socialMediaList = new ArrayList<>();

    @ApiModelProperty(value = "List of company related events")
    private List<CompanyEvent> events = new ArrayList<>();

    private List<String> externalResources = new ArrayList<>();

    public CompanyDescription() {
    }

    public CompanyDescription(Map<NimbleConfigurationProperties.LanguageID, String> companyStatement, String website, List<String> companyPhotoList, String logoImageId,
                              List<String> socialMediaList, List<CompanyEvent> events, List<String> externalResources) {
        this.companyStatement = companyStatement;
        this.website = website;
        this.companyPhotoList = companyPhotoList;
        this.logoImageId = logoImageId;
        this.socialMediaList = socialMediaList;
        this.events = events;
        this.externalResources = externalResources;
    }

    public Map<NimbleConfigurationProperties.LanguageID, String> getCompanyStatement() {
        return companyStatement;
    }

    public void setCompanyStatement(Map<NimbleConfigurationProperties.LanguageID, String> companyStatement) {
        this.companyStatement = companyStatement;
    }

    public String getWebsite() {
        return website;
    }

    public void setWebsite(String website) {
        this.website = website;
    }

    public List<String> getCompanyPhotoList() {
        return companyPhotoList;
    }

    public void setCompanyPhotoList(List<String> companyPhotoList) {
        this.companyPhotoList = companyPhotoList;
    }

    public String getLogoImageId() {
        return logoImageId;
    }

    public void setLogoImageId(String logoImageId) {
        this.logoImageId = logoImageId;
    }

    public List<String> getSocialMediaList() {
        return socialMediaList;
    }

    public void setSocialMediaList(List<String> socialMediaList) {
        this.socialMediaList = socialMediaList;
    }

    public List<CompanyEvent> getEvents() {
        return events;
    }

    public void setEvents(List<CompanyEvent> events) {
        this.events = events;
    }

    public List<String> getExternalResources() {
        return externalResources;
    }

    public void setExternalResources(List<String> externalResources) {
        this.externalResources = externalResources;
    }
} public class CompanyDetails {

    @ApiModelProperty(value = "Trade name name of the company")
    private Map<LanguageID, String> brandName = null;

    @ApiModelProperty(value = "Legal name of the company")
    private Map<LanguageID, String> legalName = null;

    @ApiModelProperty(value = "VAT identification number of the company")
    private String vatNumber = null;

    @ApiModelProperty(value = "Additional info used for verifying the company")
    private String verificationInformation = null;

    @ApiModelProperty(value = "Main address of the company")
    private Address address = null;

    @ApiModelProperty(value = "Business type identifier")
    private String businessType = null;

    @ApiModelProperty(value = "Keywords explaining business objective")
    private Map<LanguageID, String> businessKeywords = new HashMap<>();

    @ApiModelProperty(value = "Year since company is active")
    private Integer yearOfCompanyRegistration;

    @ApiModelProperty(value = "List of industry sectors in which company is active in.")
    private Map<LanguageID, String> industrySectors = new HashMap<>();

    @ApiModelProperty(value = "List of process instance ids which are included in the workflow of company")
    private List<String> processIds = new ArrayList<>();

    @ApiModelProperty(value = "List of users participating in the company")
    private List<PersonType> person = null;

    public CompanyDetails() {
    }

    public CompanyDetails(Map<LanguageID, String> brandName, Map<LanguageID, String> legalName, String vatNumber,
                          String verificationInformation, Address address, String businessType, Map<LanguageID, String> businessKeywords,
                          Integer yearOfCompanyRegistration, Map<LanguageID, String> industrySectors, List<String> processIds, List<PersonType> person) {
        this.brandName = brandName;
        this.legalName = legalName;
        this.vatNumber = vatNumber;
        this.verificationInformation = verificationInformation;
        this.address = address;
        this.businessType = businessType;
        this.businessKeywords = businessKeywords;
        this.yearOfCompanyRegistration = yearOfCompanyRegistration;
        this.industrySectors = industrySectors;
        this.processIds = processIds;
        this.person = person;
    }

    public Map<LanguageID, String> getBrandName() {
        return brandName;
    }

    public void setBrandName(Map<LanguageID, String> brandName) {
        this.brandName = brandName;
    }

    public Map<LanguageID, String> getLegalName() {
        return legalName;
    }

    public void setLegalName(Map<LanguageID, String> legalName) {
        this.legalName = legalName;
    }

    public String getVatNumber() {
        return vatNumber;
    }

    public void setVatNumber(String vatNumber) {
        this.vatNumber = vatNumber;
    }

    public String getVerificationInformation() {
        return verificationInformation;
    }

    public void setVerificationInformation(String verificationInformation) {
        this.verificationInformation = verificationInformation;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String getBusinessType() {
        return businessType;
    }

    public void setBusinessType(String businessType) {
        this.businessType = businessType;
    }

    public Map<LanguageID, String> getBusinessKeywords() {
        return businessKeywords;
    }

    public void setBusinessKeywords(Map<LanguageID, String> businessKeywords) {
        this.businessKeywords = businessKeywords;
    }

    public Integer getYearOfCompanyRegistration() {
        return yearOfCompanyRegistration;
    }

    public void setYearOfCompanyRegistration(Integer yearOfCompanyRegistration) {
        this.yearOfCompanyRegistration = yearOfCompanyRegistration;
    }

    public Map<LanguageID, String> getIndustrySectors() {
        return industrySectors;
    }

    public void setIndustrySectors(Map<LanguageID, String> industrySectors) {
        this.industrySectors = industrySectors;
    }

    public List<String> getProcessIds() {
        return processIds;
    }

    public void setProcessIds(List<String> processIds) {
        this.processIds = processIds;
    }

    public List<PersonType> getPerson() {
        return person;
    }

    public void setPerson(List<PersonType> person) {
        this.person = person;
    }
}
 public class CompanyTradeDetails {

    private List<PaymentMeans> paymentMeans = new ArrayList<>();
    private List<DeliveryTerms> deliveryTerms = new ArrayList<>();
    private Integer ppapCompatibilityLevel;
    private String stripeAccountId;

    public CompanyTradeDetails() {
    }

    public CompanyTradeDetails(List<PaymentMeans> paymentMeans, List<DeliveryTerms> deliveryTerms, Integer ppapCompatibilityLevel, String stripeAccountId) {
        this.paymentMeans = paymentMeans;
        this.deliveryTerms = deliveryTerms;
        this.ppapCompatibilityLevel = ppapCompatibilityLevel;
        this.stripeAccountId = stripeAccountId;
    }

    public List<PaymentMeans> getPaymentMeans() {
        return paymentMeans;
    }

    public void setPaymentMeans(List<PaymentMeans> paymentMeans) {
        this.paymentMeans = paymentMeans;
    }

    public List<DeliveryTerms> getDeliveryTerms() {
        return deliveryTerms;
    }

    public void setDeliveryTerms(List<DeliveryTerms> deliveryTerms) {
        this.deliveryTerms = deliveryTerms;
    }

    public Integer getPpapCompatibilityLevel() {
        return ppapCompatibilityLevel;
    }

    public void setPpapCompatibilityLevel(Integer ppapCompatibilityLevel) {
        this.ppapCompatibilityLevel = ppapCompatibilityLevel;
    }

    public String getStripeAccountId() {
        return stripeAccountId;
    }

    public void setStripeAccountId(String stripeAccountId) {
        this.stripeAccountId = stripeAccountId;
    }
} public interface PartyRepository extends PagingAndSortingRepository<PartyType, Long>, JpaSpecificationExecutor<PartyType> {

    @Transactional
    Iterable<PartyType> findAll(Sort sort);

    @Transactional
    Iterable<PartyType> findAllByDeletedIsFalse(Sort sort);

    @Transactional
    Page<PartyType> findAll(Pageable pageable);

    List<PartyType> findByHjid(Long hijd);

    List<PartyType> findByPerson(PersonType person);

    @Modifying
    @Transactional
    long deleteByHjid(Long hijd);

    @Query(value = "select hjid from document_reference_type where document_reference_party_typ_0 = ?1 and document_type = ?2", nativeQuery = true)
    List<BigInteger> findDocumentIds(Long companyId, String documentType);

    @Query(value = "FROM PartyType WHERE stripeAccountId = ?1")
    List<PartyType> findByStripeAccountId(String accountId);

    @Query(value = "FROM PartyType WHERE productPublishSubscription is not null")
    List<PartyType> findAllWithSubscriptions();

    @Query(value = "select pt.hjid from document_reference_type drt,trading_preferences tp,party_type pt where drt.id = ?1 and tp.hjid = drt.document_reference_trading_p_0 and pt.sales_terms_party_type_hjid = tp .hjid",nativeQuery = true)
    List<BigInteger> findByTermsAndConditionsDocumentReferenceId(String id);
} public interface UaaUserRepository extends PagingAndSortingRepository<UaaUser, Long> {
    Page<UaaUser> findAll(Pageable pageable);

    List<UaaUser> findByUsername(String username);

    UaaUser findOneByUsername(String username);

    UaaUser findByExternalID(String externalId);

    @Modifying
    @Transactional
    long deleteByUblPerson(PersonType ublPerson);

    List<UaaUser> findByUblPerson(PersonType ublPerson);
} @SuppressWarnings("Convert2MethodRef")
@Service
public class KeycloakAdmin {

    private static final Logger logger = LoggerFactory.getLogger(KeycloakAdmin.class);

    public static final String NIMBLE_USER_ROLE = "nimble_user";
    public static final String INITIAL_REPRESENTATIVE_ROLE = "initial_representative";
    public static final String LEGAL_REPRESENTATIVE_ROLE = "legal_representative";
    public static final String PLATFORM_MANAGER_ROLE = "platform_manager";
    public static final String NIMBLE_DELETED_USER = "nimble_deleted_user";

    public static final String PLATFORM_MANAGER_GROUP = "Platform Manager";

    public static final List<String> NON_ASSIGNABLE_ROLES = Arrays.asList("platform_manager", "uma_authorization",
            "offline_access", "admin", "create-realm",
            "create-realm", "nimble_user", "initial_representative");

    public static final List<String> NON_NIMBLE_ROLES = Arrays.asList("uma_authorization", "offline_access", "admin", "create-realm", "create-realm");

    @Value("${nimble.oauth.identityProvider.eFactory}")
    private String eFactoryIdentityProvider;

    @Autowired
    private KeycloakConfig keycloakConfig;

    @Autowired
    private OAuthClient oAuthClient;

    @Autowired
    private OAuthClientConfig oAuthClientConfig;

    private Keycloak keycloak;

    private static long oneHourInMilliSeconds = 3600000;

    @PostConstruct
    @SuppressWarnings("unused")
    public void init() {
        ResteasyClient client = new ResteasyClientBuilder().connectionPoolSize(10).build();
        this.keycloak = KeycloakBuilder.builder()
                .serverUrl(keycloakConfig.getServerUrl())
                .realm(keycloakConfig.getRealm())
                .grantType(OAuth2Constants.PASSWORD)
                .username(keycloakConfig.getAdmin().getUsername())
                .password(keycloakConfig.getAdmin().getPassword())
                .clientId(keycloakConfig.getAdmin().getCliendId())
                .clientSecret(keycloakConfig.getAdmin().getCliendSecret())
                .resteasyClient(client)
                .build();
    }

    public String initiatePasswordRecoveryProcess(String email) {
        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        UsersResource userResource = realmResource.users();

        List<UserRepresentation> userList = userResource.search(email);
        if (userList.size() != 0) {
            UserRepresentation user = userList.get(0);
            return JWT.create()
                    .withJWTId(user.getId())
                    .withClaim(GlobalConstants.JWT_TYPE_ATTRIBUTE_STRING, "reset-credentials")
                    .withSubject(user.getId())
                    .withExpiresAt(new Date(System.currentTimeMillis() + oneHourInMilliSeconds))
                    .sign(HMAC512(keycloakConfig.getAdmin().getCliendSecret().getBytes()));
        }else {
            throw new ResourceNotFoundException("Resource not found for the user name");
        }
    }

    public void resetPasswordViaRecoveryProcess(String token, String newPassword) {

        try {
            JWTVerifier verifier = JWT.require(HMAC512(keycloakConfig.getAdmin().getCliendSecret().getBytes())).build();
            verifier.verify(token);
        } catch (SignatureVerificationException e) {
            logger.warn("Invalid token received for password reset");
            throw new BadRequestException("Invalid Token");
        }

        long exp = JWT.decode(token).getClaim(GlobalConstants.JWT_EXPIRY_ATTRIBUTE_STRING).asLong();
        if (System.currentTimeMillis() > exp) {
            String sub = JWT.decode(token).getClaim(GlobalConstants.JWT_SUBJECT_ATTRIBUTE_STRING).asString();
            RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
            UserResource use = realmResource.users().get(sub);
            CredentialRepresentation passwordCredential = createPasswordCredentials(newPassword);
            passwordCredential.setTemporary(false);
            this.keycloak.realm(keycloakConfig.getRealm()).users().get(sub).resetPassword(passwordCredential);
        }else {
            throw new NotAuthorizedException("URL expired");
        }
    }

    /**
     * Verify the jwt token provided by the Keycloak
     * TODO: Implement verification by using the certs
     * https://stackoverflow.com/questions/39890232/how-to-decode-keys-from-keycloak-openid-connect-cert-api
     * @param jwtToken
     * @return
     */
    public boolean verify(String jwtToken) throws ServerException {

        String uri = keycloakConfig.getServerUrl() + "/realms/master";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> entity = new HttpEntity<String>(headers);
        RestTemplate rs = new RestTemplate();

        try {
            ResponseEntity<String> response = rs.exchange(uri, HttpMethod.GET, entity, String.class);
            ObjectMapper mapper = new ObjectMapper();
            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

            RealmConfigs realmConfigs = mapper.readValue(response.getBody(), RealmConfigs.class);
            JwtHelper.decodeAndVerify(jwtToken, new RsaVerifier(getRSAPublicKey(realmConfigs.getPublic_key())));
        } catch (IOException e) {
            logger.error("Error in extracting the data from Keycloak realm response{}", e);
            throw new ServerException("Keycloak server error", e);
        } catch (Exception e) {
            logger.error("Error in verifying token{}", e);
            return false;
        }
        return true;
    }


    /**
     * Generates the RSA Public key from Keycloak public key
     * @param publicKey
     * @return
     * @throws Exception
     */
    private RSAPublicKey getRSAPublicKey(String publicKey) throws Exception {
        if( StringUtils.isBlank(publicKey)) return null;
        try {
            KeyFactory keyFactory = java.security.KeyFactory.getInstance("RSA");
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(java.util.Base64.getDecoder().decode(publicKey));
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
            logger.error("Error forming RSA key {}", e);
            throw new Exception(e);
        }
    }

    /**
     * throws javax.ws.rs.WebApplicationException with corrensponding response for error
     **/
    public String registerUser(String firstName, String lastName, String password, String email) {

        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        UsersResource userResource = realmResource.users();

        // create proper credentials
        CredentialRepresentation passwordCredentials = createPasswordCredentials(password);

        // create user
        UserRepresentation user = new UserRepresentation();
        user.setUsername(email);
        user.setCredentials(Collections.singletonList(passwordCredentials));
        user.setFirstName(firstName);
        user.setLastName(lastName);
        user.setEmail(email);
        user.setRequiredActions(Collections.emptyList());
        user.setEnabled(true);
        user.setEmailVerified(false);

        // extract identifier of user
        Response response = userResource.create(user);
        String userId = extractCreatedId(response);
        UserResource createdUser = fetchUserResource(userId);

        // ToDo: throw exception if user already exists

        // set password
        createdUser.resetPassword(passwordCredentials);

//        // send verification mail
//        createdUser.executeActionsEmail(oAuthClientConfig.getCliendId(), "http://localhost:102", Collections.singletonList("VERIFY_EMAIL"));
//        createdUser.sendVerifyEmail(oAuthClientConfig.getCliendId());

        addRole(userId, NIMBLE_USER_ROLE);

        return createdUser.toRepresentation().getId();
    }

    public void deleteUser(String externalId) {
        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        UsersResource userResource = realmResource.users();

        // delete user
        userResource.delete(externalId);
    }

    public void deleteUserByUsername(String username) {
        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        UsersResource userResource = realmResource.users();

        List<UserRepresentation> userRepresentations = userResource.search(username);
        userRepresentations.forEach(userRepresentation -> userResource.delete(userRepresentation.getId()));
    }

    public Map<String, String> getAssignableRoles() {
        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());

        return realmResource.roles().list().stream()
                .filter(r -> NON_ASSIGNABLE_ROLES.contains(r.getName()) == false)
                .collect(Collectors.toMap(r -> r.getId(), r -> r.getName()));
    }

    public Set<String> getUserRoles(String userId) {
        return getUserRoles(userId, new ArrayList<>());
    }

    public Set<String> getUserRoles(String userId, List<String> excludeRoles) {

        List<String> finalExcludeRoles = (excludeRoles == null) ? new ArrayList<>() : excludeRoles;
        UserResource userResource = fetchUserResource(userId);
        return userResource.roles().realmLevel().listAll().stream()
                .map(r -> r.getName())
                .filter(role -> finalExcludeRoles.contains(role) == false)
                .collect(Collectors.toSet());
    }

    public UserResource getUserResource(String userId) {
        return fetchUserResource(userId);
    }

    public String getEFactoryUserId(UserResource userResource){
        for (FederatedIdentityRepresentation federatedIdentityRepresentation : userResource.getFederatedIdentity()) {
            if(federatedIdentityRepresentation.getIdentityProvider().contentEquals(eFactoryIdentityProvider)){
                    return federatedIdentityRepresentation.getUserId();
            }
        }
        return null;
    }
    public void addRole(String userId, String role) {
        UserResource userResource = fetchUserResource(userId);

        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        RoleRepresentation roleRepresentation = realmResource.roles().get(role).toRepresentation();
        userResource.roles().realmLevel().add(Collections.singletonList(roleRepresentation));
    }

    public void removeRole(String userId, String role) {
        UserResource userResource = fetchUserResource(userId);

        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        RoleRepresentation roleRepresentation = realmResource.roles().get(role).toRepresentation();
        userResource.roles().realmLevel().remove(Collections.singletonList(roleRepresentation));
    }

    public List<UserRepresentation> getPlatformManagers() {

        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        List<GroupRepresentation> groups = realmResource.groups().groups();

        Optional<GroupRepresentation> platformManagerGroup = groups.stream().filter(g -> "Platform Manager".equals(g.getName())).findFirst();
        if (platformManagerGroup.isPresent() == false) {
            logger.warn("No platform managers found!");
            return new ArrayList<>();  // empty list as fallback
        }
        return realmResource.groups().group(platformManagerGroup.get().getId()).members();
    }

    private UserResource fetchUserResource(String userId) {
        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        return realmResource.users().get(userId);
    }

    public boolean resetPassword(UaaUser user, String oldPassword, String newPassword) {

        try {
            // verify existing password
            OAuth2AccessToken accessToken = oAuthClient.getToken(user.getUsername(), oldPassword);
            if (accessToken == null)
                return false;

            // set new password
            CredentialRepresentation passwordCredential = createPasswordCredentials(newPassword);
            this.keycloak.realm(keycloakConfig.getRealm()).users().get(user.getExternalID()).resetPassword(passwordCredential);
        } catch (OAuth2AccessDeniedException ex) {
            logger.info("Authentication error while setting new password");
            return false;
        }

        return true;
    }

    private String extractCreatedId(Response response) {
        URI location = response.getLocation();
        if (!response.getStatusInfo().equals(Response.Status.CREATED)) {
            Response.StatusType statusInfo = response.getStatusInfo();
            throw new WebApplicationException("Create method returned status " +
                    statusInfo.getReasonPhrase() + " (Code: " + statusInfo.getStatusCode() + "); expected status: Created (201)",
                    statusInfo.getStatusCode());
        }
        if (location == null) {
            return null;
        }
        String path = location.getPath();
        return path.substring(path.lastIndexOf('/') + 1);
    }

    public int applyRoles(String userID, Set<String> rolesToApply) {
        // setting proper set of roles
        Set<String> currentRoles = getUserRoles(userID, NON_ASSIGNABLE_ROLES);
        Set<String> rolesToRemove = Sets.difference(currentRoles, rolesToApply);
        Set<String> rolesToAdd = Sets.difference(rolesToApply, currentRoles);
        logger.info("Applying new roles to user {}: add: {}, remove: {}", userID, rolesToAdd, rolesToRemove);
        for (String role : rolesToRemove)
            removeRole(userID, role);
        for (String role : rolesToAdd)
            addRole(userID, role);
        return rolesToAdd.size() + rolesToRemove.size();
    }

    public static List<String> prettfiyRoleIDs(List<String> roleIDs) {
        return roleIDs.stream().map(r -> WordUtils.capitalize(r.replace("_", " "))).collect(Collectors.toList());
    }

    private static CredentialRepresentation createPasswordCredentials(String password) {
        CredentialRepresentation passwordCredentials = new CredentialRepresentation();
        passwordCredentials.setTemporary(false);
        passwordCredentials.setType(CredentialRepresentation.PASSWORD);
        passwordCredentials.setValue(password);
        return passwordCredentials;
    }
} @Service
public class OAuthClient {

    @Autowired
    private OAuthClientConfig config;

    @Autowired
    private OAuth2ClientContext oauth2Context;

    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }

    public OAuth2AccessToken getToken(String username, String password) {

        // build resources
        ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
        resourceDetails.setClientId(config.getCliendId());
        resourceDetails.setClientSecret(config.getCliendSecret());
        resourceDetails.setAccessTokenUri(config.getAccessTokenUri());
        resourceDetails.setUsername(username);
        resourceDetails.setPassword(password);

        // build provider
        ResourceOwnerPasswordAccessTokenProvider accessTokenProvider = new ResourceOwnerPasswordAccessTokenProvider();
        AccessTokenRequest accessTokenRequest = oauth2Context.getAccessTokenRequest();

        // fetch token
        OAuth2AccessToken accessToken = accessTokenProvider.obtainAccessToken(resourceDetails, accessTokenRequest);
        return accessToken;
    }

    public OAuth2AccessToken refreshToken(String refreshToken) {

        // build resources
        ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
        resourceDetails.setClientId(config.getCliendId());
        resourceDetails.setClientSecret(config.getCliendSecret());
        resourceDetails.setAccessTokenUri(config.getAccessTokenUri());

        // build provider
        AccessTokenRequest accessTokenRequest = oauth2Context.getAccessTokenRequest();
        ResourceOwnerPasswordAccessTokenProvider accessTokenProvider = new ResourceOwnerPasswordAccessTokenProvider();

        // perform refresh
        OAuth2AccessToken newAccessToken = accessTokenProvider.refreshAccessToken(resourceDetails, () -> refreshToken, accessTokenRequest);
        return newAccessToken;
    }

    public enum Role {
        NIMBLE_USER("nimble_user"),
        INITIAL_REPRESENTATIVE("initial_representative"),
        LEGAL_REPRESENTATIVE("legal_representative"),
        PLATFORM_MANAGER("platform_manager"),
        COMPANY_ADMIN("company_admin"),
        PUBLISHER("publisher"),
        NIMBLE_DELETED_USER("nimble_deleted_user"),
        EXTERNAL_REPRESENTATIVE("external_representative"),
        MONITOR("monitor"),
        EFACTORY_USER("eFactoryUser");

        private final String role;

        Role(final String role) {
            this.role = role;
        }

        @Override
        public String toString() {
            return role;
        }
    }
} public class OpenIdConnectUserDetails implements UserDetails {

    private static final long serialVersionUID = 1L;

    private String userId;
    private String username;
    private List<String> realmRoles;
    private List<String> resourcesRoles;
    private List<GrantedAuthority> grantedAuthorities;

    @SuppressWarnings("unchecked")
    public OpenIdConnectUserDetails(String oidToken) throws IOException {

        Jwt tokenDecoded = JwtHelper.decode(oidToken);
        Map<String, Object> authInfo = new ObjectMapper().readValue(tokenDecoded.getClaims(), Map.class);

        this.userId = (String) authInfo.get("sub");
        this.username = (String) authInfo.get("email");


        realmRoles = (List<String>) ((Map<String, List>) authInfo.get("realm_access")).get("roles");
        resourcesRoles = (List<String>) ((Map<String, Map<String, List>>) authInfo.get("resource_access")).get("account").get("roles");
        grantedAuthorities = realmRoles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
    }

    public static OpenIdConnectUserDetails fromBearer(String bearer) throws IOException {
        return new OpenIdConnectUserDetails(bearer.replace("Bearer ", ""));
    }

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

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return grantedAuthorities;
    }

    public Boolean hasRole(String role) {
        return grantedAuthorities.stream().map(GrantedAuthority::getAuthority).anyMatch(r -> r.equals(role));
    }

    public String getUserId() {
        return userId;
    }

    @Override
    public String getPassword() {
        return null;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    public List<String> getRealmRoles() {
        return realmRoles;
    }

    public List<String> getResourcesRoles() {
        return resourcesRoles;
    }
}
import eu.nimble.core.infrastructure.identity.entity.NegotiationSettings;
import eu.nimble.core.infrastructure.identity.entity.UaaUser;
import eu.nimble.core.infrastructure.identity.entity.dto.Address;
import eu.nimble.core.infrastructure.identity.entity.dto.CompanyDescription;
import eu.nimble.core.infrastructure.identity.entity.dto.CompanyDetails;
import eu.nimble.core.infrastructure.identity.entity.dto.CompanyTradeDetails;
import eu.nimble.core.infrastructure.identity.repository.PartyRepository;
import eu.nimble.core.infrastructure.identity.repository.UaaUserRepository;
import eu.nimble.core.infrastructure.identity.uaa.KeycloakAdmin;
import eu.nimble.core.infrastructure.identity.uaa.OAuthClient;
import eu.nimble.core.infrastructure.identity.uaa.OpenIdConnectUserDetails;
import eu.nimble.service.model.ubl.commonaggregatecomponents.LocationType;
import eu.nimble.service.model.ubl.commonaggregatecomponents.PartyType;
import eu.nimble.service.model.ubl.commonaggregatecomponents.PersonType;
import eu.nimble.service.model.ubl.commonaggregatecomponents.TradingPreferences;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.ws.rs.NotFoundException;
import java.io.IOException;
import java.util.*;
package eu.nimble.core.infrastructure.identity.service;



@Service
public class IdentityService {

    private static final Logger logger = LoggerFactory.getLogger(IdentityService.class);

    @Autowired
    private UaaUserRepository uaaUserRepository;

    @Autowired
    private PartyRepository partyRepository;

    @Autowired
    private KeycloakAdmin keycloakAdmin;

    public UaaUser getUserfromBearer(String bearer) throws IOException {
        OpenIdConnectUserDetails userDetails = getUserDetails(bearer);
        return uaaUserRepository.findByExternalID(userDetails.getUserId());
    }

    public OpenIdConnectUserDetails getUserDetails(String bearer) throws IOException {
        return OpenIdConnectUserDetails.fromBearer(bearer);
    }

    /**
     * Checks if the bearer contains at least one of the given roles.
     *
     * @param bearer Token containing roles
     * @param roles  Roles to check
     * @return True if at least one matching role was found.
     * @throws IOException if roles could not be extracted from token
     */
    public boolean hasAnyRole(String bearer, OAuthClient.Role... roles) throws IOException {
        OpenIdConnectUserDetails details = getUserDetails(bearer);
        return Arrays.stream(roles).anyMatch(r -> details.hasRole(r.toString()));
    }

    public Optional<PartyType> getCompanyOfUser(UaaUser uaaUser) {
        if (uaaUser == null)
            return Optional.empty();
        return partyRepository.findByPerson(uaaUser.getUBLPerson()).stream().findFirst();
    }

    @SuppressWarnings("SimplifiableIfStatement")
    public boolean inSameCompany(UaaUser userOne, UaaUser userTwo) {

        if (userOne == null || userTwo == null)
            return false;

        Optional<PartyType> requestingCompany = getCompanyOfUser(userOne);
        Optional<PartyType> targetCompany = getCompanyOfUser(userTwo);
        if ((requestingCompany.isPresent() && targetCompany.isPresent()) == false) // check if companies exist
            return false;

        return requestingCompany.get().getHjid().equals(targetCompany.get().getHjid());
    }

    public Set<String> fetchRoles(PersonType personType) {
        try {
            UaaUser uaaUser = uaaUserRepository.findByUblPerson(personType).stream().findFirst().orElseThrow(NotFoundException::new);
            return fetchRoles(uaaUser);
        } catch (Exception exception) {
            logger.error("Error while fetching roles for person", exception);
        }
        return Collections.emptySet();
    }

    public Set<String> fetchRoles(UaaUser user) {

        Set<String> roles = new HashSet<>();
        try {
            // collect roles
            logger.debug("Fetching roles of user {}", user.getUsername());
            roles = keycloakAdmin.getUserRoles(user.getExternalID(), KeycloakAdmin.NON_NIMBLE_ROLES);
        } catch (Exception ex) {
            logger.error("Error while fetch roles of user", ex);
        }

        return roles;
    }

    public void enrichWithRoles(PersonType person) {
        Set<String> roles = fetchRoles(person);
        person.getRole().clear();
        person.getRole().addAll(roles);
    }

    public void enrichWithRoles(PartyType party) {
        party.getPerson().forEach(this::enrichWithRoles);
    }

    public static Double computeDetailsCompleteness(CompanyDetails companyDetails) {