<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Base Blocks tech</title>
    <description>The latest articles on DEV Community by Base Blocks tech (@baseblocks).</description>
    <link>https://dev.to/baseblocks</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2638%2Fee19a67b-bd63-4a68-97fa-10846409b1b7.png</url>
      <title>DEV Community: Base Blocks tech</title>
      <link>https://dev.to/baseblocks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/baseblocks"/>
    <language>en</language>
    <item>
      <title>How-to #2. Audit trail using envers</title>
      <dc:creator>Oleksii Kondratiuk</dc:creator>
      <pubDate>Sun, 27 Sep 2020 10:24:04 +0000</pubDate>
      <link>https://dev.to/baseblocks/how-to-2-audit-trail-using-envers-jhi</link>
      <guid>https://dev.to/baseblocks/how-to-2-audit-trail-using-envers-jhi</guid>
      <description>&lt;h1&gt;
  
  
  Motivation
&lt;/h1&gt;

&lt;p&gt;Consider case when changes that user or some system makes to the data in the database need to be tracked. Sometimes it is the requirement from the officials to have ability to get history of this changes, sometimes we just want to have the view on what happened in the system for error processing/debug/monitoring purposes or it might be we just want to have historical data about how, for example, height of a kid changed. All this cases can be covered with pretty simple solution - logging of the latest state of the data at the moment of changes. It is called audit trail. &lt;/p&gt;

&lt;h1&gt;
  
  
  Requirements
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Changes in tables are tracked automatically whenever we call save, update or delete operations on our repository level;&lt;/li&gt;
&lt;li&gt;Ability to easily query audit trail.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Tech solution
&lt;/h1&gt;

&lt;p&gt;We are going to work with our project &lt;a href="https://github.com/baseblocks/bb-user-service"&gt;User service&lt;/a&gt; which was used for &lt;a href="https://dev.to/baseblocks/how-to-1-data-encryption-548m"&gt;previous article&lt;/a&gt; in the series. Our goal to have audit trail of all the changes of the user data.&lt;/p&gt;

&lt;p&gt;Spring Data has the mechanism called Spring Data Envers which allow access to entity revisions managed by Hibernate Envers. It will automatically write entities snapshots of the User entity on the update, save and delete operation into separate table(user_history in our case). Let's have a look how it can be implemented.&lt;/p&gt;

