Przeglądaj źródła

Merge branch 'pw-table' of fyindr/siimee into dev

tags/0.0.1^2
maeda 3 lat temu
rodzic
commit
2d8953af21

+ 1
- 4
backend/.eslintrc Wyświetl plik

@@ -11,10 +11,7 @@
11 11
         "ecmaVersion": 12
12 12
     },
13 13
     "rules": {
14
-        "indent": [
15
-            "error",
16
-            4
17
-        ],
14
+        "indent": ["error", 4, { "SwitchCase": 1 }],
18 15
         "linebreak-style": [
19 16
             "error",
20 17
             "unix"

+ 12
- 0
backend/db/migrations/20220901171733_user_authentication.js Wyświetl plik

@@ -0,0 +1,12 @@
1
+exports.up = function (knex) {
2
+    return knex.schema.createTable('authentication', function (table) {
3
+        table.string('user_email', 90).primary().unique()
4
+        table.date('created_at').notNullable()
5
+        // table.char('token').notNullable()
6
+        table.binary('token')
7
+    })
8
+}
9
+
10
+exports.down = function (knex) {
11
+    return knex.schema.dropTable('authentication')
12
+}

+ 12
- 0
backend/lib/models/authentication.js Wyświetl plik

@@ -0,0 +1,12 @@
1
+const Schwifty = require('@hapipal/schwifty')
2
+const Joi = require('joi')
3
+const { userAuth } = require('../schemas/authentication')
4
+
5
+module.exports = class Auth extends Schwifty.Model {
6
+    static get tableName() {
7
+        return 'authentication'
8
+    }
9
+    static get joiSchema() {
10
+        return userAuth
11
+    }
12
+}

+ 4
- 0
backend/lib/plugins/user.js Wyświetl plik

@@ -5,12 +5,14 @@ const Jwt = require('@hapi/jwt')
5 5
 const JwtStrategy = require('../auth/strategies/jwt')
6 6
 
7 7
 const UserModel = require('../models/user')
8
+const AuthModel = require('../models/authentication')
8 9
 
9 10
 const UserCurrentRoute = require('../routes/user/current')
10 11
 const UserProfileCreateRoute = require('../routes/user/create-profile')
11 12
 const UserProfilesListRoute = require('../routes/user/list-profiles')
12 13
 const UserLoginRoute = require('../routes/user/login')
13 14
 const UserSignupRoute = require('../routes/user/signup')
15
+const UserPassword = require('../routes/user/authentication')
14 16
 
15 17
 const UserService = require('../services/user')
16 18
 const DisplayService = require('../services/display')
@@ -23,6 +25,7 @@ module.exports = {
23 25
         await server.register(Jwt)
24 26
         await server.register(Schwifty)
25 27
         await server.registerModel(UserModel)
28
+        await server.registerModel(AuthModel)
26 29
 
27 30
         const mainApp = server.registrations['main-app-plugin']
28 31
         const jwtOptions = JwtStrategy(mainApp.options)
@@ -46,5 +49,6 @@ module.exports = {
46 49
         await server.route(UserSignupRoute)
47 50
         await server.route(UserProfileCreateRoute)
48 51
         await server.route(UserProfilesListRoute)
52
+        await server.route(UserPassword)
49 53
     },
50 54
 }

+ 65
- 0
backend/lib/routes/user/authentication.js Wyświetl plik

@@ -0,0 +1,65 @@
1
+'use strict'
2
+
3
+const Joi = require('joi')
4
+const params = require('../../schemas/params')
5
+
6
+const pluginConfig = {
7
+    handlerType: 'password',
8
+    docs: {
9
+        get: {
10
+            description: 'get password',
11
+            notes: 'Returns a password by the user email passed in the path',
12
+        },
13
+    },
14
+}
15
+
16
+/** Validator functions by request method */
17
+const validators = {
18
+    /** Validate the route params (/active/{thing}) */
19
+    params: params.userEmail
20
+}
21
+
22
+module.exports = {
23
+    method: 'GET',
24
+    path: '/{user_email}/password',
25
+    options: {
26
+        ...pluginConfig.docs.get,
27
+        tags: ['api'],
28
+        // auth: 'default_jwt',
29
+        auth: false,
30
+        cors: true,
31
+        handler: async function (request, h) {
32
+            try {
33
+                const { userService } = request.services()
34
+                const userEmail = request.params.user_email
35
+
36
+                const password = await userService.getPassword(userEmail)
37
+
38
+                return {
39
+                    ok: true,
40
+                    handler: pluginConfig.handlerType,
41
+                    data: { password: password },
42
+                }
43
+            } catch (err) {
44
+                return {
45
+                    ok: false,
46
+                    handler: pluginConfig.handlerType,
47
+                    data: { error: err },
48
+                }
49
+            }
50
+        },
51
+        validate: {
52
+            ...validators,
53
+            failAction: 'log',
54
+        },
55
+
56
+        response: {
57
+            schema: Joi.object({
58
+                ok: Joi.bool(),
59
+                handler: Joi.string(),
60
+                data: Joi.object(),
61
+            }).label('password_res'),
62
+            failAction: 'log',
63
+        },
64
+    },
65
+}

+ 24
- 15
backend/lib/routes/user/login.js Wyświetl plik

@@ -16,13 +16,13 @@ const pluginConfig = {
16 16
 const validators = {
17 17
     post: {
18 18
         payload: Joi.object({
19
-            user: userSchema.single,
20
-            error: errorSchema.single,
21
-        })
22
-            .append()
23
-            .label('login_payload'),
19
+            user_email: Joi.string(),
20
+            password: Joi.string(),
21
+        }),
22
+        
24 23
     },
25 24
     user: userSchema.single,
25
+    error: errorSchema.single,
26 26
 }
27 27
 
28 28
 module.exports = {
@@ -34,7 +34,7 @@ module.exports = {
34 34
         auth: false,
35 35
         handler: async function (request, h) {
36 36
             try {
37
-                const { userService, displayService } = request.services()
37
+                const { userService } = request.services()
38 38
 
39 39
                 const res = request.payload
40 40
 
@@ -42,8 +42,8 @@ module.exports = {
42 42
                 const login = async txn => {
43 43
                     return await userService.login(
44 44
                         {
45
-                            email: res.user.email,
46
-                            password: res.user.password,
45
+                            email: res.user_email,
46
+                            password: res.password,
47 47
                         },
48 48
                         txn,
49 49
                     )
@@ -56,7 +56,7 @@ module.exports = {
56 56
                 return {
57 57
                     ok: true,
58 58
                     handler: pluginConfig.handlerType,
59
-                    data: displayService.user(user, token),
59
+                    data: { user_email: user.user_email, jwtToken: token },
60 60
                 }
61 61
             } catch (err) {
62 62
                 console.error(err)
@@ -69,12 +69,21 @@ module.exports = {
69 69
         },
70 70
         validate: validators.post,
71 71
         response: {
72
-            schema: Joi.object({
73
-                ok: Joi.bool(),
74
-                handler: Joi.string(),
75
-                data: validators.user,
76
-            }).label('login_res'),
77
-            failAction: 'log',
72
+            status: {
73
+                201: Joi.object({
74
+                    ok: Joi.bool(),
75
+                    handler: Joi.string(),
76
+                    data: Joi.object({
77
+                        user_email: Joi.string(),
78
+                        jwtToken: Joi.string(),
79
+                    }),
80
+                }).label('login_res'),
81
+                409: Joi.object({
82
+                    ok: Joi.bool(),
83
+                    handler: Joi.string(),
84
+                    data: validators.error,
85
+                }).label('login_error'),
86
+            },
78 87
         },
79 88
     },
80 89
 }

+ 1
- 0
backend/lib/routes/user/signup.js Wyświetl plik

@@ -56,6 +56,7 @@ module.exports = {
56 56
                         is_admin: 0,
57 57
                         is_verified: 0,
58 58
                     },
59
+                    created_at: Date.now()
59 60
                 })
60 61
                 return h
61 62
                     .response({

+ 13
- 0
backend/lib/schemas/authentication.js Wyświetl plik

@@ -0,0 +1,13 @@
1
+'use strict'
2
+
3
+const Joi = require('joi')
4
+
5
+const userAuth = Joi.object({
6
+    user_email: Joi.string(),
7
+    created_at: Joi.date(),
8
+    token: Joi.binary().allow(null)
9
+}).label('user_auth')
10
+
11
+module.exports = {
12
+    userAuth
13
+}

+ 2
- 1
backend/lib/schemas/params.js Wyświetl plik

@@ -7,5 +7,6 @@ module.exports = {
7 7
     userName: Joi.object({
8 8
         name: Joi.string().min(3).max(11),
9 9
         all: Joi.array(),
10
-    }).label('user_name_param')
10
+    }).label('user_name_param'),
11
+    userEmail: Joi.object({ user_email: Joi.string() }).label('user_email_param')
11 12
 }

+ 1
- 0
backend/lib/schemas/user.js Wyświetl plik

@@ -17,6 +17,7 @@ const userSignup = Joi.object({
17 17
     is_poster: Joi.number(),
18 18
     is_admin: Joi.number(),
19 19
     is_verified: Joi.number(),
20
+    user_pass: Joi.string()
20 21
 }).label('user_signup')
21 22
 
22 23
 module.exports = {

+ 92
- 21
backend/lib/services/user.js Wyświetl plik

@@ -1,10 +1,40 @@
1 1
 'use strict'
2
-
2
+require('dotenv').config()
3 3
 const Util = require('util')
4 4
 const Jwt = require('@hapi/jwt')
5 5
 const Schmervice = require('@hapipal/schmervice')
6 6
 const SecurePassword = require('secure-password')
7 7
 
8
+const hasher = async (pwd, steak) => {
9
+    const hash = await pwd.hash(steak)
10
+    const result = await pwd.verify(steak, hash)
11
+    let squirtle = null
12
+
13
+    switch (result) {
14
+        case SecurePassword.INVALID_UNRECOGNIZED_HASH:
15
+            return console.error(
16
+                'This hash was not made with secure-password. Attempt legacy algorithm',
17
+            )
18
+        case SecurePassword.INVALID:
19
+            return console.log('Invalid password')
20
+        case SecurePassword.VALID:
21
+            return result
22
+        case SecurePassword.VALID_NEEDS_REHASH:
23
+            console.log('Yay you made it, wait for us to improve your safety')
24
+            try {
25
+                squirtle = await pwd.hash(steak)
26
+                // console.log('improvedHash', squirtle)
27
+                // const saveHash = Auth.insert({user_email: matchingEmails}, ).into('token')
28
+                return squirtle
29
+            } catch (err) {
30
+                console.error(
31
+                    'You are authenticated, but we could not improve your safety this time around',
32
+                )
33
+            }
34
+            break
35
+    }
36
+}
37
+
8 38
 /** Class for methods used in the User plugin */
9 39
 module.exports = class UserService extends Schmervice.Service {
10 40
     /**
@@ -56,21 +86,33 @@ module.exports = class UserService extends Schmervice.Service {
56 86
      * @param {*} txn
57 87
      * @returns
58 88
      */
59
-    async signup({ password, userInfo }, txn) {
60
-        const { User } = this.server.models()
89
+    async signup({ password, userInfo, created_at }, txn) {
90
+        const { User, Auth } = this.server.models()
61 91
         const matchingEmails = await User.query().where(
62 92
             'user_email',
63 93
             userInfo.user_email,
64 94
         )
65
-
66 95
         if (matchingEmails.length > 0) {
67 96
             throw `User ${userInfo.user_email} already exists: Cannot create a user without a unique email`
68 97
         }
69
-        const user = await User.query(txn).insert(userInfo)
70
-        user.user_id = user.id
71
-        delete user.id
72
-        // await this.changePassword(id, password, txn)
73
-        return user
98
+        // Insert User Info to User table
99
+        const insertUser = await User.query().insert(userInfo)
100
+        // insert a row with blank password to be updated by changePassword()
101
+        await Auth.query().insert({
102
+            user_email: insertUser.user_email,
103
+            created_at: created_at,
104
+            token: null,
105
+        })
106
+        // update null token with hashed password
107
+        await this.changePassword(insertUser.user_email, password, txn)
108
+        return {
109
+            user_id: insertUser.id,
110
+            user_name: insertUser.user_name,
111
+            user_email: insertUser.user_email,
112
+            is_poster: insertUser.is_poster,
113
+            is_admin: insertUser.is_admin,
114
+            is_verified: insertUser.is_verified,
115
+        }
74 116
     }
75 117
 
76 118
     /**
@@ -104,21 +146,26 @@ module.exports = class UserService extends Schmervice.Service {
104 146
      * @returns
105 147
      */
106 148
     async login({ email, password }, txn) {
107
-        const { User } = this.server.models()
149
+        const { User, Auth } = this.server.models()
108 150
 
109
-        const user = await User.query(txn)
151
+        
152
+        
153
+        const user = await Auth.query(txn)
110 154
             .throwIfNotFound()
111 155
             .first()
112 156
             .where({ user_email: email })
113 157
 
158
+        const bufferPepper = Buffer.from(process.env.PEPPER + password)
159
+
114 160
         /** Uncomment to run password check using SecurePassword */
115
-        // const passwordCheck = await this.pwd.verify(Buffer.from(password), user.password)
116
-        // if (passwordCheck === SecurePassword.VALID_NEEDS_REHASH) {
117
-        //     await this.changePassword(user.id, password, txn)
118
-        // }
119
-        // else if (passwordCheck !== SecurePassword.VALID) {
120
-        //     throw User.createNotFoundError()
121
-        // }
161
+        const passwordCheck = await this.pwd.verify(bufferPepper, user.token)
162
+        console.log("passwordCheck", passwordCheck)
163
+        if (passwordCheck === SecurePassword.VALID_NEEDS_REHASH) {
164
+            await this.changePassword(user.user_email, password, txn)
165
+        }
166
+        else if (passwordCheck !== SecurePassword.VALID) {
167
+            throw User.createNotFoundError()
168
+        }
122 169
 
123 170
         return user
124 171
     }
@@ -154,9 +201,23 @@ module.exports = class UserService extends Schmervice.Service {
154 201
      * @param {*} txn
155 202
      * @returns {number}
156 203
      */
157
-    async changePassword(id, password, txn) {
158
-        const { User } = this.server.models()
159
-        return 'done'
204
+    async changePassword(email, password, txn) {
205
+        const { User, Auth } = this.server.models()
206
+
207
+        console.log('email passed to changePassword', email)
208
+
209
+        const hashed = await this.pwd.hash(Buffer.from(process.env.PEPPER + password))
210
+        console.log('hashed', hashed)
211
+
212
+        await Auth.query(txn)
213
+            .throwIfNotFound()
214
+            .where({ user_email: email })
215
+            .patch({
216
+                // user_email: email,
217
+                token: hashed
218
+            })
219
+        console.log('changePassword query completed')
220
+        return email
160 221
 
161 222
         // await User.query(txn)
162 223
         //     .throwIfNotFound()
@@ -166,4 +227,14 @@ module.exports = class UserService extends Schmervice.Service {
166 227
         //     })
167 228
         // return id
168 229
     }
230
+
231
+    async getPassword(email, txn) {
232
+        const { Auth } = this.server.models()
233
+
234
+        const passwordRow = await Auth.query(txn)
235
+            .where('user_email', email)
236
+            .first()
237
+
238
+        return passwordRow ? passwordRow.token : null
239
+    }
169 240
 }

Ładowanie…
Anuluj
Zapisz