Skip to content
This repository was archived by the owner on Feb 4, 2021. It is now read-only.

Commit e7f4736

Browse files
committed
Impl UpdatePassword
1 parent 0560fa1 commit e7f4736

File tree

8 files changed

+119
-20
lines changed

8 files changed

+119
-20
lines changed

api/protos/password_resets.proto

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ message CreatePasswordResetRequest {
4141

4242
message UpdatePasswordRequest {
4343
string token = 1;
44+
// updateの時はemailはquery parameterじゃなくてrequest bodyに入れて欲しい
4445
string email = 2;
4546
string new_password = 3;
4647
}

app/server/password_resets_server.go

+36-6
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import (
77
"github.com/golang/protobuf/ptypes/empty"
88
"github.com/izumin5210/grapi/pkg/grapiserver"
99
"github.com/pkg/errors"
10-
"google.golang.org/grpc/codes"
11-
"google.golang.org/grpc/status"
1210

1311
api_pb "github.com/ProgrammingLab/prolab-accounts/api"
1412
"github.com/ProgrammingLab/prolab-accounts/app/config"
@@ -64,13 +62,13 @@ func (s *passwordResetServiceServerImpl) CreatePasswordReset(ctx context.Context
6462
}
6563

6664
ps := s.PasswordResetStore(ctx)
67-
p, token, err := ps.CreateConfirmation(model.UserID(u.ID), email)
65+
r, token, err := ps.CreateConfirmation(model.UserID(u.ID), email)
6866
if err != nil {
6967
return nil, err
7068
}
7169

7270
sender := s.EmailSender(ctx)
73-
err = sender.SendPasswordReset(p, token)
71+
err = sender.SendPasswordReset(r, token)
7472
if err != nil {
7573
return nil, err
7674
}
@@ -79,6 +77,38 @@ func (s *passwordResetServiceServerImpl) CreatePasswordReset(ctx context.Context
7977
}
8078

8179
func (s *passwordResetServiceServerImpl) UpdatePassword(ctx context.Context, req *api_pb.UpdatePasswordRequest) (*api_pb.Session, error) {
82-
// TODO: Not yet implemented.
83-
return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!")
80+
password := req.GetNewPassword()
81+
if len(password) < MinPasswordLength || MaxPasswordLength < len(password) {
82+
return nil, ErrOutOfRangePasswordLength
83+
}
84+
85+
ps := s.PasswordResetStore(ctx)
86+
r, err := ps.GetConfirmation(req.GetEmail(), req.GetToken())
87+
if err != nil {
88+
if errors.Cause(err) == sql.ErrNoRows {
89+
return nil, util.ErrNotFound
90+
}
91+
return nil, err
92+
}
93+
94+
err = ps.UpdatePassword(r, password)
95+
if err != nil {
96+
return nil, err
97+
}
98+
99+
ss := s.SessionStore(ctx)
100+
session, err := ss.ResetSession(model.UserID(r.R.User.ID))
101+
if err != nil {
102+
return nil, err
103+
}
104+
105+
sender := s.EmailSender(ctx)
106+
err = sender.SendPasswordChanged(r.R.User)
107+
if err != nil {
108+
return nil, err
109+
}
110+
111+
return &api_pb.Session{
112+
SessionId: session.ID,
113+
}, nil
84114
}

app/server/users_server.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ const (
4444
MaxIconSize = 1024 * 1024 // 1MiB
4545
// MaxPasswordLength represents max length of password
4646
MaxPasswordLength = 72
47+
// MinPasswordLength represents min length of password
48+
MinPasswordLength = 6
4749
)
4850

4951
var (
@@ -55,8 +57,8 @@ var (
5557
ErrInvalidImageFormat = status.Error(codes.InvalidArgument, "invalid iamge format")
5658
// ErrNameAlreadyInUse is returned when name is already in use
5759
ErrNameAlreadyInUse = status.Error(codes.AlreadyExists, "name is already in use")
58-
// ErrTooLongPassword will be returned when password is too long
59-
ErrTooLongPassword = status.Error(codes.InvalidArgument, fmt.Sprintf("length of password must be less than %v", MaxPasswordLength+1))
60+
// ErrOutOfRangePasswordLength will be returned when password length is out of range
61+
ErrOutOfRangePasswordLength = status.Error(codes.InvalidArgument, fmt.Sprintf("length of password must be less than %v and more than %v", MaxPasswordLength+1, MinPasswordLength-1))
6062
)
6163

6264
func (s *userServiceServerImpl) ListPublicUsers(ctx context.Context, req *api_pb.ListUsersRequest) (*api_pb.ListUsersResponse, error) {
@@ -172,8 +174,8 @@ func (s *userServiceServerImpl) CreateUser(ctx context.Context, req *api_pb.Crea
172174
}
173175

174176
password := req.GetPassword()
175-
if MaxPasswordLength < len(password) {
176-
return nil, ErrTooLongPassword
177+
if len(password) < MinPasswordLength || MaxPasswordLength < len(password) {
178+
return nil, ErrOutOfRangePasswordLength
177179
}
178180

179181
d, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)

infra/email/email.go

+12
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type Sender interface {
1919
SendEmailConfirmation(conf *record.EmailConfirmation) error
2020
SendEmailChanged(user *record.User, oldEmail string) error
2121
SendPasswordReset(reset *record.PasswordReset, token string) error
22+
SendPasswordChanged(user *record.User) error
2223
}
2324

2425
// NewSender creates new sender
@@ -97,6 +98,17 @@ func (s *senderImpl) SendPasswordReset(reset *record.PasswordReset, token string
9798
return s.send(reset.Email, "パスワードのリセット", "password_reset.tmpl", d)
9899
}
99100

101+
type passwordChangedData struct {
102+
Name string
103+
}
104+
105+
func (s *senderImpl) SendPasswordChanged(user *record.User) error {
106+
d := passwordChangedData{
107+
Name: user.Name,
108+
}
109+
return s.send(user.Email, "パスワードが変更されました", "password_changed.tmpl", d)
110+
}
111+
100112
func (s *senderImpl) send(to, subject, tmplName string, d interface{}) error {
101113
tmpl, err := s.asset.GetTemplate(tmplName)
102114
if err != nil {

infra/store/session/session_store.go

+53-10
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ type sessionStoreImpl struct {
2727
const (
2828
// SessionExpiration is sessions expiration
2929
SessionExpiration = time.Hour * 24 * 7
30+
31+
keyPrefix = "session"
32+
)
33+
34+
var (
35+
errSessionNotFound = fmt.Errorf("session not found")
3036
)
3137

3238
// NewSessionStore returns new session store
@@ -52,22 +58,25 @@ func (s *sessionStoreImpl) CreateSession(nameOrEmail, password string) (*model.S
5258
return nil, errors.WithStack(err)
5359
}
5460

55-
session, err := model.NewSession(model.UserID(u.ID))
56-
if err != nil {
57-
return nil, errors.WithStack(err)
58-
}
59-
60-
key := redisKey(session.ID)
61-
err = s.client.Set(key, strconv.FormatInt(u.ID, 10), SessionExpiration).Err()
61+
session, err := s.setSession(model.UserID(u.ID))
6262
if err != nil {
63-
return nil, errors.Wrap(err, "")
63+
return nil, err
6464
}
6565

6666
return session, nil
6767
}
6868

6969
func (s *sessionStoreImpl) GetSession(sessionID string) (*model.Session, error) {
70-
v, err := s.client.Get(redisKey(sessionID)).Result()
70+
keys, err := s.client.Keys(redisKey(sessionID) + ":*").Result()
71+
if err != nil {
72+
return nil, errors.WithStack(err)
73+
}
74+
75+
if len(keys) == 0 {
76+
return nil, errors.WithStack(errSessionNotFound)
77+
}
78+
79+
v, err := s.client.Get(keys[0]).Result()
7180
if err != nil {
7281
return nil, errors.WithStack(err)
7382
}
@@ -83,6 +92,40 @@ func (s *sessionStoreImpl) GetSession(sessionID string) (*model.Session, error)
8392
}, nil
8493
}
8594

95+
func (s *sessionStoreImpl) ResetSession(userID model.UserID) (*model.Session, error) {
96+
keys, err := s.client.Keys(fmt.Sprintf("%s:*:%v", keyPrefix, userID)).Result()
97+
if err != nil {
98+
return nil, errors.WithStack(err)
99+
}
100+
_, err = s.client.Del(keys...).Result()
101+
if err != nil {
102+
return nil, errors.WithStack(err)
103+
}
104+
105+
session, err := s.setSession(userID)
106+
if err != nil {
107+
return nil, err
108+
}
109+
110+
return session, nil
111+
}
112+
113+
func (s *sessionStoreImpl) setSession(userID model.UserID) (*model.Session, error) {
114+
session, err := model.NewSession(userID)
115+
if err != nil {
116+
return nil, errors.WithStack(err)
117+
}
118+
119+
id := strconv.FormatInt(int64(userID), 10)
120+
key := redisKey(session.ID) + ":" + id
121+
err = s.client.Set(key, id, SessionExpiration).Err()
122+
if err != nil {
123+
return nil, errors.Wrap(err, "")
124+
}
125+
126+
return session, nil
127+
}
128+
86129
func redisKey(sessionID string) string {
87-
return fmt.Sprintf("session:%s", sessionID)
130+
return fmt.Sprintf("%s:%s", keyPrefix, sessionID)
88131
}

infra/store/session_store.go

+1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ import "github.com/ProgrammingLab/prolab-accounts/model"
66
type SessionStore interface {
77
CreateSession(nameOrEmail, password string) (*model.Session, error)
88
GetSession(sessionID string) (*model.Session, error)
9+
ResetSession(userID model.UserID) (*model.Session, error)
910
}

packrd/packed-packr.go

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

static/emails/password_changed.tmpl

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@{{.Name}} さん
2+
3+
プロラボアカウントのパスワードが変更されました。
4+
5+
-----------------------------------------------
6+
久留米高専 プログラミングラボ部
7+
https://kurume-nct.com
8+
-----------------------------------------------

0 commit comments

Comments
 (0)