-
Notifications
You must be signed in to change notification settings - Fork 1
/
policy.js
98 lines (79 loc) · 3.27 KB
/
policy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
const FenceBuilder = require('../cjs');
const utils = require('./externals/utils');
// Const's get this show on the road!
let FB = new FenceBuilder();
// A major benefit to the `FenceBuilder` approach is that by lazily executing
// function references, we can pull validation functions from different modules
FB = FB.register(utils.required, 'required');
// Function chaining allows for concise declarations and protects against mutation
FB = FB.register(utils.isString, 'isString').register(
utils.isValidEmailAddress,
'isValidEmailAddress'
);
// Function declarations can also be used. In this case we are defining a higher
// order validation that will receive an `entity` and a `policy` and return an
// object that represents the validity of said entity against said policy
FB = FB.register(function(entity, policy) {
const results = [];
// This validation assumes that there is a 1:1 mapping between attributes and
// validation functions. We will define the validation functions below
for (const attribute in entity) {
if (policy[attribute]) {
results.push(policy[attribute].run(entity[attribute]));
}
}
return results;
}, 'policy');
// All policies that extend from `basePolicy` should have a value
const basePolicy = FB.fork().required();
// Beyond that, we may want to be more specific with our validation functions,
// so we register a `minLength` and a `maxLength` function
let usernamePolicy = basePolicy.fork();
usernamePolicy = usernamePolicy.register(function(val, length) {
if (!utils.required(val) || !utils.isString(val) || !utils.isInteger(length)) {
return false;
}
return val.length >= length;
}, 'minLength');
// You don't have to chain functions, just make sure to store a reference
usernamePolicy = usernamePolicy.register(function(val, length) {
if (!utils.required(val) || !utils.isString(val) || !utils.isInteger(length)) {
return false;
}
return val.length <= length;
}, 'maxLength');
// The data type for username columns in MySQL is constCHAR(255), so we'll use that
// for the maximum length validation.
usernamePolicy = usernamePolicy.maxLength(255);
// Passwords have the same data type, should also have a value, etc, so consts call
// `fork()` on `usernamePolicy` to pick those up.
let passwordPolicy = usernamePolicy.fork();
// Beyond `maxLength`, usernames and passwords have different requirements/
usernamePolicy = usernamePolicy.minLength(4).isValidEmailAddress();
passwordPolicy = passwordPolicy.minLength(8);
// We create a 1:1 mapping of attributes to policies. This allows us to create a
// higher order policy, or a 'Policy of Policies', that will use `.policy()`
const userPolicy = {
username: usernamePolicy.build(),
password: passwordPolicy.build()
};
// Our higher order policy validation
const userFence = FB.fork()
.policy(userPolicy)
.build();
// Will pass all policy checks
const goodUser = {
username: 'tim.carlson@velocloud.net',
password: 'somepassword'
};
// Will fail some policy checks
const badUser = {
username: 'tim',
password: 'password'
};
// A collection of users we will run `userFence` against
const users = [goodUser, badUser];
console.log('User Fence');
users.forEach(function(user) {
userFence.run(user).explain();
});