]>
Commit | Line | Data |
---|---|---|
7a9e5112 | 1 | package api |
2 | ||
3 | import ( | |
4 | "bytes" | |
5 | "fmt" | |
6 | "image/png" | |
7 | ||
8 | "github.com/gin-gonic/gin" | |
9 | "github.com/pquerna/otp/totp" | |
10 | "immae.eu/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Front/db" | |
11 | ) | |
12 | ||
13 | func GenerateSecret(user db.User) (bytes.Buffer, string, error) { | |
14 | var buf bytes.Buffer | |
15 | key, err := totp.Generate(totp.GenerateOpts{ | |
16 | Issuer: "cryptoportfolio", | |
17 | AccountName: user.Email, | |
18 | Period: 30, | |
19 | }) | |
20 | ||
21 | if err != nil { | |
22 | return buf, "", err | |
23 | } | |
24 | ||
25 | qrImage, err := key.Image(200, 200) | |
26 | if err != nil { | |
27 | return buf, "", err | |
28 | } | |
29 | ||
30 | png.Encode(&buf, qrImage) | |
31 | if err != nil { | |
32 | return buf, "", err | |
33 | } | |
34 | ||
35 | return buf, key.Secret(), nil | |
36 | } | |
37 | ||
38 | type OtpEnrollmentQuery struct { | |
39 | In struct { | |
40 | User db.User | |
41 | } | |
42 | } | |
43 | ||
44 | func (q OtpEnrollmentQuery) ValidateParams() *Error { | |
45 | return nil | |
46 | } | |
47 | ||
48 | func (q OtpEnrollmentQuery) Run() (*bytes.Buffer, string, *Error) { | |
49 | if q.In.User.OtpSecret != "" && q.In.User.IsOtpSetup == true { | |
50 | return nil, "", &Error{OtpAlreadySetup, "otp is already setup", fmt.Errorf("otp already setup")} | |
51 | } | |
52 | ||
53 | buf, secret, err := GenerateSecret(q.In.User) | |
54 | if err != nil { | |
55 | return nil, "", NewInternalError(err) | |
56 | } | |
57 | ||
58 | err = db.SetOtpSecret(&q.In.User, secret, true) | |
59 | if err != nil { | |
60 | return nil, "", NewInternalError(err) | |
61 | } | |
62 | ||
63 | return &buf, secret, nil | |
64 | } | |
65 | ||
66 | type OtpValidateQuery struct { | |
67 | In struct { | |
68 | Pass string | |
69 | User db.User | |
70 | Claims JwtClaims | |
71 | } | |
72 | ||
73 | Out struct { | |
74 | Token string `json:"token"` | |
75 | } | |
76 | } | |
77 | ||
78 | func (q OtpValidateQuery) ValidateParams() *Error { | |
79 | if q.In.Pass == "" { | |
80 | return &Error{InvalidOtp, "invalid otp", fmt.Errorf("invalid otp '%v'", q.In.Pass)} | |
81 | } | |
82 | ||
83 | return nil | |
84 | } | |
85 | ||
86 | func (q OtpValidateQuery) Run() (interface{}, *Error) { | |
87 | var err error | |
88 | ||
89 | if q.In.User.OtpSecret == "" { | |
90 | return nil, &Error{OtpNotSetup, "otp is not setup", fmt.Errorf("otp is not setup")} | |
91 | } | |
92 | ||
93 | if !totp.Validate(q.In.Pass, q.In.User.OtpSecret) { | |
94 | return nil, &Error{InvalidOtp, "invalid otp", fmt.Errorf("invalid otp '%v'", q.In.Pass)} | |
95 | ||
96 | } else if err := db.SetOtpSecret(&q.In.User, q.In.User.OtpSecret, false); err != nil { | |
97 | return nil, NewInternalError(err) | |
98 | } | |
99 | ||
100 | q.In.Claims.Authorized = true | |
101 | q.Out.Token, err = SignJwt(q.In.Claims) | |
102 | if err != nil { | |
103 | return nil, NewInternalError(err) | |
104 | } | |
105 | ||
106 | return q.Out, nil | |
107 | } | |
108 | ||
109 | func OtpAuth(c *gin.Context) *Error { | |
110 | claims := GetClaims(c) | |
111 | user := GetUser(c) | |
112 | ||
113 | if user.IsOtpSetup == false || user.OtpSecret == "" { | |
114 | return &Error{OtpNotSetup, "otp is not setup", fmt.Errorf("otp is not setup")} | |
115 | } | |
116 | ||
117 | if !claims.Authorized { | |
118 | return &Error{NeedOtpValidation, "not authorized", fmt.Errorf("otp not authorized")} | |
119 | } | |
120 | ||
121 | return nil | |
122 | } |