@stanlemon/server-with-auth 0.2.12 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,70 @@
1
+ import Joi from "joi";
2
+ import { ROUTES } from "../constants.js";
3
+
4
+ /**
5
+ * Schema for a strong password.
6
+ */
7
+ export const PASSWORD = Joi.string()
8
+ .required()
9
+ .min(8)
10
+ .max(64)
11
+ .label("Password");
12
+
13
+ /**
14
+ * Schema for creating a new user
15
+ */
16
+ export const CREATE_USER = Joi.object({
17
+ username: Joi.string().required().label("Username"),
18
+ password: PASSWORD,
19
+ });
20
+
21
+ export const RESET_PASSWORD = Joi.object({
22
+ username: Joi.string().required().label("Username"),
23
+ });
24
+
25
+ export const CHANGE_PASSWORD = Joi.object({
26
+ password: PASSWORD,
27
+ current_password: PASSWORD.label("Current Password"),
28
+ });
29
+
30
+ export const USER = Joi.object({});
31
+
32
+ /**
33
+ * For use by the DAO to make sure the data shape is correct.
34
+ */
35
+ export const DAO = Joi.object({
36
+ id: Joi.string().uuid().label("ID").default("").required(),
37
+ username: Joi.string().label("Username").default("").required(),
38
+ password: Joi.string().label("Password").default("").required(),
39
+ verification_token: Joi.string().label("Verification Token").default(""),
40
+ verify_at: Joi.date().label("Date Verified").default(null),
41
+ last_logged_in: Joi.date().label("Date Last Logged In").default(null),
42
+ created_at: Joi.date().label("Date Created").default(null).required(),
43
+ last_updated: Joi.date().label("Date Last Updated").default(null).required(),
44
+ });
45
+
46
+ /**
47
+ * Base schemas mapped to routes.
48
+ */
49
+ const SCHEMAS = {
50
+ [ROUTES.SIGNUP]: CREATE_USER,
51
+ [ROUTES.RESET]: RESET_PASSWORD,
52
+ [ROUTES.PASSWORD]: CHANGE_PASSWORD,
53
+ [ROUTES.USER]: USER,
54
+ };
55
+
56
+ export default SCHEMAS;
57
+
58
+ /**
59
+ * Create a collection of schemas while supplying a custom user schema.
60
+ * @param {Joi.Schema} user User schema to append to the base schemas for sign up and user operations.
61
+ * @returns {Object.<string, Joi.Schema>} Schemas by end point.
62
+ */
63
+ export function createSchemas(user = {}) {
64
+ return {
65
+ ...SCHEMAS,
66
+ [ROUTES.SIGNUP]: SCHEMAS[[ROUTES.SIGNUP]].append(user),
67
+ [ROUTES.USER]: SCHEMAS[[ROUTES.USER]].append(user),
68
+ dao: DAO.append(user),
69
+ };
70
+ }
@@ -0,0 +1,25 @@
1
+ import Joi from "joi";
2
+ import { ROUTES } from "../constants.js";
3
+
4
+ export default function checkSchemas(schemas) {
5
+ Object.values(schemas).forEach((schema) => {
6
+ if (!Joi.isSchema(schema)) {
7
+ throw new Error("The schema object must be of type Joi schema.");
8
+ }
9
+ });
10
+
11
+ if (schemas[ROUTES.SIGNUP] === undefined) {
12
+ throw new Error(
13
+ "The schemas object must have a schema designed for the signup route."
14
+ );
15
+ }
16
+
17
+ if (
18
+ !schemas[ROUTES.SIGNUP].describe().keys.username ||
19
+ !schemas[ROUTES.SIGNUP].describe().keys.password
20
+ ) {
21
+ throw new Error(
22
+ "The schema object for the signup route must have a username and password defined."
23
+ );
24
+ }
25
+ }
@@ -0,0 +1,7 @@
1
+ import UserDao from "../data/user-dao.js";
2
+
3
+ export default function checkUserDao(dao) {
4
+ if (!(dao instanceof UserDao)) {
5
+ throw new Error("The dao object must be of type UserDao.");
6
+ }
7
+ }
package/test.http CHANGED
@@ -2,11 +2,13 @@ GET http://localhost:3003/ HTTP/1.1
2
2
  content-type: application/json
3
3
 
4
4
  ###
5
- POST http://localhost:3003/auth/register HTTP/1.1
5
+ # @name signup
6
+
7
+ POST http://localhost:3003/auth/signup HTTP/1.1
6
8
  Content-Type: application/json
7
9
 
