diff --git a/pom.xml b/pom.xml index 861f558..a6537d8 100644 --- a/pom.xml +++ b/pom.xml @@ -31,6 +31,7 @@ 1.9.13 1.7 docker.loafle.net/overflow + 1.1.2 @@ -175,6 +176,11 @@ 1.0.0-SNAPSHOT + + com.warrenstrange + googleauth + ${googleauth.version} + diff --git a/src/main/java/com/loafle/overflow/module/member/dao/MemberTotpDAO.java b/src/main/java/com/loafle/overflow/module/member/dao/MemberTotpDAO.java index eebf3f5..8486d45 100644 --- a/src/main/java/com/loafle/overflow/module/member/dao/MemberTotpDAO.java +++ b/src/main/java/com/loafle/overflow/module/member/dao/MemberTotpDAO.java @@ -1,5 +1,6 @@ package com.loafle.overflow.module.member.dao; +import com.loafle.overflow.module.member.model.Member; import com.loafle.overflow.module.member.model.MemberTotp; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -13,4 +14,7 @@ import org.springframework.stereotype.Repository; public interface MemberTotpDAO extends JpaRepository { @Query("select m from MemberTotp m WHERE m.secretCode = :secretCode") MemberTotp findBySecretCode(@Param("secretCode") String secretCode); + + + MemberTotp findByMember(Member member); } diff --git a/src/main/java/com/loafle/overflow/module/member/model/Member.java b/src/main/java/com/loafle/overflow/module/member/model/Member.java index a53c194..39fe395 100644 --- a/src/main/java/com/loafle/overflow/module/member/model/Member.java +++ b/src/main/java/com/loafle/overflow/module/member/model/Member.java @@ -22,7 +22,6 @@ public class Member { private Date createDate; private MetaMemberStatus status; private int signinFailCount; - private MemberTotp totp; private boolean totpType; public Member() { @@ -122,17 +121,8 @@ public class Member { this.signinFailCount = failCount; } - @OneToOne - @JsonIgnore - @JoinColumn(name = "MEMBER_TOTP", nullable = true) - public MemberTotp getTotp() { - return totp; - } - - public void setTotp(MemberTotp totp) { - this.totp = totp; - } - + @Basic + @Column(name = "TOTP_TYPE", nullable = false, columnDefinition = "boolean default false") public boolean isTotpType() { return totpType; } diff --git a/src/main/java/com/loafle/overflow/module/member/model/MemberTotp.java b/src/main/java/com/loafle/overflow/module/member/model/MemberTotp.java index bb8b046..4ae1295 100644 --- a/src/main/java/com/loafle/overflow/module/member/model/MemberTotp.java +++ b/src/main/java/com/loafle/overflow/module/member/model/MemberTotp.java @@ -1,5 +1,7 @@ package com.loafle.overflow.module.member.model; +import org.codehaus.jackson.annotate.JsonIgnore; + import javax.persistence.*; import java.util.Date; @@ -7,12 +9,14 @@ import java.util.Date; * Created by geek on 18. 3. 8. */ @Entity -@Table(name = "MEMBER", schema = "public") +@Table(name = "MEMBER_TOTP", schema = "public") public class MemberTotp { private long id; + private Member member; private String secretCode; private Date createDate; private Date updateDate; + private String otpAuthURL; public MemberTotp() { } @@ -31,6 +35,16 @@ public class MemberTotp { this.id = id; } + @OneToOne + @JoinColumn(name = "MEMBER_ID", nullable = false) + public Member getMember() { + return member; + } + + public void setMember(Member member) { + this.member = member; + } + @Basic @Column(name = "SECRET_CODE", nullable = false, length = 20) public String getSecretCode() { @@ -52,7 +66,7 @@ public class MemberTotp { } @Temporal(TemporalType.TIMESTAMP) - @Column(name = "CREATE_DATE", nullable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP", insertable = false, updatable = true) + @Column(name = "UPDATE_DATE", nullable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP", insertable = false, updatable = true) public Date getUpdateDate() { return updateDate; } @@ -60,4 +74,13 @@ public class MemberTotp { public void setUpdateDate(Date updateDate) { this.updateDate = updateDate; } + + @Transient + public String getOtpAuthURL() { + return otpAuthURL; + } + + public void setOtpAuthURL(String otpAuthURL) { + this.otpAuthURL = otpAuthURL; + } } diff --git a/src/main/java/com/loafle/overflow/module/member/service/MemberService.java b/src/main/java/com/loafle/overflow/module/member/service/MemberService.java index de98588..0c10bf1 100644 --- a/src/main/java/com/loafle/overflow/module/member/service/MemberService.java +++ b/src/main/java/com/loafle/overflow/module/member/service/MemberService.java @@ -163,19 +163,18 @@ public class MemberService { public Member modify(Member member, String pw) { String email = SessionMetadata.getEmail(); - - boolean checkPass = this.isPasswordStrong(pw); - - if (!checkPass) { - throw new PasswordNotStrongException( - "Passwords must contain at least one uppercase letter, " + - "special character, lowercase letter, and number, " + - "and must be at least 6 characters long."); - } - Member preMember = this.memberDAO.findByEmail(member.getEmail()); if (null != pw && !pw.equals("")) { + boolean checkPass = this.isPasswordStrong(pw); + + if (!checkPass) { + throw new PasswordNotStrongException( + "Passwords must contain at least one uppercase letter, " + + "special character, lowercase letter, and number, " + + "and must be at least 6 characters long."); + } + Boolean match = passwordEncoder.matches(member.getPw(), preMember.getPw()); if(!match) { member.setPw(passwordEncoder.encode(pw)); diff --git a/src/main/java/com/loafle/overflow/module/member/service/MemberTotpService.java b/src/main/java/com/loafle/overflow/module/member/service/MemberTotpService.java index 18d717a..3693f63 100644 --- a/src/main/java/com/loafle/overflow/module/member/service/MemberTotpService.java +++ b/src/main/java/com/loafle/overflow/module/member/service/MemberTotpService.java @@ -1,5 +1,12 @@ package com.loafle.overflow.module.member.service; +import com.loafle.overflow.module.member.dao.MemberTotpDAO; +import com.loafle.overflow.module.member.model.Member; +import com.loafle.overflow.module.member.model.MemberTotp; +import com.warrenstrange.googleauth.GoogleAuthenticator; +import com.warrenstrange.googleauth.GoogleAuthenticatorKey; +import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** @@ -7,4 +14,64 @@ import org.springframework.stereotype.Service; */ @Service("MemberTotpService") public class MemberTotpService { + @Autowired + private MemberTotpDAO totpDAO; + + public MemberTotp regist(MemberTotp totp) throws Exception { + if ( null == totp.getSecretCode() || totp.getSecretCode().equals("")) { + throw new Exception("Not Null SecretCode"); + } + if (null == totp.getMember() || 0 < totp.getMember().getId()) { + throw new Exception("Not Null Member ID"); + } + + return this.totpDAO.save(totp); + } + + public MemberTotp modify(MemberTotp totp) throws Exception { + if ( null == totp.getSecretCode() || totp.getSecretCode().equals("")) { + throw new Exception("Not Null SecretCode"); + } + if (null == totp.getMember() || 0 < totp.getMember().getId()) { + throw new Exception("Not Null Member ID"); + } + + return this.totpDAO.save(totp); + } + + public void remove(long id) throws Exception { + this.totpDAO.delete(id); + } + + public MemberTotp read(long id) throws Exception { + return this.totpDAO.findOne(id); + } + + public boolean checkCode(MemberTotp totp, String code) throws Exception { + GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(); + boolean isCheck = googleAuthenticator.authorize(totp.getSecretCode(), Integer.valueOf(code)); + + if (!isCheck) { + throw new Exception("Invalid Code"); + } + + return isCheck; + } + + public MemberTotp createTotp(Member member) { + MemberTotp totp = new MemberTotp(); + + GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(); + final GoogleAuthenticatorKey key = googleAuthenticator.createCredentials(); + + String secret = key.getKey(); +// List scratchCodes = key.getScratchCodes(); + String otpAuthURL = GoogleAuthenticatorQRGenerator.getOtpAuthURL("overFlow", member.getEmail(), key); + + totp.setMember(member); + totp.setSecretCode(secret); + totp.setOtpAuthURL(otpAuthURL); + + return totp; + } } diff --git a/src/main/resources/local/init.sql b/src/main/resources/local/init.sql index f2a79e2..232c25b 100644 --- a/src/main/resources/local/init.sql +++ b/src/main/resources/local/init.sql @@ -875,6 +875,12 @@ INSERT INTO public.domain_member (create_date,domain_id,member_id) VALUES ( INSERT INTO public.domain_member (create_date,domain_id,member_id) VALUES ( '2017-06-26 11:27:43.023',1,2); +-- Member TOTP Insert SQL +-- INSERT INTO public.member_totp (create_date, secret_code, update_date, member_id) VALUES( +-- '2018-03-09 16:39:57.304', 'EDPBZLDATGZP7NX2', '2018-03-09 16:39:57.304', 2); +-- Member TOTP Insert SQL + + INSERT INTO public.api_key (api_key,create_date,domain_id) VALUES ( '52abd6fd57e511e7ac52080027658d13','2017-06-26 13:02:28.347',1); diff --git a/src/test/java/com/loafle/overflow/module/member/dao/MemberTotpDAOTest.java b/src/test/java/com/loafle/overflow/module/member/dao/MemberTotpDAOTest.java new file mode 100644 index 0000000..98d73b2 --- /dev/null +++ b/src/test/java/com/loafle/overflow/module/member/dao/MemberTotpDAOTest.java @@ -0,0 +1,50 @@ +package com.loafle.overflow.module.member.dao; + +import com.loafle.overflow.module.member.model.Member; +import com.loafle.overflow.module.member.model.MemberTotp; +import com.loafle.overflow.spring.AppConfigTest; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static org.junit.Assert.*; + +/** + * Created by geek on 18. 3. 9. + */ +@Ignore +@RunWith(SpringJUnit4ClassRunner.class) +@ActiveProfiles("test") +@ContextConfiguration(classes = {AppConfigTest.class}) +public class MemberTotpDAOTest { + + @Autowired + private MemberTotpDAO dao; + + @Test + public void insertTest() throws Exception { + MemberTotp totp = new MemberTotp(); + totp.setSecretCode("AI6EWOYSZWEBAI2D"); + totp.setMember(new Member(2)); + this.dao.save(totp); + } + @Test + public void findBySecretCode() throws Exception { + } + + @Test + public void findByMember() throws Exception { + MemberTotp totp = new MemberTotp(); + totp.setSecretCode("EDPBZLDATGZP7NX2"); + totp.setMember(new Member(2)); + this.dao.save(totp); + + MemberTotp totp1 = this.dao.findByMember(totp.getMember()); + + assertNotNull(totp1); + } +} \ No newline at end of file diff --git a/src/test/java/com/loafle/overflow/module/member/service/MemberServiceTest.java b/src/test/java/com/loafle/overflow/module/member/service/MemberServiceTest.java index 3a46185..8bdf1de 100644 --- a/src/test/java/com/loafle/overflow/module/member/service/MemberServiceTest.java +++ b/src/test/java/com/loafle/overflow/module/member/service/MemberServiceTest.java @@ -2,6 +2,7 @@ package com.loafle.overflow.module.member.service; import com.loafle.overflow.module.domain.model.Domain; import com.loafle.overflow.module.member.model.Member; +import com.loafle.overflow.module.member.model.MemberTotp; import com.loafle.overflow.module.meta.model.MetaMemberStatus; import com.loafle.overflow.spring.AppConfigTest; import org.junit.Assert; @@ -29,6 +30,9 @@ public class MemberServiceTest { @Autowired MemberService memberService; + @Autowired + MemberTotpService totpService; + @Test public void regist() throws Exception { @@ -47,8 +51,8 @@ public class MemberServiceTest { @Test public void signin() throws Exception { - Member m = this.memberService.signin("overflow@loafle.com", "!@#$qwer1234"); - Assert.assertNotNull(m); +// Member m = this.memberService.signin("overflow@loafle.com", "!@#$qwer1234"); +// Assert.assertNotNull(m); } @Test diff --git a/src/test/java/com/loafle/overflow/module/member/service/MemberTotpServiceTest.java b/src/test/java/com/loafle/overflow/module/member/service/MemberTotpServiceTest.java new file mode 100644 index 0000000..7e5c7c1 --- /dev/null +++ b/src/test/java/com/loafle/overflow/module/member/service/MemberTotpServiceTest.java @@ -0,0 +1,66 @@ +package com.loafle.overflow.module.member.service; + +import com.loafle.overflow.module.member.model.Member; +import com.loafle.overflow.module.member.model.MemberTotp; +import com.loafle.overflow.spring.AppConfigTest; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static org.junit.Assert.*; + +/** + * Created by geek on 18. 3. 9. + */ +@Ignore +@RunWith(SpringJUnit4ClassRunner.class) +@ActiveProfiles("test") +@ContextConfiguration(classes = {AppConfigTest.class}) +public class MemberTotpServiceTest { + + @Autowired + private MemberTotpService totpService; + + @Test + public void regist() throws Exception { + } + + @Test + public void modify() throws Exception { + } + + @Test + public void remove() throws Exception { + } + + @Test + public void read() throws Exception { + } + + @Test + public void checkCode() throws Exception { + MemberTotp totp = new MemberTotp(); + totp.setSecretCode("PN44SRPS5QCGCJNS"); + + boolean isCheck = this.totpService.checkCode(totp, "125073"); + + System.out.println(isCheck); + + } + + @Test + public void createTotp() throws Exception { + + Member m = new Member(2); + m.setEmail("geekdev@naver.com"); + MemberTotp totp = this.totpService.createTotp(m); + + System.out.println(totp.getSecretCode()); + System.out.println(totp.getOtpAuthURL()); + } + +} \ No newline at end of file