Browse Source

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

tags/0.0.1^2
maeda 3 years ago
parent
commit
2d8953af21

+ 1
- 4
backend/.eslintrc View File

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

+ 12
- 0
backend/db/migrations/20220901171733_user_authentication.js View File

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 View File

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 View File

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

+ 65
- 0
backend/lib/routes/user/authentication.js View File

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 View File

16
 const validators = {
16
 const validators = {
17
     post: {
17
     post: {
18
         payload: Joi.object({
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
     user: userSchema.single,
24
     user: userSchema.single,
25
+    error: errorSchema.single,
26
 }
26
 }
27
 
27
 
28
 module.exports = {
28
 module.exports = {
34
         auth: false,
34
         auth: false,
35
         handler: async function (request, h) {
35
         handler: async function (request, h) {
36
             try {
36
             try {
37
-                const { userService, displayService } = request.services()
37
+                const { userService } = request.services()
38
 
38
 
39
                 const res = request.payload
39
                 const res = request.payload
40
 
40
 
42
                 const login = async txn => {
42
                 const login = async txn => {
43
                     return await userService.login(
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
                         txn,
48
                         txn,
49
                     )
49
                     )
56
                 return {
56
                 return {
57
                     ok: true,
57
                     ok: true,
58
                     handler: pluginConfig.handlerType,
58
                     handler: pluginConfig.handlerType,
59
-                    data: displayService.user(user, token),
59
+                    data: { user_email: user.user_email, jwtToken: token },
60
                 }
60
                 }
61
             } catch (err) {
61
             } catch (err) {
62
                 console.error(err)
62
                 console.error(err)
69
         },
69
         },
70
         validate: validators.post,
70
         validate: validators.post,
71
         response: {
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 View File

56
                         is_admin: 0,
56
                         is_admin: 0,
57
                         is_verified: 0,
57
                         is_verified: 0,
58
                     },
58
                     },
59
+                    created_at: Date.now()
59
                 })
60
                 })
60
                 return h
61
                 return h
61
                     .response({
62
                     .response({

+ 13
- 0
backend/lib/schemas/authentication.js View File

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 View File

7
     userName: Joi.object({
7
     userName: Joi.object({
8
         name: Joi.string().min(3).max(11),
8
         name: Joi.string().min(3).max(11),
9
         all: Joi.array(),
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 View File

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

+ 92
- 21
backend/lib/services/user.js View File

1
 'use strict'
1
 'use strict'
2
-
2
+require('dotenv').config()
3
 const Util = require('util')
3
 const Util = require('util')
4
 const Jwt = require('@hapi/jwt')
4
 const Jwt = require('@hapi/jwt')
5
 const Schmervice = require('@hapipal/schmervice')
5
 const Schmervice = require('@hapipal/schmervice')
6
 const SecurePassword = require('secure-password')
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
 /** Class for methods used in the User plugin */
38
 /** Class for methods used in the User plugin */
9
 module.exports = class UserService extends Schmervice.Service {
39
 module.exports = class UserService extends Schmervice.Service {
10
     /**
40
     /**
56
      * @param {*} txn
86
      * @param {*} txn
57
      * @returns
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
         const matchingEmails = await User.query().where(
91
         const matchingEmails = await User.query().where(
62
             'user_email',
92
             'user_email',
63
             userInfo.user_email,
93
             userInfo.user_email,
64
         )
94
         )
65
-
66
         if (matchingEmails.length > 0) {
95
         if (matchingEmails.length > 0) {
67
             throw `User ${userInfo.user_email} already exists: Cannot create a user without a unique email`
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
      * @returns
146
      * @returns
105
      */
147
      */
106
     async login({ email, password }, txn) {
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
             .throwIfNotFound()
154
             .throwIfNotFound()
111
             .first()
155
             .first()
112
             .where({ user_email: email })
156
             .where({ user_email: email })
113
 
157
 
158
+        const bufferPepper = Buffer.from(process.env.PEPPER + password)
159
+
114
         /** Uncomment to run password check using SecurePassword */
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
         return user
170
         return user
124
     }
171
     }
154
      * @param {*} txn
201
      * @param {*} txn
155
      * @returns {number}
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
         // await User.query(txn)
222
         // await User.query(txn)
162
         //     .throwIfNotFound()
223
         //     .throwIfNotFound()
166
         //     })
227
         //     })
167
         // return id
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
 }

Loading…
Cancel
Save