8
10
  {
9
- "name": "Test Example",
11
+ "fullName": "Test Example",
10
12
  "email": "example@example.com",
11
13
  "username": "test123",
12
14
  "password": "password"
@@ -19,7 +21,7 @@ POST http://localhost:3003/auth/login HTTP/1.1
19
21
  Content-Type: application/json
20
22
 
21
23
  {
22
- "username": "user",
24
+ "username": "test123",
23
25
  "password": "password"
24
26
  }
25
27
 
@@ -27,11 +29,67 @@ Content-Type: application/json
27
29
 
28
30
  @authToken = {{login.response.body.token}}
29
31
 
30
- # @name session
32
+ ###
33
+
31
34
  GET http://localhost:3003/auth/session
32
35
  Authorization: Bearer {{authToken}}
33
36
 
34
37
  ###
35
38
 
39
+ GET http://localhost:3003/auth/user
40
+ Authorization: Bearer {{authToken}}
41
+
42
+ ###
43
+
36
44
  GET http://localhost:3003/api/users
37
45
  Authorization: Bearer {{authToken}}
46
+
47
+ ###
48
+
49
+ PUT http://localhost:3003/auth/user HTTP/1.1
50
+ Authorization: Bearer {{authToken}}
51
+ Content-Type: application/json
52
+
53
+ {
54
+ "email": "test@test.com",
55
+ "fullName": "Stanley Lemon"
56
+ }
57
+ ###
58
+
59
+ POST http://localhost:3003/auth/reset HTTP/1.1
60
+ Authorization: Bearer {{authToken}}
61
+ Content-Type: application/json
62
+
63
+ {
64
+ "username": "test123"
65
+ }
66
+
67
+ ###
68
+
69
+ POST http://localhost:3003/auth/password HTTP/1.1
70
+ Authorization: Bearer {{authToken}}
71
+ Content-Type: application/json
72
+
73
+ {
74
+ "currentPassword": "password",
75
+ "password": "newPassw0rd"
76
+ }
77
+
78
+ ###
79
+
80
+ DELETE http://localhost:3003/auth/user HTTP/1.1
81
+ Authorization: Bearer {{authToken}}
82
+
83
+ ###
84
+
85
+ GET http://localhost:3003/auth/logout
86
+ Authorization: Bearer {{authToken}}
87
+
88
+ ###
89
+
90
+ GET http://localhost:3003/doesNotExist
91
+
92
+ ###
93
+
94
+ GET http://localhost:3003/api/doesNotExist
95
+ Authorization: Bearer {{authToken}}
@@ -1,111 +0,0 @@
1
- /**
2
- * @jest-environment node
3
- */
4
- import LowDBUserDao, { createInMemoryDb } from "./lowdb-user-dao.js";
5
-
6
- describe("lowdb-user-dao", () => {
7
- // This is a user that we will reuse in our tests
8
- const data = {
9
- username: "test",
10
- password: "password",
11
- };
12
-
13
- /** @type {LowSync} */
14
- let db;
15
- /** @type {LowDBUserDao} */
16
- let dao;
17
-
18
- beforeEach(() => {
19
- // Before each test reset our users database
20
- db = createInMemoryDb();
21
- dao = new LowDBUserDao(db);
22
- });
23
-
24
- it("creates a user", async () => {
25
- let user = await dao.createUser(data);
26
-
27
- expect(user.username).toEqual(data.username);
28
- // The value should be encrypted now
29
- expect(user.password).not.toEqual(data.password);
30
- // These fields are added by the create process
31
- expect(user.verification_token).not.toBeUndefined();
32
- expect(user.created_at).not.toBeUndefined();
33
- expect(user.last_updated).not.toBeUndefined();
34
-
35
- const refresh = dao.getUserByUsername(data.username);
36
-
37
- // If we retrieve the user by username, it matches the object we got when we created it
38
- expect(refresh).toMatchObject(user);
39
- });
40
-
41
- it("creates a user with an existing username errors", async () => {
42
- let hasError = false;
43
-
44
- try {
45
- // Create the user
46
- await dao.createUser(data);
47
- // Attempt to create the user again, this will fail
48
- await dao.createUser(data);
49
- } catch (err) {
50
- hasError = err;
51
- }
52
-
53
- expect(hasError).not.toBe(false);
54
- expect(hasError.message).toEqual("This username is already taken.");
55
- });
56
-
57
- it("retrieve user by username and password", async () => {
58
- const user1 = await dao.createUser(data);
59
-
60
- const user2 = dao.getUserByUsernameAndPassword(
61
- data.username,
62
- data.password
63
- );
64
-
65
- expect(user1).toMatchObject(user2);
66
- });
67
-
68
- it("retrieve user by username and wrong password fails", async () => {
69
- await dao.createUser(data);
70
-
71
- const user2 = dao.getUserByUsernameAndPassword(
72
- data.username,
73
- "wrong password"
74
- );
75
-
76
- expect(user2).toBe(false);
77
- });
78
-
79
- it("retrieve user by username and undefined password fails", async () => {
80
- await dao.createUser(data);
81
-
82
- const user2 = dao.getUserByUsernameAndPassword(data.username, undefined);
83
-
84
- expect(user2).toBe(false);
85
- });
86
-
87
- it("retrieve user by username that does not exist fails", async () => {
88
- const user = dao.getUserByUsernameAndPassword("notarealuser", "password");
89
-
90
- expect(user).toBe(false);
91
- });
92
-
93
- it("retrieve user by username that is undefined fails", async () => {
94
- const user = dao.getUserByUsernameAndPassword(undefined, "password");
95
-
96
- expect(user).toBe(false);
97
- });
98
-
99
- it("deletes a user by id", async () => {
100
- const user = await dao.createUser(data);
101
-
102
- expect(db.data.users).toHaveLength(1);
103
-
104
- const deleted = dao.deleteUser(user.id);
105
-
106
- expect(deleted).toBe(true);
107
- expect(dao.getUserById(user.id)).toBeUndefined();
108
-
109
- expect(db.data.users).toHaveLength(0);
110
- });
111
- });
@@ -1,8 +0,0 @@
1
- import Joi from "joi";
2
-
3
- const schema = Joi.object({
4
- username: Joi.string().required().label("Username"),
5
- password: Joi.string().required().min(8).max(64).label("Password"),
6
- });
7
-
8
- export default schema;