Skip to content

Commit 1b43a7e

Browse files
authored
Merge pull request #33 from yodamad/dev
1.6
2 parents eeaecce + fdfc6dd commit 1b43a7e

22 files changed

+491
-124
lines changed

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>fr.yodamad.svn2git</groupId>
66
<artifactId>svn-2-git</artifactId>
7-
<version>1.5</version>
7+
<version>1.6</version>
88
<packaging>war</packaging>
99
<name>Svn 2 GitLab</name>
1010

src/main/java/fr/yodamad/svn2git/domain/Migration.java

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package fr.yodamad.svn2git.domain;
22

33
import fr.yodamad.svn2git.domain.enumeration.StatusEnum;
4+
import fr.yodamad.svn2git.security.PasswordConverter;
45

56
import javax.persistence.*;
67
import javax.validation.constraints.NotNull;
@@ -60,6 +61,7 @@ public class Migration implements Serializable {
6061
private String gitlabUrl;
6162

6263
@Column(name = "gitlab_token")
64+
@Convert(converter = PasswordConverter.class)
6365
private String gitlabToken;
6466

6567
@Column(name = "svn_url")
@@ -69,6 +71,7 @@ public class Migration implements Serializable {
6971
private String svnUser;
7072

7173
@Column(name = "svn_password")
74+
@Convert(converter = PasswordConverter.class)
7275
private String svnPassword;
7376

7477
@OneToMany(mappedBy = "migration")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package fr.yodamad.svn2git.security;
2+
3+
import javax.crypto.Cipher;
4+
import javax.crypto.NoSuchPaddingException;
5+
import javax.crypto.spec.IvParameterSpec;
6+
import javax.crypto.spec.SecretKeySpec;
7+
import java.security.*;
8+
import java.security.spec.AlgorithmParameterSpec;
9+
10+
/**
11+
* Utility class to generate an instance of {@link javax.crypto.Cipher}
12+
*
13+
* @author Sunit Katkar, sunitkatkar@gmail.com
14+
* @since ver 1.0 (Apr 2018)
15+
* @version 1.0
16+
*/
17+
public class CipherMaker {
18+
19+
private static final String CIPHER_INSTANCE_NAME = "AES/CBC/PKCS5Padding";
20+
private static final String SECRET_KEY_ALGORITHM = "AES";
21+
22+
/**
23+
* @param encryptionMode
24+
* - decides whether to ecrypt or decrypt data. Values accepted:
25+
* {@link Cipher#ENCRYPT_MODE} for encryption and
26+
* {@link Cipher#DECRYPT_MODE} for decryption.
27+
* @param key
28+
* - the key to use for encrypting or decrypting data. This can be a
29+
* simple String like "MySecretKey" or a more complex, hard to guess
30+
* longer string
31+
* @return
32+
* @throws NoSuchPaddingException
33+
* @throws NoSuchAlgorithmException
34+
*/
35+
public Cipher configureAndGetInstance(int encryptionMode, String key)
36+
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException {
37+
38+
Cipher cipher = Cipher.getInstance(CIPHER_INSTANCE_NAME);
39+
byte[] bytesOfMessage = key.getBytes();
40+
MessageDigest md = MessageDigest.getInstance("SHA-256");
41+
byte[] b = md.digest(bytesOfMessage);
42+
Key secretKey = new SecretKeySpec(b, SECRET_KEY_ALGORITHM);
43+
44+
byte[] ivBytes = new byte[cipher.getBlockSize()];
45+
AlgorithmParameterSpec algorithmParameters = new IvParameterSpec(ivBytes);
46+
47+
cipher.init(encryptionMode, secretKey, algorithmParameters);
48+
return cipher;
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package fr.yodamad.svn2git.security;
2+
3+
import org.apache.commons.lang3.StringUtils;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.beans.factory.annotation.Configurable;
6+
import org.springframework.core.env.Environment;
7+
import org.springframework.stereotype.Component;
8+
9+
import javax.crypto.BadPaddingException;
10+
import javax.crypto.Cipher;
11+
import javax.crypto.IllegalBlockSizeException;
12+
import javax.crypto.NoSuchPaddingException;
13+
import javax.persistence.AttributeConverter;
14+
import javax.persistence.Converter;
15+
import java.security.InvalidAlgorithmParameterException;
16+
import java.security.InvalidKeyException;
17+
import java.security.NoSuchAlgorithmException;
18+
import java.util.Base64;
19+
20+
/**
21+
* Concrete implementation of the {@link AttributeConverter}
22+
* abstract class to encrypt/decrypt an entity attribute of type
23+
* {@link java.lang.String} <br/>
24+
* Note: This is the class where the {@literal @}Converter annotation is applied
25+
*
26+
* @author Sunit Katkar, sunitkatkar@gmail.com
27+
* @since ver 1.0 (Apr 2018)
28+
* @version 1.0 *
29+
*/
30+
@Component
31+
@Configurable
32+
@Converter(autoApply = false)
33+
public class PasswordConverter implements AttributeConverter<String, String> {
34+
35+
/** Spring env to retrieve secret. */
36+
private static Environment env;
37+
38+
@Autowired
39+
public void initEnv(Environment en){
40+
PasswordConverter.env = en;
41+
}
42+
43+
/** CipherMaker is needed to configure and create instance of Cipher */
44+
private CipherMaker cipherMaker;
45+
46+
/**
47+
* Default constructor initializes with an instance of the
48+
* {@link CipherMaker} crypto class to get a {@link javax.crypto.Cipher}
49+
* instance
50+
*/
51+
public PasswordConverter() {
52+
this(new CipherMaker());
53+
}
54+
55+
/**
56+
* Constructor
57+
*
58+
* @param cipherMaker
59+
*/
60+
public PasswordConverter(CipherMaker cipherMaker) {
61+
this.cipherMaker = cipherMaker;
62+
}
63+
64+
/*
65+
* (non-Javadoc)
66+
*
67+
* @see
68+
* javax.persistence.AttributeConverter#convertToDatabaseColumn(java.lang.
69+
* Object)
70+
*/
71+
@Override
72+
public String convertToDatabaseColumn(String attribute) {
73+
String secret = env.getProperty("password.cipher");
74+
if (!StringUtils.isEmpty(secret) && !StringUtils.isEmpty(attribute)) {
75+
try {
76+
Cipher cipher = cipherMaker.configureAndGetInstance(Cipher.ENCRYPT_MODE, secret);
77+
return encryptData(cipher, attribute);
78+
} catch (NoSuchAlgorithmException
79+
| InvalidKeyException
80+
| InvalidAlgorithmParameterException
81+
| BadPaddingException
82+
| NoSuchPaddingException
83+
| IllegalBlockSizeException e) {
84+
throw new RuntimeException(e);
85+
}
86+
}
87+
return convertEntityAttributeToString(attribute);
88+
}
89+
90+
/*
91+
* (non-Javadoc)
92+
*
93+
* @see
94+
* javax.persistence.AttributeConverter#convertToEntityAttribute(java.lang.
95+
* Object)
96+
*/
97+
@Override
98+
public String convertToEntityAttribute(String dbData) {
99+
String secret = env.getProperty("password.cipher");
100+
if (!StringUtils.isEmpty(secret) && !StringUtils.isEmpty(dbData)) {
101+
try {
102+
Cipher cipher = cipherMaker.configureAndGetInstance(Cipher.DECRYPT_MODE, secret);
103+
return decryptData(cipher, dbData);
104+
} catch (NoSuchAlgorithmException
105+
| InvalidAlgorithmParameterException
106+
| InvalidKeyException
107+
| BadPaddingException
108+
| NoSuchPaddingException
109+
| IllegalBlockSizeException e) {
110+
throw new RuntimeException(e);
111+
}
112+
}
113+
return convertStringToEntityAttribute(dbData);
114+
}
115+
116+
private String convertStringToEntityAttribute(String dbData) {
117+
// the input is a string and output is a string
118+
return dbData;
119+
}
120+
121+
private String convertEntityAttributeToString(String attribute) {
122+
// Here too the input is a string and output is a string
123+
return attribute;
124+
}
125+
126+
/**
127+
* Helper method to encrypt data
128+
*
129+
* @param cipher
130+
* @param attribute
131+
* @return
132+
* @throws IllegalBlockSizeException
133+
* @throws BadPaddingException
134+
*/
135+
private String encryptData(Cipher cipher, String attribute)
136+
throws IllegalBlockSizeException, BadPaddingException {
137+
byte[] bytesToEncrypt = convertEntityAttributeToString(attribute).getBytes();
138+
byte[] encryptedBytes = cipher.doFinal(bytesToEncrypt);
139+
return Base64.getEncoder().encodeToString(encryptedBytes);
140+
}
141+
142+
/**
143+
* Helper method to decrypt data
144+
*
145+
* @param cipher
146+
* @param dbData
147+
* @return
148+
* @throws IllegalBlockSizeException
149+
* @throws BadPaddingException
150+
*/
151+
private String decryptData(Cipher cipher, String dbData)
152+
throws IllegalBlockSizeException, BadPaddingException {
153+
byte[] bytesToDecrypt = Base64.getDecoder().decode(dbData);
154+
byte[] decryptedBytes = cipher.doFinal(bytesToDecrypt);
155+
return convertStringToEntityAttribute(new String(decryptedBytes));
156+
}
157+
}

src/main/java/fr/yodamad/svn2git/service/MigrationChecker.java

+20-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import fr.yodamad.svn2git.domain.enumeration.StatusEnum;
44
import fr.yodamad.svn2git.repository.MigrationRepository;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
57
import org.springframework.context.annotation.DependsOn;
68
import org.springframework.context.annotation.Lazy;
79
import org.springframework.stereotype.Service;
@@ -16,6 +18,8 @@
1618
@Lazy(false)
1719
public class MigrationChecker {
1820

21+
private static final Logger LOG = LoggerFactory.getLogger(MigrationChecker.class);
22+
1923
/** Migration manager. */
2024
private final MigrationManager manager;
2125
/** Migration repo. */
@@ -34,12 +38,21 @@ public MigrationChecker(MigrationManager migrationManager,
3438
@PostConstruct
3539
@DependsOn("asyncConfiguration")
3640
public void checkDb() {
37-
// Start waiting migrations
38-
repository.findAllByStatusOrderByDateDesc(StatusEnum.WAITING).stream().forEach(mig -> manager.startMigration(mig.getId()));
39-
// Fail running migrations
40-
repository.findAllByStatusOrderByDateDesc(StatusEnum.RUNNING).stream().forEach(mig -> {
41-
mig.status(StatusEnum.FAILED);
42-
repository.save(mig);
43-
});
41+
try {
42+
// Start waiting migrations
43+
repository.findAllByStatusOrderByDateDesc(StatusEnum.WAITING).stream().forEach(mig -> manager.startMigration(mig.getId()));
44+
// Fail running migrations
45+
repository.findAllByStatusOrderByDateDesc(StatusEnum.RUNNING).stream().forEach(mig -> {
46+
mig.status(StatusEnum.FAILED);
47+
repository.save(mig);
48+
});
49+
} catch (Exception exc) {
50+
LOG.error("Failed to check migration on startup", exc);
51+
// Fail running migrations
52+
repository.findAllByStatusOrderByDateDesc(StatusEnum.RUNNING).stream().forEach(mig -> {
53+
mig.status(StatusEnum.FAILED);
54+
repository.save(mig);
55+
});
56+
}
4457
}
4558
}

src/main/resources/.h2.server.properties

-6
This file was deleted.

src/main/resources/config/application.yml

+2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ svn:
112112
user:
113113
password:
114114

115+
password.cipher: svn2git
116+
115117
# ===================================================================
116118
# Application specific properties
117119
# Add your own application properties here, see the ApplicationProperties class

src/main/webapp/app/migration/migration-check.component.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
<div>
33
<span jhiTranslate="global.search-by" style="margin-right: 20px">SearchBy</span>
44
<form [formGroup]="searchFormGroup">
5-
<mat-form-field><input matInput type="text" placeholder="user login" formControlName="userCriteria" /></mat-form-field>
5+
<mat-form-field><input matInput type="text" placeholder="{{'svn2GitApp.migration.login' | translate}}" formControlName="userCriteria" /></mat-form-field>
66
<span jhiTranslate="global.or" style="margin: 0 20px">Or</span>
7-
<mat-form-field><input matInput type="text" placeholder="project group name" formControlName="groupCriteria"/></mat-form-field>
7+
<mat-form-field><input matInput type="text" placeholder="{{'svn2GitApp.migration.svnGroup' | translate}}" formControlName="groupCriteria"/></mat-form-field>
88
<button mat-raised-button type="submit" color="primary" style="margin-left: 20px" (click)="search()"><span jhiTranslate="global.search">Search</span></button>
99
</form>
1010
<mat-divider></mat-divider>

0 commit comments

Comments
 (0)