&lt;p&gt;Respectful dependency should be added to enable it in our project:&lt;br&gt;
&lt;code&gt;compile group: 'org.springframework.data', name: 'spring-data-envers'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Repository factory bean should be changed. It can be done by adding EnableJpaRepositories(or modifying if you already have it) on the Application class(class marked by @Configuration) with the EnversRevisionRepositoryFactoryBean as factory bean class:&lt;br&gt;
&lt;code&gt;@EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Audited annotation should be added to enable audit trail of the particular entity:&lt;br&gt;
&lt;code&gt;@Entity&lt;br&gt;
@Data&lt;br&gt;
@Table(name = "bb_user")&lt;br&gt;
@EqualsAndHashCode(onlyExplicitlyIncluded = true)&lt;br&gt;
@Audited&lt;br&gt;
public class User {&lt;br&gt;
...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Repository should extend one more interface - RevisionRepository. It enables repo to access revisions of the entity when you will want to get all the data revisions or some particular one:&lt;br&gt;
&lt;code&gt;public interface UserRepository extends RevisionRepository&amp;lt;User,Long, Integer&amp;gt;, CrudRepository&amp;lt;User, Long&amp;gt; {&lt;br&gt;
...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There is no need to define user_history entity. You should just specify the suffix that Spring Data will add automatically add to the name of the entity and will use as a name of the table to which audit trail will be written. It is configurable in application.yaml:&lt;br&gt;
&lt;code&gt;spring.jpa.properties.org.hibernate.envers.audit_table_suffix: _history&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In our case user_history is the table that will be used for audit trail. Let's configure Spring JPA to create tables on the application startup(set spring.jpa.generate-ddl to true and spring.jpa.hibernate.ddl-auto to create), start the application and have a look at this table. Now we have user and usera_history tables in the database. user_history table has two additional columns in comparison to the user table - rev and revtype. Revision is store in the rev column. revtype defines the type of the operation that was performed on the entity:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;0 - ADD - A database table row was inserted;&lt;/li&gt;
&lt;li&gt;1 - MOD - A database table row was updated;&lt;/li&gt;
&lt;li&gt;2 - DEL - A database table row was deleted.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Moving to the test. First user needs to be created. In this case new records will be created in the user database with revtype 0 and first revision. revision is one per table, not per entity, this is important. So if I insert next user then the last revision (let`s say 2) will be taken and incremented by 1(3). Then if i insert next user then its revision will be 4. And so on.&lt;br&gt;
User record will be updated, but there will be new record in user_history table with next revision in case of update operation on the existing user.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Spring Data Envers is a very convenient mechanism for the audit trail. It supports more complicated mappings as well. For instance, Audited annotation can be put not on the class level, but on some specific field and only that field will be audited. I encourage you to check its Javadoc and check &lt;a href="https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#envers"&gt;Hibernate doc&lt;/a&gt; to get more insights.&lt;/p&gt;

</description>
      <category>java</category>
    </item>
    <item>
      <title>How-to #1. Data encryption.</title>
      <dc:creator>Oleksii Kondratiuk</dc:creator>
      <pubDate>Sun, 06 Sep 2020 16:14:32 +0000</pubDate>
      <link>https://dev.to/baseblocks/how-to-1-data-encryption-548m</link>
      <guid>https://dev.to/baseblocks/how-to-1-data-encryption-548m</guid>
      <description>&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;Pretty much every developer faces a lot of challenges along his career. Especially if you work on a lot of different projects and assigned a variety of diverse tasks. Of course, most of them are pretty obvious and requires knowledge that you already have. But some amount of them requires an understanding of the specifics. That is why I start a series of how-to blog posts. I want to share my practical experience in frameworks, databases, servers, clouds, etc. I hope this will help someone get a better knowledge of them, spend less time wondering how to do this or that, and be more productive in general.&lt;/p&gt;

&lt;h1&gt;
  
  
  Data encryption
&lt;/h1&gt;

&lt;p&gt;Each and every user wants their data to be in safe. No matter if it is his progress in learning something in some online system or his money in online banking. We all want this data to be protected. That is also why GDPR and other regulations appeared. I will cover how to make sensitive data encrypted in the SQL database if you use Java and such technologies like Spring and Hibernate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project overview
&lt;/h2&gt;

&lt;p&gt;It is a practical guide, so let`s build some small project which we will utilize to master this topic. Assume you build a microservice that is responsible to store user data. Not authenticate, but just storing whatever is specific to the user. &lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;Birthdate, country of origin, address, etc. All this is sensitive data that is not subject to sharing to third parties and even to the employees of the company where you work. Obviously you should restrict access to production servers and databases, make sure that your APIs do not allow you to get data of some other person, but these measures restrict access to the data. Data itself is still vulnerable - if someone gets access to the database then we are in trouble. Let`s see how we can fix this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Data should be stored encrypted in the database.&lt;/li&gt;
&lt;li&gt;Encryption should happen when data stored in the database.&lt;/li&gt;
&lt;li&gt;Decryption should happen each time we get data from the database.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tech solution
&lt;/h2&gt;

&lt;p&gt;There are different ways how to implement this. We will focus on one specific now. Hibernate converters and Java encryption capabilities are going to be used for the solution.&lt;/p&gt;

&lt;p&gt;Let`s start with our model:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity
@Data
@Table(name = "bb_user")
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class User {

    @Id
    @GeneratedValue
    @EqualsAndHashCode.Include
    private Long id;

    @Convert(converter = StringAttributeConverter.class)
    private String name;
    private String token;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you most probably noticed, there is Convert annotation from javax.persistence package. Its role here to trigger StringAttributeConverter exactly at the moments when we need it. Here its source code:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component
public class StringAttributeConverter implements 
AttributeConverter&amp;lt;String, String&amp;gt; {
    @Autowired
    private Encryptor encryptor;

    @Override
    public String convertToDatabaseColumn(String attribute) {
        return encryptor.encrypt(attribute);
    }

    @Override
    public String convertToEntityAttribute(String dbData) {
         return encryptor.decrypt(dbData);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;convertToDatabaseColumn&lt;/code&gt; method is triggered when the entity is saved to the database or an existing record is updated. In our case encryptor is triggered to encrypt data. The field value for which the method is executed(name in our case) will be substituted with the value produced by this method before the flush of the transaction.&lt;br&gt;
&lt;code&gt;convertToEntityAttribute&lt;/code&gt; method obviously is triggered at the moment when the entity is obtained from the database. The result of method execution is decrypted name.&lt;br&gt;
The repository for this entity is a simple implementor of the Spring Data`s CrudRepository, no additional implementation needed on the repository level:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface UserRepository extends CrudRepository&amp;lt;User, Long&amp;gt; {

    Optional&amp;lt;User&amp;gt; findByToken(String token);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Encryptor interface is created in case if we will want to change encryption implementation:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface Encryptor {

    String encrypt(String plainText);

    String decrypt(String encryptedString);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Standard Java encryption capabilities are used with AES encryption for the implementer:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component
public class AesEncryptor implements Encryptor {
    private static final Logger LOGGER = 
LoggerFactory.getLogger(AesEncryptor.class);
    private static final String TRANSFORMATION = 
"AES/ECB/PKCS5Padding";
    private static final String UTF_8 = "UTF-8";

    @Autowired
    private SecretKeySpec secretKey;

    @Override
    public String encrypt(String plainText) {
        LOGGER.info("Encrypted field {}", plainText);

        try {
            Cipher cipher = 
Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            Base64.Encoder encoder = Base64.getEncoder();
            byte[] stringBytes = plainText.getBytes(UTF_8);
            return 
 encoder.encodeToString(cipher.doFinal(stringBytes));
        } catch (Exception e) {
            throw new RuntimeException("Could not handle the encryption");
        }
    }

    @Override
    public String decrypt(String encryptedString) {
        LOGGER.info("Decrypted field {}", encryptedString);
        try {
            Cipher cipher = 
Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            Base64.Decoder decoder = Base64.getDecoder();
            return new 
String(cipher.doFinal(decoder.decode(encryptedString)));
        } catch (Exception e) {
            throw new RuntimeException("Could not handle the decryption", e); 
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And the last piece here is the secret key that is used for the encryption/decryption process. Here is the configuration class which includes secret key bean creation:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class ApplicationConfiguration {

    @Value("${encryption.key}")
    private String encryptionKey;

    @Bean
    public SecretKeySpec secretKey() throws 
UnsupportedEncodingException, NoSuchAlgorithmException {
        byte[] keyBytes = encryptionKey.getBytes("UTF-8");
        MessageDigest sha = MessageDigest.getInstance("SHA-1");
        keyBytes = sha.digest(keyBytes);
        keyBytes = Arrays.copyOf(keyBytes, 16);
        return new SecretKeySpec(keyBytes, "AES");
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The full source code is available in &lt;a href="https://github.com/baseblocks/bb-user-service"&gt;GitHub repo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>security</category>
    </item>
  </channel>
</rss>
