aboutsummaryrefslogtreecommitdiff
path: root/api
diff options
context:
space:
mode:
authorjloup <jeanloup.jamet@gmail.com>2018-05-04 11:55:15 +0200
committerjloup <jeanloup.jamet@gmail.com>2018-05-04 11:55:15 +0200
commit85545aba62546f219a9c9730945511412a3174ef (patch)
tree7bf7feb99c6e7e51e83c386aafb3da3b7610d6bd /api
parentb94f3416c1afe6363249b46bf2b299dfe8e4007f (diff)
downloadFront-85545aba62546f219a9c9730945511412a3174ef.tar.gz
Front-85545aba62546f219a9c9730945511412a3174ef.tar.zst
Front-85545aba62546f219a9c9730945511412a3174ef.zip
Password reset.
Diffstat (limited to 'api')
-rw-r--r--api/api.go16
-rw-r--r--api/auth_jwt.go4
-rw-r--r--api/free_sms.go26
-rw-r--r--api/password_reset.go103
-rw-r--r--api/routes.go19
-rw-r--r--api/user.go7
6 files changed, 171 insertions, 4 deletions
diff --git a/api/api.go b/api/api.go
index 7b7be49..42b9923 100644
--- a/api/api.go
+++ b/api/api.go
@@ -7,6 +7,22 @@ import (
7 "github.com/gin-gonic/gin" 7 "github.com/gin-gonic/gin"
8) 8)
9 9
10var CONFIG Config
11
12type Config struct {
13 JwtSecret string `toml:"jwt_secret"`
14 PasswordResetSecret string `toml:"password_reset_secret"`
15 FreeSMSUser string `toml:"free_sms_user"`
16 FreeSMSPass string `toml:"free_sms_pass"`
17}
18
19func SetConfig(config Config) {
20 CONFIG = config
21
22 JWT_SECRET = []byte(config.JwtSecret)
23 PASSWORD_RESET_SECRET = []byte(config.PasswordResetSecret)
24}
25
10type Error struct { 26type Error struct {
11 Code ErrorCode 27 Code ErrorCode
12 UserMessage string 28 UserMessage string
diff --git a/api/auth_jwt.go b/api/auth_jwt.go
index 5ce1593..db7e3f4 100644
--- a/api/auth_jwt.go
+++ b/api/auth_jwt.go
@@ -20,10 +20,6 @@ type JwtClaims struct {
20 jwt.StandardClaims 20 jwt.StandardClaims
21} 21}
22 22
23func SetJwtSecretKey(secret string) {
24 JWT_SECRET = []byte(secret)
25}
26
27func VerifyJwtToken(token string) (JwtClaims, error) { 23func VerifyJwtToken(token string) (JwtClaims, error) {
28 if len(JWT_SECRET) == 0 { 24 if len(JWT_SECRET) == 0 {
29 return JwtClaims{}, fmt.Errorf("not initialized jwt secret") 25 return JwtClaims{}, fmt.Errorf("not initialized jwt secret")
diff --git a/api/free_sms.go b/api/free_sms.go
new file mode 100644
index 0000000..f09a1d1
--- /dev/null
+++ b/api/free_sms.go
@@ -0,0 +1,26 @@
1package api
2
3import (
4 "fmt"
5 "net/http"
6 "net/url"
7)
8
9func SendSMS(user, pass, msg string) error {
10 form := url.Values{
11 "user": []string{user},
12 "pass": []string{pass},
13 "msg": []string{msg},
14 }
15
16 response, err := http.Get(fmt.Sprintf("https://smsapi.free-mobile.fr/sendmsg?%s", form.Encode()))
17 if err != nil {
18 return err
19 }
20
21 if response.StatusCode != 200 {
22 return fmt.Errorf("Cannot send sms: status code %v", response.StatusCode)
23 }
24
25 return nil
26}
diff --git a/api/password_reset.go b/api/password_reset.go
new file mode 100644
index 0000000..82aaaef
--- /dev/null
+++ b/api/password_reset.go
@@ -0,0 +1,103 @@
1package api
2
3import (
4 "fmt"
5 "time"
6
7 "github.com/dchest/passwordreset"
8 "immae.eu/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Front/db"
9)
10
11var PASSWORD_RESET_SECRET []byte
12
13type PasswordResetQuery struct {
14 In struct {
15 Email string
16 }
17}
18
19func (q PasswordResetQuery) ValidateParams() *Error {
20 if q.In.Email == "" {
21 return &Error{InvalidEmail, "invalid email", fmt.Errorf("invalid email")}
22 }
23
24 return nil
25}
26
27func (q PasswordResetQuery) Run() (interface{}, *Error) {
28 user, err := db.GetUserByEmail(q.In.Email)
29 if err != nil {
30 return nil, NewInternalError(err)
31 }
32
33 if user == nil {
34 return nil, &Error{NotFound, "account not found", fmt.Errorf("'%v' is not registered", q.In.Email)}
35 }
36
37 token := passwordreset.NewToken(q.In.Email, time.Hour*24*1, []byte(user.PasswordHash), PASSWORD_RESET_SECRET)
38 if CONFIG.FreeSMSUser != "" {
39 err := SendSMS(CONFIG.FreeSMSUser, CONFIG.FreeSMSPass, fmt.Sprintf("'%v' request a password reset. Token '/change-password?token=%v'", q.In.Email, token))
40 if err != nil {
41 return nil, NewInternalError(err)
42 }
43 }
44
45 return "OK", nil
46}
47
48type ChangePasswordQuery struct {
49 In struct {
50 Token string
51 Password string
52 }
53}
54
55func (q ChangePasswordQuery) ValidateParams() *Error {
56 if q.In.Password == "" {
57 return &Error{InvalidPassword, "invalid password", fmt.Errorf("invalid password")}
58 }
59
60 if q.In.Token == "" {
61 return &Error{BadRequest, "invalid token", fmt.Errorf("invalid token")}
62 }
63
64 return nil
65}
66
67func (q ChangePasswordQuery) Run() (interface{}, *Error) {
68 var user *db.User
69
70 email, err := passwordreset.VerifyToken(q.In.Token, func(email string) ([]byte, error) {
71 var err error
72 user, err = db.GetUserByEmail(email)
73 if err != nil {
74 return nil, err
75 }
76
77 if user == nil {
78 return nil, fmt.Errorf("'%v' is not registered", email)
79 }
80
81 return []byte(user.PasswordHash), nil
82
83 }, PASSWORD_RESET_SECRET)
84
85 if err != nil && (err == passwordreset.ErrExpiredToken) {
86 return nil, &Error{BadRequest, "expired token", fmt.Errorf("expired token")}
87 } else if err != nil && (err == passwordreset.ErrMalformedToken || err == passwordreset.ErrWrongSignature) {
88 return nil, &Error{BadRequest, "wrong token", fmt.Errorf("wrong token")}
89 } else if err != nil {
90 return nil, NewInternalError(err)
91 }
92
93 if user == nil {
94 return nil, &Error{BadRequest, "bad request", fmt.Errorf("no user found for email '%v'", email)}
95 }
96
97 err = db.SetPassword(user, q.In.Password)
98 if err != nil {
99 return nil, NewInternalError(err)
100 }
101
102 return "OK", nil
103}
diff --git a/api/routes.go b/api/routes.go
index cdf3dd9..22af0e7 100644
--- a/api/routes.go
+++ b/api/routes.go
@@ -25,6 +25,8 @@ var Groups = []Group{
25 []Route{ 25 []Route{
26 {"POST", []gin.HandlerFunc{Signup}, "/signup"}, 26 {"POST", []gin.HandlerFunc{Signup}, "/signup"},
27 {"POST", []gin.HandlerFunc{Signin}, "/signin"}, 27 {"POST", []gin.HandlerFunc{Signin}, "/signin"},
28 {"POST", []gin.HandlerFunc{PasswordReset}, "/passwordreset"},
29 {"POST", []gin.HandlerFunc{ChangePassword}, "/changepassword"},
28 }, 30 },
29 }, 31 },
30 { 32 {
@@ -132,3 +134,20 @@ func UpdateMarketConfig(c *gin.Context) {
132 134
133 RunQuery(query, c) 135 RunQuery(query, c)
134} 136}
137
138func PasswordReset(c *gin.Context) {
139 query := &PasswordResetQuery{}
140
141 query.In.Email = c.PostForm("email")
142
143 RunQuery(query, c)
144}
145
146func ChangePassword(c *gin.Context) {
147 query := &ChangePasswordQuery{}
148
149 query.In.Token = c.PostForm("token")
150 query.In.Password = c.PostForm("password")
151
152 RunQuery(query, c)
153}
diff --git a/api/user.go b/api/user.go
index 1dc69e4..9fd9479 100644
--- a/api/user.go
+++ b/api/user.go
@@ -74,6 +74,13 @@ func (q SignupQuery) Run() (interface{}, *Error) {
74 return nil, NewInternalError(fmt.Errorf("cannot create jwt token %v", err)) 74 return nil, NewInternalError(fmt.Errorf("cannot create jwt token %v", err))
75 } 75 }
76 76
77 if CONFIG.FreeSMSUser != "" {
78 err := SendSMS(CONFIG.FreeSMSUser, CONFIG.FreeSMSPass, fmt.Sprintf("New user signup '%v'", q.In.Email))
79 if err != nil {
80 return nil, NewInternalError(err)
81 }
82 }
83
77 return SignResult{token}, nil 84 return SignResult{token}, nil
78} 85}
79 86