First commit
This commit is contained in:
9
node_modules/@ldapjs/messages/.eslintrc
generated
vendored
Normal file
9
node_modules/@ldapjs/messages/.eslintrc
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest"
|
||||
},
|
||||
|
||||
"extends": [
|
||||
"standard"
|
||||
]
|
||||
}
|
||||
10
node_modules/@ldapjs/messages/.github/workflows/main.yml
generated
vendored
Normal file
10
node_modules/@ldapjs/messages/.github/workflows/main.yml
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
name: "CI"
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
call-core-ci:
|
||||
uses: ldapjs/.github/.github/workflows/node-ci.yml@main
|
||||
5
node_modules/@ldapjs/messages/.taprc.yaml
generated
vendored
Normal file
5
node_modules/@ldapjs/messages/.taprc.yaml
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
reporter: terse
|
||||
coverage-map: coverage-map.js
|
||||
|
||||
files:
|
||||
- 'lib/**/*.test.js'
|
||||
21
node_modules/@ldapjs/messages/LICENSE
generated
vendored
Normal file
21
node_modules/@ldapjs/messages/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
Copyright (c) 2014 Patrick Mooney. All rights reserved.
|
||||
Copyright (c) 2014 Mark Cavage, Inc. All rights reserved.
|
||||
Copyright (c) 2022 The LDAPJS Collaborators.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE
|
||||
7
node_modules/@ldapjs/messages/README.md
generated
vendored
Normal file
7
node_modules/@ldapjs/messages/README.md
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# @ldapjs/messages
|
||||
|
||||
Provides methods and objects to represent LDAP messages.
|
||||
|
||||
## License
|
||||
|
||||
MIT.
|
||||
3
node_modules/@ldapjs/messages/coverage-map.js
generated
vendored
Normal file
3
node_modules/@ldapjs/messages/coverage-map.js
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = testFile => testFile.replace(/\.test\.js$/, '.js')
|
||||
41
node_modules/@ldapjs/messages/index.js
generated
vendored
Normal file
41
node_modules/@ldapjs/messages/index.js
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
// Base objects.
|
||||
LdapMessage: require('./lib/ldap-message'),
|
||||
LdapResult: require('./lib/ldap-result'),
|
||||
|
||||
// Request objects.
|
||||
AbandonRequest: require('./lib/messages/abandon-request'),
|
||||
AddRequest: require('./lib/messages/add-request'),
|
||||
BindRequest: require('./lib/messages/bind-request'),
|
||||
CompareRequest: require('./lib/messages/compare-request'),
|
||||
DeleteRequest: require('./lib/messages/delete-request'),
|
||||
ExtensionRequest: require('./lib/messages/extension-request'),
|
||||
ModifyRequest: require('./lib/messages/modify-request'),
|
||||
ModifyDnRequest: require('./lib/messages/modifydn-request'),
|
||||
SearchRequest: require('./lib/messages/search-request'),
|
||||
UnbindRequest: require('./lib/messages/unbind-request'),
|
||||
|
||||
// Response objects.
|
||||
AbandonResponse: require('./lib/messages/abandon-response'),
|
||||
AddResponse: require('./lib/messages/add-response'),
|
||||
BindResponse: require('./lib/messages/bind-response'),
|
||||
CompareResponse: require('./lib/messages/compare-response'),
|
||||
DeleteResponse: require('./lib/messages/delete-response'),
|
||||
ExtensionResponse: require('./lib/messages/extension-response'),
|
||||
ModifyResponse: require('./lib/messages/modify-response'),
|
||||
ModifyDnResponse: require('./lib/messages/modifydn-response'),
|
||||
|
||||
// Search request messages.
|
||||
SearchResultEntry: require('./lib/messages/search-result-entry'),
|
||||
SearchResultReference: require('./lib/messages/search-result-reference'),
|
||||
SearchResultDone: require('./lib/messages/search-result-done'),
|
||||
|
||||
// Specific extension response implementations.
|
||||
PasswordModifyResponse: require('./lib/messages/extension-responses/password-modify'),
|
||||
WhoAmIResponse: require('./lib/messages/extension-responses/who-am-i'),
|
||||
|
||||
// Miscellaneous objects.
|
||||
IntermediateResponse: require('./lib/messages/intermediate-response')
|
||||
}
|
||||
149
node_modules/@ldapjs/messages/lib/_fixtures/evolution-filter-req.js
generated
vendored
Normal file
149
node_modules/@ldapjs/messages/lib/_fixtures/evolution-filter-req.js
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = [
|
||||
0x30, 0x82, 0x04, 0xe4, // sequence, 1_252 bytes
|
||||
0x02, 0x01, 0x01, // message id (1)
|
||||
0x63, 0x82, 0x04, 0xdd, // protocol op (0x63), 1_245 bytes
|
||||
|
||||
0x04, 0x23, // string (baseObject), 35 bytes
|
||||
// "dc=dc3d09f8c56386aff779f0a9a7bc9514"
|
||||
0x64, 0x63, 0x3d, 0x64, 0x63, 0x33, 0x64, 0x30, 0x39, 0x66,
|
||||
0x38, 0x63, 0x35, 0x36, 0x33, 0x38, 0x36, 0x61, 0x66, 0x66,
|
||||
0x37, 0x37, 0x39, 0x66, 0x30, 0x61, 0x39, 0x61, 0x37, 0x62,
|
||||
0x63, 0x39, 0x35, 0x31, 0x34,
|
||||
|
||||
0x0a, 0x01, // scope, 1 byte
|
||||
0x00, // "base"
|
||||
0x0a, 0x01, // derefAliases, 1 byte
|
||||
0x00, // "never"
|
||||
0x02, 0x01, 0x00, // sizeLimit = 0
|
||||
0x02, 0x01, 0x0a, // timeLimit = 10
|
||||
0x01, 0x01, 0x00, // typesOnly = false
|
||||
|
||||
// See the `evolution-filter.js` fixture in `@ldapjs/filter` for a
|
||||
// complete breakdown of the filter bytes.
|
||||
// https://github.com/ldapjs/filter/blob/8a5892c/lib/ber-parsing/_fixtures/evolution-filter.js
|
||||
0xa1, 0x82, 0x04, 0xa3, // filter, 1_187 bytes
|
||||
0xa4, 0x0b, 0x04,
|
||||
0x02, 0x63, 0x6e, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f,
|
||||
0xa4, 0x12, 0x04, 0x09, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f,
|
||||
0xa4, 0x0b, 0x04, 0x02, 0x73, 0x6e, 0x30, 0x05, 0x80, 0x03,
|
||||
0x6f, 0x67, 0x6f, 0xa4, 0x0d, 0x04, 0x04, 0x6d, 0x61, 0x69,
|
||||
0x6c, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x0f,
|
||||
0x04, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x30, 0x05,
|
||||
0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x15, 0x04, 0x0c, 0x70,
|
||||
0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x70, 0x68, 0x6f, 0x6e,
|
||||
0x65, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x18,
|
||||
0x04, 0x0f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e,
|
||||
0x65, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x30, 0x05, 0x80,
|
||||
0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x12, 0x04, 0x09, 0x68, 0x6f,
|
||||
0x6d, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x30, 0x05, 0x80,
|
||||
0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x0f, 0x04, 0x06, 0x6d, 0x6f,
|
||||
0x62, 0x69, 0x6c, 0x65, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67,
|
||||
0x6f, 0xa4, 0x11, 0x04, 0x08, 0x63, 0x61, 0x72, 0x70, 0x68,
|
||||
0x6f, 0x6e, 0x65, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f,
|
||||
0xa4, 0x21, 0x04, 0x18, 0x66, 0x61, 0x63, 0x73, 0x69, 0x6d,
|
||||
0x69, 0x6c, 0x65, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f,
|
||||
0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x30, 0x05,
|
||||
0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x25, 0x04, 0x1c, 0x68,
|
||||
0x6f, 0x6d, 0x65, 0x66, 0x61, 0x63, 0x73, 0x69, 0x6d, 0x69,
|
||||
0x6c, 0x65, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e,
|
||||
0x65, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x30, 0x05, 0x80,
|
||||
0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x13, 0x04, 0x0a, 0x6f, 0x74,
|
||||
0x68, 0x65, 0x72, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x30, 0x05,
|
||||
0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x26, 0x04, 0x1d, 0x6f,
|
||||
0x74, 0x68, 0x65, 0x72, 0x66, 0x61, 0x63, 0x73, 0x69, 0x6d,
|
||||
0x69, 0x6c, 0x65, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f,
|
||||
0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x30, 0x05,
|
||||
0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x20, 0x04, 0x17, 0x69,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x61, 0x6c, 0x69, 0x73, 0x64, 0x6e, 0x6e, 0x75, 0x6d, 0x62,
|
||||
0x65, 0x72, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4,
|
||||
0x0e, 0x04, 0x05, 0x70, 0x61, 0x67, 0x65, 0x72, 0x30, 0x05,
|
||||
0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x0e, 0x04, 0x05, 0x72,
|
||||
0x61, 0x64, 0x69, 0x6f, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67,
|
||||
0x6f, 0xa4, 0x0e, 0x04, 0x05, 0x74, 0x65, 0x6c, 0x65, 0x78,
|
||||
0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x17, 0x04,
|
||||
0x0e, 0x61, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74,
|
||||
0x70, 0x68, 0x6f, 0x6e, 0x65, 0x30, 0x05, 0x80, 0x03, 0x6f,
|
||||
0x67, 0x6f, 0xa4, 0x15, 0x04, 0x0c, 0x63, 0x6f, 0x6d, 0x70,
|
||||
0x61, 0x6e, 0x79, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x30, 0x05,
|
||||
0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x16, 0x04, 0x0d, 0x63,
|
||||
0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x68, 0x6f,
|
||||
0x6e, 0x65, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4,
|
||||
0x0c, 0x04, 0x03, 0x74, 0x74, 0x79, 0x30, 0x05, 0x80, 0x03,
|
||||
0x6f, 0x67, 0x6f, 0xa4, 0x0a, 0x04, 0x01, 0x6f, 0x30, 0x05,
|
||||
0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x0b, 0x04, 0x02, 0x6f,
|
||||
0x75, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x13,
|
||||
0x04, 0x0a, 0x72, 0x6f, 0x6f, 0x6d, 0x6e, 0x75, 0x6d, 0x62,
|
||||
0x65, 0x72, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4,
|
||||
0x0e, 0x04, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x30, 0x05,
|
||||
0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x15, 0x04, 0x0c, 0x62,
|
||||
0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x72, 0x6f, 0x6c,
|
||||
0x65, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x14,
|
||||
0x04, 0x0b, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f,
|
||||
0xa4, 0x16, 0x04, 0x0d, 0x61, 0x73, 0x73, 0x69, 0x73, 0x74,
|
||||
0x61, 0x6e, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x30, 0x05, 0x80,
|
||||
0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x16, 0x04, 0x0d, 0x70, 0x6f,
|
||||
0x73, 0x74, 0x61, 0x6c, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x0a,
|
||||
0x04, 0x01, 0x6c, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f,
|
||||
0xa4, 0x0b, 0x04, 0x02, 0x73, 0x74, 0x30, 0x05, 0x80, 0x03,
|
||||
0x6f, 0x67, 0x6f, 0xa4, 0x16, 0x04, 0x0d, 0x70, 0x6f, 0x73,
|
||||
0x74, 0x6f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x62, 0x6f, 0x78,
|
||||
0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x13, 0x04,
|
||||
0x0a, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x6c, 0x63, 0x6f, 0x64,
|
||||
0x65, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x0a,
|
||||
0x04, 0x01, 0x63, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f,
|
||||
0xa4, 0x1a, 0x04, 0x11, 0x68, 0x6f, 0x6d, 0x65, 0x70, 0x6f,
|
||||
0x73, 0x74, 0x61, 0x6c, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x20,
|
||||
0x04, 0x17, 0x6d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, 0x61, 0x68,
|
||||
0x6f, 0x6d, 0x65, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x74,
|
||||
0x79, 0x6e, 0x61, 0x6d, 0x65, 0x30, 0x05, 0x80, 0x03, 0x6f,
|
||||
0x67, 0x6f, 0xa4, 0x19, 0x04, 0x10, 0x6d, 0x6f, 0x7a, 0x69,
|
||||
0x6c, 0x6c, 0x61, 0x68, 0x6f, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||
0x74, 0x65, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4,
|
||||
0x1e, 0x04, 0x15, 0x6d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, 0x61,
|
||||
0x68, 0x6f, 0x6d, 0x65, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x6c,
|
||||
0x63, 0x6f, 0x64, 0x65, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67,
|
||||
0x6f, 0xa4, 0x1f, 0x04, 0x16, 0x6d, 0x6f, 0x7a, 0x69, 0x6c,
|
||||
0x6c, 0x61, 0x68, 0x6f, 0x6d, 0x65, 0x63, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x72, 0x79, 0x6e, 0x61, 0x6d, 0x65, 0x30, 0x05, 0x80,
|
||||
0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x1b, 0x04, 0x12, 0x6f, 0x74,
|
||||
0x68, 0x65, 0x72, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x6c, 0x61,
|
||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x30, 0x05, 0x80, 0x03,
|
||||
0x6f, 0x67, 0x6f, 0xa4, 0x12, 0x04, 0x09, 0x6a, 0x70, 0x65,
|
||||
0x67, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x30, 0x05, 0x80, 0x03,
|
||||
0x6f, 0x67, 0x6f, 0xa4, 0x18, 0x04, 0x0f, 0x75, 0x73, 0x65,
|
||||
0x72, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
|
||||
0x74, 0x65, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4,
|
||||
0x13, 0x04, 0x0a, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x64,
|
||||
0x75, 0x72, 0x69, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f,
|
||||
0xa4, 0x14, 0x04, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61,
|
||||
0x79, 0x6e, 0x61, 0x6d, 0x65, 0x30, 0x05, 0x80, 0x03, 0x6f,
|
||||
0x67, 0x6f, 0xa4, 0x13, 0x04, 0x0a, 0x73, 0x70, 0x6f, 0x75,
|
||||
0x73, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x30, 0x05, 0x80, 0x03,
|
||||
0x6f, 0x67, 0x6f, 0xa4, 0x0d, 0x04, 0x04, 0x6e, 0x6f, 0x74,
|
||||
0x65, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x14,
|
||||
0x04, 0x0b, 0x61, 0x6e, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73,
|
||||
0x61, 0x72, 0x79, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f,
|
||||
0xa4, 0x12, 0x04, 0x09, 0x62, 0x69, 0x72, 0x74, 0x68, 0x64,
|
||||
0x61, 0x74, 0x65, 0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f,
|
||||
0xa4, 0x0f, 0x04, 0x06, 0x6d, 0x61, 0x69, 0x6c, 0x65, 0x72,
|
||||
0x30, 0x05, 0x80, 0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x0f, 0x04,
|
||||
0x06, 0x66, 0x69, 0x6c, 0x65, 0x61, 0x73, 0x30, 0x05, 0x80,
|
||||
0x03, 0x6f, 0x67, 0x6f, 0xa4, 0x11, 0x04, 0x08, 0x63, 0x61,
|
||||
0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x30, 0x05, 0x80, 0x03,
|
||||
0x6f, 0x67, 0x6f, 0xa4, 0x12, 0x04, 0x09, 0x63, 0x61, 0x6c,
|
||||
0x63, 0x61, 0x6c, 0x75, 0x72, 0x69, 0x30, 0x05, 0x80, 0x03,
|
||||
0x6f, 0x67, 0x6f, 0xa4, 0x11, 0x04, 0x08, 0x63, 0x61, 0x6c,
|
||||
0x66, 0x62, 0x75, 0x72, 0x6c, 0x30, 0x05, 0x80, 0x03, 0x6f,
|
||||
0x67, 0x6f, 0xa4, 0x14, 0x04, 0x0b, 0x69, 0x63, 0x73, 0x63,
|
||||
0x61, 0x6c, 0x65, 0x6e, 0x64, 0x61, 0x72, 0x30, 0x05, 0x80,
|
||||
0x03, 0x6f, 0x67, 0x6f,
|
||||
|
||||
0x30, 0x00 // attributes set, 0 bytes
|
||||
]
|
||||
16
node_modules/@ldapjs/messages/lib/deprecations.js
generated
vendored
Normal file
16
node_modules/@ldapjs/messages/lib/deprecations.js
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
'use strict'
|
||||
|
||||
const warning = require('process-warning')()
|
||||
const clazz = 'LdapjsMessageWarning'
|
||||
|
||||
warning.create(clazz, 'LDAP_MESSAGE_DEP_001', 'messageID is deprecated. Use messageId instead.')
|
||||
|
||||
warning.create(clazz, 'LDAP_MESSAGE_DEP_002', 'The .json property is deprecated. Use .pojo instead.')
|
||||
|
||||
warning.create(clazz, 'LDAP_MESSAGE_DEP_003', 'abandonID is deprecated. Use abandonId instead.')
|
||||
|
||||
warning.create(clazz, 'LDAP_MESSAGE_DEP_004', 'errorMessage is deprecated. Use diagnosticMessage instead.')
|
||||
|
||||
warning.create(clazz, 'LDAP_ATTRIBUTE_SPEC_ERR_001', 'received attempt to define attribute with an empty name: attribute skipped.', { unlimited: true })
|
||||
|
||||
module.exports = warning
|
||||
282
node_modules/@ldapjs/messages/lib/ldap-message.js
generated
vendored
Normal file
282
node_modules/@ldapjs/messages/lib/ldap-message.js
generated
vendored
Normal file
@ -0,0 +1,282 @@
|
||||
'use strict'
|
||||
|
||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
||||
const warning = require('./deprecations')
|
||||
|
||||
/**
|
||||
* Implements a base LDAP message as defined in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.1.1.
|
||||
*/
|
||||
class LdapMessage {
|
||||
#messageId = 0
|
||||
#protocolOp
|
||||
#controls = []
|
||||
|
||||
/**
|
||||
* @typedef {object} LdapMessageOptions
|
||||
* @property {number} [messageId=1] An identifier for the message.
|
||||
* @property {number} [protocolOp] The tag for the message operation.
|
||||
* @property {import('@ldapjs/controls').Control[]} [controls] A set of LDAP
|
||||
* controls to send with the message. See the `@ldapjs/controls` package.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {LdapMessageOptions} [options]
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
this.#messageId = parseInt(options.messageId ?? options.messageID ?? '1', 10)
|
||||
if (options.messageID !== undefined) {
|
||||
warning.emit('LDAP_MESSAGE_DEP_001')
|
||||
}
|
||||
|
||||
if (typeof options.protocolOp === 'number') {
|
||||
this.#protocolOp = options.protocolOp
|
||||
}
|
||||
|
||||
this.controls = options.controls ?? []
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag] () {
|
||||
return 'LdapMessage'
|
||||
}
|
||||
|
||||
/**
|
||||
* A copy of the list of controls that will be sent with the request.
|
||||
*
|
||||
* @returns {import('@ldapjs/controls').Control[]}
|
||||
*/
|
||||
get controls () {
|
||||
return this.#controls.slice(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the list of controls that will be sent with the request. Any
|
||||
* existing controls will be discarded.
|
||||
*
|
||||
* @param {import('@ldapjs/controls').Control[]} values
|
||||
*
|
||||
* @throws When a control value is invalid.
|
||||
*/
|
||||
set controls (values) {
|
||||
if (Array.isArray(values) !== true) {
|
||||
throw Error('controls must be an array')
|
||||
}
|
||||
const newControls = []
|
||||
for (const val of values) {
|
||||
if (Object.prototype.toString.call(val) !== '[object LdapControl]') {
|
||||
throw Error('control must be an instance of LdapControl')
|
||||
}
|
||||
newControls.push(val)
|
||||
}
|
||||
this.#controls = newControls
|
||||
}
|
||||
|
||||
/**
|
||||
* The message identifier.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get id () {
|
||||
return this.#messageId
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the message identifier for the request.
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set id (value) {
|
||||
if (Number.isInteger(value) === false) {
|
||||
throw Error('id must be an integer')
|
||||
}
|
||||
this.#messageId = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for {@link id}.
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get messageId () {
|
||||
return this.id
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for {@link id}.
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set messageId (value) {
|
||||
this.id = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for {@link id}.
|
||||
*
|
||||
* @returns {number}
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
get messageID () {
|
||||
warning.emit('LDAP_MESSAGE_DEP_001')
|
||||
return this.id
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for {@link id}.
|
||||
*
|
||||
* @param {number} value
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
set messageID (value) {
|
||||
warning.emit('LDAP_MESSAGE_DEP_001')
|
||||
this.id = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Message type specific. Each message type must implement a `_dn` property
|
||||
* that provides this value.
|
||||
*
|
||||
* @type {import('@ldapjs/dn').DN}
|
||||
*/
|
||||
get dn () {
|
||||
return this._dn
|
||||
}
|
||||
|
||||
/**
|
||||
* The LDAP protocol operation code for the message.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get protocolOp () {
|
||||
return this.#protocolOp
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the message class.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'LdapMessage'
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link pojo} instead.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
get json () {
|
||||
warning.emit('LDAP_MESSAGE_DEP_002')
|
||||
return this.pojo
|
||||
}
|
||||
|
||||
/**
|
||||
* A serialized representation of the message as a plain JavaScript object.
|
||||
* Specific message types must implement the `_pojo(obj)` method. The passed
|
||||
* in `obj` must be extended with the specific message's unique properties
|
||||
* and returned as the result.
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
get pojo () {
|
||||
let result = {
|
||||
messageId: this.id,
|
||||
protocolOp: this.#protocolOp,
|
||||
type: this.type
|
||||
}
|
||||
|
||||
if (typeof this._pojo === 'function') {
|
||||
result = this._pojo(result)
|
||||
}
|
||||
|
||||
result.controls = this.#controls.map(c => c.pojo)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
addControl (control) {
|
||||
this.#controls.push(control)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an {@link LdapMessage} object into a set of BER bytes that can
|
||||
* be sent across the wire. Specific message implementations must implement
|
||||
* the `_toBer(ber)` method. This method will write its unique sequence(s)
|
||||
* to the passed in `ber` object.
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerReader}
|
||||
*/
|
||||
toBer () {
|
||||
if (typeof this._toBer !== 'function') {
|
||||
throw Error(`${this.type} does not implement _toBer`)
|
||||
}
|
||||
|
||||
const writer = new BerWriter()
|
||||
writer.startSequence()
|
||||
writer.writeInt(this.id)
|
||||
|
||||
this._toBer(writer)
|
||||
|
||||
if (this.#controls.length > 0) {
|
||||
writer.startSequence(0xa0)
|
||||
for (const control of this.#controls) {
|
||||
control.toBer(writer)
|
||||
}
|
||||
writer.endSequence()
|
||||
}
|
||||
|
||||
writer.endSequence()
|
||||
return new BerReader(writer.buffer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the message into a JSON representation.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
toString () {
|
||||
return JSON.stringify(this.pojo)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a BER into a message object. The offset of the BER _must_ point
|
||||
* to the start of an LDAP Message sequence. That is, the first few bytes
|
||||
* must indicate:
|
||||
*
|
||||
* 1. a sequence tag and how many bytes are in that sequence
|
||||
* 2. an integer representing the message identifier
|
||||
* 3. a protocol operation, e.g. BindRequest, and the number of bytes in
|
||||
* that operation
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {LdapMessage}
|
||||
*/
|
||||
static parse (ber) {
|
||||
// We must require here because `parseToMessage` imports subclasses
|
||||
// that need `LdapMessage` to be defined. If we try importing earlier,
|
||||
// then `LdapMessage` will not be available, and we will get errors about
|
||||
// trying to subclass null objects.
|
||||
return require('./parse-to-message')(ber)
|
||||
}
|
||||
|
||||
/**
|
||||
* When invoked on specific message types, e.g. {@link BindRequest}, this
|
||||
* method will parse a BER into a plain JavaScript object that is usable as
|
||||
* an options object for constructing that specific message object.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber A BER to parse. The reader
|
||||
* offset must point to the start of a valid sequence, i.e. the "tag" byte
|
||||
* in the TLV tuple, that represents the message to be parsed. For example,
|
||||
* in a {@link BindRequest} the starting sequence and message identifier must
|
||||
* already be read such that the offset is at the protocol operation sequence
|
||||
* byte.
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
throw Error('Use LdapMessage.parse, or a specific message type\'s parseToPojo, instead.')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LdapMessage
|
||||
315
node_modules/@ldapjs/messages/lib/ldap-message.test.js
generated
vendored
Normal file
315
node_modules/@ldapjs/messages/lib/ldap-message.test.js
generated
vendored
Normal file
@ -0,0 +1,315 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const warning = require('./deprecations')
|
||||
const { Control } = require('@ldapjs/controls')
|
||||
const LdapMessage = require('./ldap-message')
|
||||
|
||||
// Silence the standard warning logs. We will test the messages explicitly.
|
||||
process.removeAllListeners('warning')
|
||||
|
||||
const {
|
||||
abandonRequestBytes,
|
||||
bindRequestBytes,
|
||||
deleteRequestBytes
|
||||
} = require('./messages/_fixtures/message-byte-arrays')
|
||||
|
||||
tap.test('constructor', t => {
|
||||
t.test('no args', async t => {
|
||||
const message = new LdapMessage()
|
||||
t.strictSame(message.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: undefined,
|
||||
type: 'LdapMessage',
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('all options supplied', t => {
|
||||
process.on('warning', handler)
|
||||
t.teardown(async () => {
|
||||
process.removeListener('warning', handler)
|
||||
warning.emitted.set('LDAP_MESSAGE_DEP_001', false)
|
||||
})
|
||||
|
||||
const message = new LdapMessage({
|
||||
messageID: 10,
|
||||
protocolOp: 0x01,
|
||||
controls: [new Control({ type: 'foo', value: 'foo' })]
|
||||
})
|
||||
t.strictSame(message.pojo, {
|
||||
messageId: 10,
|
||||
protocolOp: 0x01,
|
||||
type: 'LdapMessage',
|
||||
controls: [{
|
||||
type: 'foo',
|
||||
value: 'foo',
|
||||
criticality: false
|
||||
}]
|
||||
})
|
||||
|
||||
function handler (error) {
|
||||
t.equal(error.message, 'messageID is deprecated. Use messageId instead.')
|
||||
t.end()
|
||||
}
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('misc', t => {
|
||||
t.test('toStringTag is correct', async t => {
|
||||
const message = new LdapMessage()
|
||||
t.equal(Object.prototype.toString.call(message), '[object LdapMessage]')
|
||||
})
|
||||
|
||||
t.test('dn returns _dn', async t => {
|
||||
class Foo extends LdapMessage {
|
||||
get _dn () {
|
||||
return 'foo'
|
||||
}
|
||||
}
|
||||
|
||||
const message = new Foo()
|
||||
t.equal(message.dn, 'foo')
|
||||
})
|
||||
|
||||
t.test('protocolOp returns code', async t => {
|
||||
const message = new LdapMessage({ protocolOp: 1 })
|
||||
t.equal(message.protocolOp, 1)
|
||||
})
|
||||
|
||||
t.test('json emits warning', t => {
|
||||
process.on('warning', handler)
|
||||
t.teardown(async () => {
|
||||
process.removeListener('warning', handler)
|
||||
warning.emitted.set('LDAP_MESSAGE_DEP_002', false)
|
||||
})
|
||||
|
||||
const message = new LdapMessage()
|
||||
t.ok(message.json)
|
||||
|
||||
function handler (error) {
|
||||
t.equal(
|
||||
error.message,
|
||||
'The .json property is deprecated. Use .pojo instead.'
|
||||
)
|
||||
t.end()
|
||||
}
|
||||
})
|
||||
|
||||
t.test('toString returns JSON', async t => {
|
||||
const message = new LdapMessage()
|
||||
const expected = JSON.stringify(message.pojo)
|
||||
t.equal(message.toString(), expected)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.controls', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const req = new LdapMessage()
|
||||
t.strictSame(req.controls, [])
|
||||
|
||||
req.controls = [new Control()]
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: undefined,
|
||||
type: 'LdapMessage',
|
||||
controls: [{
|
||||
type: '',
|
||||
value: null,
|
||||
criticality: false
|
||||
}]
|
||||
})
|
||||
})
|
||||
|
||||
t.test('rejects for non-array', async t => {
|
||||
const req = new LdapMessage()
|
||||
t.throws(
|
||||
() => {
|
||||
req.controls = {}
|
||||
},
|
||||
'controls must be an array'
|
||||
)
|
||||
})
|
||||
|
||||
t.test('rejects if array item is not a control', async t => {
|
||||
const req = new LdapMessage()
|
||||
t.throws(
|
||||
() => {
|
||||
req.controls = ['foo']
|
||||
},
|
||||
'control must be an instance of LdapControl'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.id', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const req = new LdapMessage()
|
||||
t.equal(req.id, 1)
|
||||
|
||||
req.id = 2
|
||||
t.equal(req.id, 2)
|
||||
t.equal(req.messageId, 2)
|
||||
|
||||
req.messageId = 3
|
||||
t.equal(req.id, 3)
|
||||
})
|
||||
|
||||
t.test('throws if not an integer', async t => {
|
||||
const req = new LdapMessage()
|
||||
t.throws(
|
||||
() => {
|
||||
req.id = 1.5
|
||||
},
|
||||
'id must be an integer'
|
||||
)
|
||||
})
|
||||
|
||||
t.test('get messageID is deprecated', t => {
|
||||
process.on('warning', handler)
|
||||
t.teardown(async () => {
|
||||
process.removeListener('warning', handler)
|
||||
warning.emitted.set('LDAP_MESSAGE_DEP_001', false)
|
||||
})
|
||||
|
||||
const message = new LdapMessage()
|
||||
t.ok(message.messageID)
|
||||
|
||||
function handler (error) {
|
||||
t.equal(
|
||||
error.message,
|
||||
'messageID is deprecated. Use messageId instead.'
|
||||
)
|
||||
t.end()
|
||||
}
|
||||
})
|
||||
|
||||
t.test('set messageID is deprecated', t => {
|
||||
process.on('warning', handler)
|
||||
t.teardown(async () => {
|
||||
process.removeListener('warning', handler)
|
||||
warning.emitted.set('LDAP_MESSAGE_DEP_001', false)
|
||||
})
|
||||
|
||||
const message = new LdapMessage()
|
||||
message.messageID = 2
|
||||
|
||||
function handler (error) {
|
||||
t.equal(
|
||||
error.message,
|
||||
'messageID is deprecated. Use messageId instead.'
|
||||
)
|
||||
t.end()
|
||||
}
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('toBer', t => {
|
||||
t.test('throws for bad subclass', async t => {
|
||||
class Foo extends LdapMessage {
|
||||
}
|
||||
|
||||
const message = new Foo()
|
||||
|
||||
t.throws(
|
||||
() => message.toBer(),
|
||||
Error('LdapMessage does not implement _toBer')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('converts BindRequest to BER', async t => {
|
||||
const reqBuffer = Buffer.from(bindRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
const message = LdapMessage.parse(reader)
|
||||
|
||||
const ber = message.toBer()
|
||||
t.equal('[object BerReader]', Object.prototype.toString.call(ber))
|
||||
t.equal(reqBuffer.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.test('converts DeleteRequest to BER', async t => {
|
||||
const reqBuffer = Buffer.from(deleteRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
const message = LdapMessage.parse(reader)
|
||||
|
||||
const ber = message.toBer()
|
||||
t.equal(reqBuffer.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parse', t => {
|
||||
t.test('parses an abandon request', async t => {
|
||||
const reader = new BerReader(Buffer.from(abandonRequestBytes))
|
||||
const message = LdapMessage.parse(reader)
|
||||
|
||||
t.strictSame(message.pojo, {
|
||||
messageId: 6,
|
||||
protocolOp: 0x50,
|
||||
type: 'AbandonRequest',
|
||||
abandonId: 5,
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('parses a bind request', async t => {
|
||||
const reader = new BerReader(Buffer.from(bindRequestBytes))
|
||||
const message = LdapMessage.parse(reader)
|
||||
|
||||
t.strictSame(message.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: 0x60,
|
||||
type: 'BindRequest',
|
||||
version: 3,
|
||||
name: 'uid=admin,ou=system',
|
||||
authenticationType: 'simple',
|
||||
credentials: 'secret',
|
||||
controls: []
|
||||
})
|
||||
|
||||
t.equal(message.name, 'uid=admin,ou=system')
|
||||
})
|
||||
|
||||
t.test('parses a delete request with controls', async t => {
|
||||
const reader = new BerReader(Buffer.from(deleteRequestBytes))
|
||||
const message = LdapMessage.parse(reader)
|
||||
|
||||
// We need to parse the JSON representation because stringSame will return
|
||||
// false when comparing a plain object to an instance of Control.
|
||||
t.strictSame(JSON.parse(message.toString()), {
|
||||
messageId: 5,
|
||||
protocolOp: 0x4a,
|
||||
type: 'DeleteRequest',
|
||||
entry: 'dc=example,dc=com',
|
||||
controls: [{
|
||||
type: '1.2.840.113556.1.4.805',
|
||||
criticality: true,
|
||||
value: null
|
||||
}]
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws because not implemented', async t => {
|
||||
const expected = Error('Use LdapMessage.parse, or a specific message type\'s parseToPojo, instead.')
|
||||
t.throws(
|
||||
() => LdapMessage.parseToPojo(),
|
||||
expected
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
244
node_modules/@ldapjs/messages/lib/ldap-result.js
generated
vendored
Normal file
244
node_modules/@ldapjs/messages/lib/ldap-result.js
generated
vendored
Normal file
@ -0,0 +1,244 @@
|
||||
'use strict'
|
||||
|
||||
const LdapMessage = require('./ldap-message')
|
||||
const { resultCodes, operations } = require('@ldapjs/protocol')
|
||||
const warning = require('./deprecations')
|
||||
|
||||
/**
|
||||
* Implements the base LDAP response message as defined in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.1.9.
|
||||
*/
|
||||
class LdapResult extends LdapMessage {
|
||||
#connection = null
|
||||
#diagnosticMessage
|
||||
#matchedDN
|
||||
#referrals = []
|
||||
#status
|
||||
|
||||
/**
|
||||
* @typedef {LdapMessageOptions} LdapResultOptions
|
||||
* @property {number} [status=0] An LDAP status code.
|
||||
* @param {string} [matchedDN=''] The DN that matched the request.
|
||||
* @param {string[]} [referrals=[]] A set of servers to query for references.
|
||||
* @param {string} [diagnosticMessage] A message indicating why a request
|
||||
* failed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {LdapResultOptions} options
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
super(options)
|
||||
|
||||
this.#status = options.status ?? resultCodes.SUCCESS
|
||||
this.#matchedDN = options.matchedDN || ''
|
||||
this.#referrals = options.referrals || []
|
||||
this.#diagnosticMessage = options.diagnosticMessage || options.errorMessage || ''
|
||||
if (options.errorMessage) {
|
||||
warning.emit('LDAP_MESSAGE_DEP_004')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The failure message as returned by the server if one is present.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
get diagnosticMessage () {
|
||||
return this.#diagnosticMessage
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a diagnostic message to the instance.
|
||||
*
|
||||
* @param {string} message
|
||||
*/
|
||||
set diagnosticMessage (message) {
|
||||
this.#diagnosticMessage = message
|
||||
}
|
||||
|
||||
/**
|
||||
* The DN that a request matched.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
get matchedDN () {
|
||||
return this.#matchedDN
|
||||
}
|
||||
|
||||
/**
|
||||
* Define which DN a request matched.
|
||||
*
|
||||
* @param {string} dn
|
||||
*/
|
||||
set matchedDN (dn) {
|
||||
this.#matchedDN = dn
|
||||
}
|
||||
|
||||
/**
|
||||
* A serialized representation of the message as a plain JavaScript object.
|
||||
* Specific message types must implement the `_pojo(obj)` method. The passed
|
||||
* in `obj` must be extended with the specific message's unique properties
|
||||
* and returned as the result.
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
get pojo () {
|
||||
let result = {
|
||||
status: this.status,
|
||||
matchedDN: this.matchedDN,
|
||||
diagnosticMessage: this.diagnosticMessage,
|
||||
referrals: this.referrals
|
||||
}
|
||||
|
||||
if (typeof this._pojo === 'function') {
|
||||
result = this._pojo(result)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of servers that should be consulted to get an answer
|
||||
* to the query.
|
||||
*
|
||||
* @returns {string[]}
|
||||
*/
|
||||
get referrals () {
|
||||
return this.#referrals.slice(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* The LDAP response code for the request.
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get status () {
|
||||
return this.#status
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the response code for the request.
|
||||
*
|
||||
* @param {number} s
|
||||
*/
|
||||
set status (s) {
|
||||
this.#status = s
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'LdapResult'
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new server to the list of servers that should be
|
||||
* consulted for an answer to the query.
|
||||
*
|
||||
* @param {string} referral
|
||||
*/
|
||||
addReferral (referral) {
|
||||
this.#referrals.push(referral)
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only. Subclasses may implement a `_writeResponse`
|
||||
* method to add to the sequence after any referrals.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.startSequence(this.protocolOp)
|
||||
ber.writeEnumeration(this.status)
|
||||
ber.writeString(this.matchedDN)
|
||||
ber.writeString(this.diagnosticMessage)
|
||||
|
||||
if (this.referrals.length > 0) {
|
||||
ber.startSequence(operations.LDAP_RES_REFERRAL)
|
||||
ber.writeStringArray(this.referrals)
|
||||
ber.endSequence()
|
||||
}
|
||||
|
||||
if (typeof this._writeResponse === 'function') {
|
||||
this._writeResponse(ber)
|
||||
}
|
||||
|
||||
ber.endSequence()
|
||||
}
|
||||
|
||||
/**
|
||||
* When invoked on specific message types, e.g. {@link AddResponse}, this
|
||||
* method will parse a BER into a plain JavaScript object that is usable as
|
||||
* an options object for constructing that specific message object.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber A BER to parse. The reader
|
||||
* offset must point to the start of a valid sequence, i.e. the "tag" byte
|
||||
* in the TLV tuple, that represents the message to be parsed. For example,
|
||||
* in a {@link AddResponse} the starting sequence and message identifier must
|
||||
* already be read such that the offset is at the protocol operation sequence
|
||||
* byte.
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
throw Error('Use LdapMessage.parse, or a specific message type\'s parseToPojo, instead.')
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* Response messages are a little more generic to parse than request messages.
|
||||
* However, they still need to recognize the correct protocol operation. So
|
||||
* the public {@link parseToPojo} for each response object should invoke this
|
||||
* private static method to parse the BER and indicate the correct protocol
|
||||
* operation to recognize.
|
||||
*
|
||||
* @param {object} input
|
||||
* @param {number} input.opCode The expected protocol operation to look for.
|
||||
* @param {import('@ldapjs/asn1').BerReader} berReader The BER to process. It
|
||||
* must start at an offset representing a protocol operation tag.
|
||||
* @param {object} [input.pojo] A plain JavaScript object to populate with
|
||||
* the parsed keys and values.
|
||||
*
|
||||
* @returns {object}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
static _parseToPojo ({ opCode, berReader, pojo = {} }) {
|
||||
const protocolOp = berReader.readSequence()
|
||||
if (protocolOp !== opCode) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
const status = berReader.readEnumeration()
|
||||
const matchedDN = berReader.readString()
|
||||
const diagnosticMessage = berReader.readString()
|
||||
const referrals = []
|
||||
|
||||
if (berReader.peek() === operations.LDAP_RES_REFERRAL) {
|
||||
// Advance the offset to the start of the value and
|
||||
// put the sequence length into the `reader.length` field.
|
||||
berReader.readSequence(operations.LDAP_RES_REFERRAL)
|
||||
const end = berReader.length
|
||||
while (berReader.offset < end) {
|
||||
referrals.push(berReader.readString())
|
||||
}
|
||||
}
|
||||
|
||||
pojo.status = status
|
||||
pojo.matchedDN = matchedDN
|
||||
pojo.diagnosticMessage = diagnosticMessage
|
||||
pojo.referrals = referrals
|
||||
|
||||
return pojo
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LdapResult
|
||||
283
node_modules/@ldapjs/messages/lib/ldap-result.test.js
generated
vendored
Normal file
283
node_modules/@ldapjs/messages/lib/ldap-result.test.js
generated
vendored
Normal file
@ -0,0 +1,283 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const warning = require('./deprecations')
|
||||
const { resultCodes, operations } = require('@ldapjs/protocol')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const {
|
||||
addResponseBasicBytes,
|
||||
addResponseNoSuchObjectBytes,
|
||||
addResponseReferralsBytes,
|
||||
extensionDisconnectionNotificationResponseBytes
|
||||
} = require('./messages/_fixtures/message-byte-arrays')
|
||||
const RECOGNIZED_OIDS = require('./messages/extension-utils/recognized-oids')
|
||||
const LdapResult = require('./ldap-result')
|
||||
const ExtensionResponse = require('./messages/extension-response')
|
||||
|
||||
// Silence the standard warning logs. We will test the messages explicitly.
|
||||
process.removeAllListeners('warning')
|
||||
|
||||
tap.test('constructor', t => {
|
||||
t.test('no args', async t => {
|
||||
const res = new LdapResult()
|
||||
t.equal(res.status, 0)
|
||||
t.equal(res.matchedDN, '')
|
||||
t.strictSame(res.referrals, [])
|
||||
t.equal(res.diagnosticMessage, '')
|
||||
})
|
||||
|
||||
t.test('emits warning for abandonID', t => {
|
||||
process.on('warning', handler)
|
||||
t.teardown(async () => {
|
||||
process.removeListener('warning', handler)
|
||||
warning.emitted.set('LDAP_MESSAGE_DEP_004', false)
|
||||
})
|
||||
|
||||
const res = new LdapResult({
|
||||
errorMessage: 'foo'
|
||||
})
|
||||
t.ok(res)
|
||||
|
||||
function handler (error) {
|
||||
t.equal(
|
||||
error.message,
|
||||
'errorMessage is deprecated. Use diagnosticMessage instead.'
|
||||
)
|
||||
t.end()
|
||||
}
|
||||
})
|
||||
|
||||
t.test('with options', async t => {
|
||||
const res = new LdapResult({
|
||||
status: 1,
|
||||
matchedDN: 'foo',
|
||||
referrals: ['foo.example.com'],
|
||||
diagnosticMessage: 'bar'
|
||||
})
|
||||
t.equal(res.status, 1)
|
||||
t.equal(res.matchedDN, 'foo')
|
||||
t.strictSame(res.referrals, ['foo.example.com'])
|
||||
t.equal(res.diagnosticMessage, 'bar')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.diagnosticMessage', t => {
|
||||
t.test('sets and gets', async t => {
|
||||
const res = new LdapResult()
|
||||
t.equal(res.diagnosticMessage, '')
|
||||
res.diagnosticMessage = 'foo'
|
||||
t.equal(res.diagnosticMessage, 'foo')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.matchedDN', t => {
|
||||
t.test('sets and gets', async t => {
|
||||
const res = new LdapResult()
|
||||
t.equal(res.matchedDN, '')
|
||||
res.matchedDN = 'foo'
|
||||
t.equal(res.matchedDN, 'foo')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.pojo', t => {
|
||||
t.test('returns a plain JavaScript object', async t => {
|
||||
const res = new LdapResult()
|
||||
t.strictSame(res.pojo, {
|
||||
status: 0,
|
||||
matchedDN: '',
|
||||
diagnosticMessage: '',
|
||||
referrals: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('returns a plain JavaScript object from subclass', async t => {
|
||||
class Foo extends LdapResult {
|
||||
_pojo (obj) {
|
||||
obj.foo = 'foo'
|
||||
return obj
|
||||
}
|
||||
}
|
||||
|
||||
const res = new Foo()
|
||||
t.strictSame(res.pojo, {
|
||||
status: 0,
|
||||
matchedDN: '',
|
||||
diagnosticMessage: '',
|
||||
referrals: [],
|
||||
foo: 'foo'
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.referrals', t => {
|
||||
t.test('gets', async t => {
|
||||
const res = new LdapResult({ referrals: ['foo'] })
|
||||
t.strictSame(res.referrals, ['foo'])
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.status', t => {
|
||||
t.test('sets and gets', async t => {
|
||||
const res = new LdapResult()
|
||||
t.equal(res.status, 0)
|
||||
res.status = 1
|
||||
t.equal(res.status, 1)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.type', t => {
|
||||
t.test('gets', async t => {
|
||||
const res = new LdapResult()
|
||||
t.equal(res.type, 'LdapResult')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('addReferral', t => {
|
||||
t.test('adds to existing list', async t => {
|
||||
const res = new LdapResult({ referrals: ['foo'] })
|
||||
t.strictSame(res.referrals, ['foo'])
|
||||
res.addReferral('bar')
|
||||
t.strictSame(res.referrals, ['foo', 'bar'])
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_toBer', t => {
|
||||
t.test('returns basic bytes', async t => {
|
||||
const res = new LdapResult({
|
||||
protocolOp: operations.LDAP_RES_ADD,
|
||||
messageId: 2
|
||||
})
|
||||
const ber = res.toBer()
|
||||
const expected = Buffer.from(addResponseBasicBytes)
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.test('returns bytes with referrals', async t => {
|
||||
const res = new LdapResult({
|
||||
protocolOp: operations.LDAP_RES_ADD,
|
||||
messageId: 3,
|
||||
status: resultCodes.REFERRAL,
|
||||
diagnosticMessage: 'This server is read-only. Try a different one.',
|
||||
referrals: [
|
||||
'ldap://alternate1.example.com:389/uid=jdoe,ou=Remote,dc=example,dc=com',
|
||||
'ldap://alternate2.example.com:389/uid=jdoe,ou=Remote,dc=example,dc=com'
|
||||
]
|
||||
})
|
||||
const ber = res.toBer()
|
||||
const expected = Buffer.from(addResponseReferralsBytes)
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.test('hands off to _writeResponse', async t => {
|
||||
const res = new ExtensionResponse({
|
||||
protocolOp: operations.LDAP_RES_EXTENSION,
|
||||
messageId: 0,
|
||||
status: resultCodes.UNAVAILABLE,
|
||||
diagnosticMessage: 'The Directory Server is shutting down',
|
||||
referrals: [],
|
||||
responseName: RECOGNIZED_OIDS.get('DISCONNECTION_NOTIFICATION')
|
||||
})
|
||||
const ber = res.toBer()
|
||||
const expected = Buffer.from(extensionDisconnectionNotificationResponseBytes)
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws because not implemented', async t => {
|
||||
const expected = Error('Use LdapMessage.parse, or a specific message type\'s parseToPojo, instead.')
|
||||
t.throws(
|
||||
() => LdapResult.parseToPojo(),
|
||||
expected
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#_parseToPojo', async t => {
|
||||
t.test('throws if protocol op is wrong', async t => {
|
||||
const bytes = addResponseBasicBytes.slice(5)
|
||||
bytes[0] = 0x68
|
||||
const berReader = new BerReader(Buffer.from(bytes))
|
||||
t.throws(
|
||||
() => LdapResult._parseToPojo({
|
||||
opCode: operations.LDAP_RES_ADD,
|
||||
berReader
|
||||
}),
|
||||
Error('found wrong protocol operation: 0x68')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('parses a basic object', async t => {
|
||||
const bytes = addResponseBasicBytes.slice(5)
|
||||
const berReader = new BerReader(Buffer.from(bytes))
|
||||
const pojo = { foo: 'foo' }
|
||||
LdapResult._parseToPojo({
|
||||
opCode: operations.LDAP_RES_ADD,
|
||||
berReader,
|
||||
pojo
|
||||
})
|
||||
t.strictSame(pojo, {
|
||||
status: 0,
|
||||
matchedDN: '',
|
||||
diagnosticMessage: '',
|
||||
referrals: [],
|
||||
foo: 'foo'
|
||||
})
|
||||
})
|
||||
|
||||
t.test('parses object with matched dn and diagnostic message', async t => {
|
||||
const bytes = addResponseNoSuchObjectBytes.slice(6)
|
||||
const berReader = new BerReader(Buffer.from(bytes))
|
||||
const pojo = LdapResult._parseToPojo({
|
||||
opCode: operations.LDAP_RES_ADD,
|
||||
berReader
|
||||
})
|
||||
t.strictSame(pojo, {
|
||||
status: resultCodes.NO_SUCH_OBJECT,
|
||||
referrals: [],
|
||||
matchedDN: 'ou=People, dc=example, dc=com',
|
||||
diagnosticMessage: [
|
||||
'Entry uid=missing1, ou=missing2, ou=People, dc=example, dc=com cannot',
|
||||
' be created because its parent does not exist.'
|
||||
].join('')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('parses object with referrals', async t => {
|
||||
const bytes = addResponseReferralsBytes.slice(6)
|
||||
const berReader = new BerReader(Buffer.from(bytes))
|
||||
const pojo = LdapResult._parseToPojo({
|
||||
opCode: operations.LDAP_RES_ADD,
|
||||
berReader
|
||||
})
|
||||
t.strictSame(pojo, {
|
||||
status: resultCodes.REFERRAL,
|
||||
referrals: [
|
||||
'ldap://alternate1.example.com:389/uid=jdoe,ou=Remote,dc=example,dc=com',
|
||||
'ldap://alternate2.example.com:389/uid=jdoe,ou=Remote,dc=example,dc=com'
|
||||
],
|
||||
matchedDN: '',
|
||||
diagnosticMessage: 'This server is read-only. Try a different one.'
|
||||
})
|
||||
})
|
||||
})
|
||||
828
node_modules/@ldapjs/messages/lib/messages/_fixtures/message-byte-arrays.js
generated
vendored
Normal file
828
node_modules/@ldapjs/messages/lib/messages/_fixtures/message-byte-arrays.js
generated
vendored
Normal file
@ -0,0 +1,828 @@
|
||||
'use strict'
|
||||
|
||||
// The byte arrays in this file are used by the
|
||||
// `parseToMessage` function test suite. Any byte block
|
||||
// added to this file will automatically get picked up by
|
||||
// that test suite. Thus, any byte block added here should be
|
||||
// parseable, and serializable, by the generic LDAP object
|
||||
// type associated with that byte block.
|
||||
|
||||
module.exports.abandonRequestBytes = [
|
||||
0x30, 0x06, // sequence, 6 bytes
|
||||
0x02, 0x01, 0x06, // message id (integer value "6")
|
||||
0x50, 0x01, 0x05 // abandon request protocol op (application primitive integer 5)
|
||||
]
|
||||
|
||||
/**
|
||||
* Technically, this is nonsense. The spec does not define an abandon response.
|
||||
* We are making something up here just to test the ldapjs specific response
|
||||
* object.
|
||||
*/
|
||||
module.exports.abandonResponseBytes = [
|
||||
0x30, 0x0c, // start sequence, 12 bytes
|
||||
0x02, 0x01, 0x01, // message id (integer value 1)
|
||||
0x00, 0x07, // protocol op (0x61) bind response
|
||||
0x0a, 0x01, 0x00, // success result code (enumerated value 0)
|
||||
0x04, 0x00, // no matched DN (0-byte octet string)
|
||||
0x04, 0x00 // no diagnostic message (0-byte octet string)
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a basic ADD response.
|
||||
* Taken from https://web.archive.org/web/20220630073105/https://nawilson.com/ldapv3-wire-protocol-reference-add/
|
||||
*/
|
||||
module.exports.addResponseBasicBytes = [
|
||||
0x30, 0x0c, // sequence, 12 bytes
|
||||
0x02, 0x01, 0x02, // message id 2
|
||||
0x69, 0x07, // add response op, 7 bytes
|
||||
0x0a, 0x01, 0x00, // success result code (enumerated value 0)
|
||||
0x04, 0x00, // no matched DN (0-byte octet string)
|
||||
0x04, 0x00 // no diagnostic message (0-byte octet string)
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents an ADD response with an error and a diagnostic message.
|
||||
* Taken from https://web.archive.org/web/20220630073105/https://nawilson.com/ldapv3-wire-protocol-reference-add/
|
||||
*/
|
||||
module.exports.addResponseNoSuchObjectBytes = [
|
||||
0x30, 0x81, 0x9d, // sequence, 157 bytes
|
||||
0x02, 0x01, 0x03, // message id 3
|
||||
0x69, 0x81, 0x97, // add response op, 151 bytes
|
||||
0x0a, 0x01, 0x20, // noSuchObject result code (enumerated value 32)
|
||||
|
||||
0x04, 0x1d, // octet string, 29 bytes (matched dn)
|
||||
0x6f, 0x75, 0x3d, 0x50, 0x65, 0x6f, 0x70, 0x6c, // "ou=Peopl"
|
||||
0x65, 0x2c, 0x20, 0x64, 0x63, 0x3d, 0x65, 0x78, // "e, dc=ex"
|
||||
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x20, 0x64, // "ample, d"
|
||||
0x63, 0x3d, 0x63, 0x6f, 0x6d, // "c=com"
|
||||
|
||||
0x04, 0x73, // octet string, 115 bytes (diagnostic message)
|
||||
// "Entry uid=missing1, ou=missing2, ou=People, dc=example, dc=com cannot be
|
||||
// created because its parent does not exist."
|
||||
0x45, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x75, 0x69,
|
||||
0x64, 0x3d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e,
|
||||
0x67, 0x31, 0x2c, 0x20, 0x6f, 0x75, 0x3d, 0x6d,
|
||||
0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x32, 0x2c,
|
||||
0x20, 0x6f, 0x75, 0x3d, 0x50, 0x65, 0x6f, 0x70,
|
||||
0x6c, 0x65, 0x2c, 0x20, 0x64, 0x63, 0x3d, 0x65,
|
||||
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x20,
|
||||
0x64, 0x63, 0x3d, 0x63, 0x6f, 0x6d, 0x20, 0x63,
|
||||
0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65,
|
||||
0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64,
|
||||
0x20, 0x62, 0x65, 0x63, 0x61, 0x75, 0x73, 0x65,
|
||||
0x20, 0x69, 0x74, 0x73, 0x20, 0x70, 0x61, 0x72,
|
||||
0x65, 0x6e, 0x74, 0x20, 0x64, 0x6f, 0x65, 0x73,
|
||||
0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x78, 0x69,
|
||||
0x73, 0x74, 0x2e
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents an ADD response with referrals.
|
||||
* Taken from https://web.archive.org/web/20220630073105/https://nawilson.com/ldapv3-wire-protocol-reference-add/
|
||||
*/
|
||||
module.exports.addResponseReferralsBytes = [
|
||||
0x30, 0x81, 0xcf, // sequence, 207 bytes
|
||||
0x02, 0x01, 0x03, // message id 3
|
||||
0x69, 0x81, 0xc9, // add response op, 201 bytes
|
||||
0x0a, 0x01, 0x0a, // referral result code (enumerated value 10)
|
||||
0x04, 0x00, // no matched dn (0-byte octet string)
|
||||
|
||||
0x04, 0x2f, // octet string, 47 bytes (diagnostic message)
|
||||
// "This server is read-only. Try a different one."
|
||||
0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x65, 0x72,
|
||||
0x76, 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x72,
|
||||
0x65, 0x61, 0x64, 0x2d, 0x6f, 0x6e, 0x6c, 0x79,
|
||||
0x2e, 0x20, 0x20, 0x54, 0x72, 0x79, 0x20, 0x61,
|
||||
0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65,
|
||||
0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x2e,
|
||||
|
||||
0xa3, 0x81, 0x90, // referrals sequence, 144 bytes
|
||||
0x04, 0x46, // string, 70 bytes (first url)
|
||||
// "ldap://alternate1.example.com:389/uid=jdoe,ou=Remote,dc=example,dc=com"
|
||||
0x6c, 0x64, 0x61, 0x70, 0x3a, 0x2f, 0x2f, 0x61,
|
||||
0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65,
|
||||
0x31, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
|
||||
0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3a, 0x33, 0x38,
|
||||
0x39, 0x2f, 0x75, 0x69, 0x64, 0x3d, 0x6a, 0x64,
|
||||
0x6f, 0x65, 0x2c, 0x6f, 0x75, 0x3d, 0x52, 0x65,
|
||||
0x6d, 0x6f, 0x74, 0x65, 0x2c, 0x64, 0x63, 0x3d,
|
||||
0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c,
|
||||
0x64, 0x63, 0x3d, 0x63, 0x6f, 0x6d,
|
||||
|
||||
0x04, 0x46, // string, 70 bytes (second url)
|
||||
// "ldap://alternate2.example.com:389/uid=jdoe,ou=Remote,dc=example,dc=com"
|
||||
0x6c, 0x64, 0x61, 0x70, 0x3a, 0x2f, 0x2f, 0x61,
|
||||
0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65,
|
||||
0x32, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
|
||||
0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3a, 0x33, 0x38,
|
||||
0x39, 0x2f, 0x75, 0x69, 0x64, 0x3d, 0x6a, 0x64,
|
||||
0x6f, 0x65, 0x2c, 0x6f, 0x75, 0x3d, 0x52, 0x65,
|
||||
0x6d, 0x6f, 0x74, 0x65, 0x2c, 0x64, 0x63, 0x3d,
|
||||
0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c,
|
||||
0x64, 0x63, 0x3d, 0x63, 0x6f, 0x6d
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents an ADD request with attributes.
|
||||
* Taken from https://web.archive.org/web/20220630073105/https://nawilson.com/ldapv3-wire-protocol-reference-add/
|
||||
*/
|
||||
module.exports.addRequestBytes = [
|
||||
0x30, 0x49, // start sequence, 73 bytes
|
||||
0x02, 0x01, 0x02, // message id 2
|
||||
0x68, 0x44, // add op, 68 bytes
|
||||
|
||||
0x04, 0x11, // entry dn string, 17 bytes
|
||||
0x64, 0x63, 0x3d, 0x65, // "dc=example,dc=com"
|
||||
0x78, 0x61, 0x6d, 0x70,
|
||||
0x6c, 0x65, 0x2c, 0x64,
|
||||
0x63, 0x3d, 0x63, 0x6f,
|
||||
0x6d,
|
||||
|
||||
0x30, 0x2f, // start attributes sequence, 47 bytes
|
||||
0x30, 0x1c, // start first attribute sequence, 28 bytes
|
||||
|
||||
0x04, 0x0b, // string, 11 bytes
|
||||
0x6f, 0x62, 0x6a, 0x65, // "objectclass"
|
||||
0x63, 0x74, 0x63, 0x6c,
|
||||
0x61, 0x73, 0x73,
|
||||
0x31, 0x0d, // start value sequence, 13 bytes
|
||||
0x04, 0x03, 0x74, 0x6f, 0x70, // string: "top"
|
||||
0x04, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, // string: "domain"
|
||||
|
||||
0x30, 0x0f, // start second attribute sequence, 15 bytes
|
||||
0x04, 0x02, 0x64, 0x63, // string: "dc"
|
||||
0x31, 0x09, // start second value sequence, 9 bytes
|
||||
0x04, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65 // string: "example"
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents an LDAP BIND Request Message as defined by
|
||||
* RFC 4511 §4.2. It has a message id of "1" and attempts to bind
|
||||
* with simple authentication as user "uid=admin,ou=sys" with the password
|
||||
* "secret".
|
||||
*/
|
||||
module.exports.bindRequestBytes = [
|
||||
0x30, 0x25, // sequence, 37 bytes
|
||||
0x02, 0x01, 0x01, // message id (integer value "1")
|
||||
0x60, 0x20, // protocol op (0x60), 32 bytes
|
||||
0x02, 0x01, 0x03, // version (integer value "3")
|
||||
0x04, 0x13, // string, 19 bytes
|
||||
0x75, 0x69, 0x64, 0x3d, // "uid="
|
||||
0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2c, // "admin,"
|
||||
0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, // "ou=sys"
|
||||
0x74, 0x65, 0x6d, // "tem"
|
||||
0x80, 0x06, // auth choice 0 ("simple"), 6 bytes
|
||||
0x73, 0x65, 0x63, 0x72, 0x65, 0x74 // "secret"
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents an anonymous BIND request.
|
||||
*/
|
||||
module.exports.bindRequestAnonymousBytes = [
|
||||
0x30, 0x0c,
|
||||
0x02, 0x01, 0x01,
|
||||
0x60, 0x07,
|
||||
0x02, 0x01, 0x03,
|
||||
0x04, 0x00,
|
||||
0x80, 0x00
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a BIND response with attributes.
|
||||
* Taken from https://web.archive.org/web/20220518232654/https://nawilson.com/ldapv3-wire-protocol-reference-bind/
|
||||
*/
|
||||
module.exports.bindResponseBytes = [
|
||||
0x30, 0x0c, // start sequence, 12 bytes
|
||||
0x02, 0x01, 0x01, // message id (integer value 1)
|
||||
0x61, 0x07, // protocol op (0x61) bind response
|
||||
0x0a, 0x01, 0x00, // success result code (enumerated value 0)
|
||||
0x04, 0x00, // no matched DN (0-byte octet string)
|
||||
0x04, 0x00 // no diagnostic message (0-byte octet string)
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a COMPARE request with attributes.
|
||||
* Taken from https://web.archive.org/web/20220630080454/https://nawilson.com/ldapv3-wire-protocol-reference-compare/
|
||||
*/
|
||||
module.exports.compareRequestBytes = [
|
||||
0x30, 0x45, // start sequence, 69 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x6e, 0x40, // protocol op (0x6e) compare request
|
||||
|
||||
0x04, 0x24, // string (target entry dn), 36 bytes
|
||||
// "uid=jdoe,ou=People,dc=example,dc=com")
|
||||
0x75, 0x69, 0x64, 0x3d, 0x6a, 0x64, 0x6f, 0x65,
|
||||
0x2c, 0x6f, 0x75, 0x3d, 0x50, 0x65, 0x6f, 0x70,
|
||||
0x6c, 0x65, 0x2c, 0x64, 0x63, 0x3d, 0x65, 0x78,
|
||||
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x64, 0x63,
|
||||
0x3d, 0x63, 0x6f, 0x6d,
|
||||
|
||||
0x30, 0x18, // sequence, 24 bytes
|
||||
0x04, 0x0c, // string (attribute name), 12 bytes
|
||||
// "employeeType"
|
||||
0x65, 0x6d, 0x70, 0x6c,
|
||||
0x6f, 0x79, 0x65, 0x65,
|
||||
0x54, 0x79, 0x70, 0x65,
|
||||
|
||||
0x04, 0x08, // string (assertion value), 8 bytes
|
||||
// "salaried"
|
||||
0x73, 0x61, 0x6c, 0x61,
|
||||
0x72, 0x69, 0x65, 0x64
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a COMPARE response with attributes.
|
||||
* Taken from https://web.archive.org/web/20220630080454/https://nawilson.com/ldapv3-wire-protocol-reference-compare/
|
||||
*/
|
||||
module.exports.compareResponseBytes = [
|
||||
0x30, 0x0c, // start sequence, 12 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x6f, 0x07, // protocol op (0x6f), 7 bytes
|
||||
0x0a, 0x01, 0x06, // compareTrue result code (enumerated value 6)
|
||||
0x04, 0x00, // No matched DN (0-byte octet string)
|
||||
0x04, 0x00 // No diagnostic message (0-byte octet string)
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a DELETE request with controls supplied for recursive removal.
|
||||
* Taken from https://web.archive.org/web/20220629203240/https://ldap.com/ldapv3-wire-protocol-reference-ldap-message/#ldapmessage-example
|
||||
*/
|
||||
module.exports.deleteRequestBytes = [
|
||||
0x30, 0x35, // start sequence, 53 bytes
|
||||
0x02, 0x01, 0x05, // message id 5
|
||||
0x4a, 0x11, // delete request op (entry), 17 bytes
|
||||
0x64, 0x63, 0x3d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, // dc=exampl
|
||||
0x65, 0x2c, 0x64, 0x63, 0x3d, 0x63, 0x6f, 0x6d, // e,dc=com
|
||||
|
||||
0xa0, 0x1d, // start sequence (controls), 29 bytes
|
||||
0x30, 0x1b, // start first control sequence, 27 bytes
|
||||
0x04, 0x16, 0x31, 0x2e, // control OID (octet string):
|
||||
0x32, 0x2e, 0x38, 0x34, // 1.2.840.113556.1.4.805
|
||||
0x30, 0x2e, 0x31, 0x31,
|
||||
0x33, 0x35, 0x35, 0x36,
|
||||
0x2e, 0x31, 0x2e, 0x34,
|
||||
0x2e, 0x38, 0x30, 0x35,
|
||||
0x01, 0x01, 0xff // control criticality (boolean true)
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a DELETE response.
|
||||
* Taken from https://web.archive.org/web/20220518193408/https://nawilson.com/ldapv3-wire-protocol-reference-delete/
|
||||
*/
|
||||
module.exports.deleteResponseBytes = [
|
||||
0x30, 0x0c, // start sequence, 12 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x6b, 0x07, // protocol op (0x6b), 7 bytes
|
||||
0x0a, 0x01, 0x00, // success result code (enumerated value 0)
|
||||
0x04, 0x00, // no matched DN (0-byte octet string)
|
||||
0x04, 0x00 // no diagnostic message (0-byte octet string)
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a simple EXTENSION request with a string name
|
||||
* and string value.
|
||||
*/
|
||||
module.exports.extensionNameAndValueRequestBytes = [
|
||||
0x30, 0x18, // start sequence, 24 bytes
|
||||
0x02, 0x01, 0x01, // message ID (integer value 1)
|
||||
0x77, 0x13, // protocol op (0x77), 19 bytes
|
||||
|
||||
0x80, 0x09, // string (request name), 9 bytes
|
||||
// "1.3.6.1.1"
|
||||
0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x31,
|
||||
|
||||
0x81, 0x06, // string (request value), 6 bytes
|
||||
// "foobar"
|
||||
0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a simple EXTENSION request with a string name
|
||||
* and no value.
|
||||
*/
|
||||
module.exports.extensionNameAndNoValueRequestBytes = [
|
||||
0x30, 0x10, // start sequence, 16 bytes
|
||||
0x02, 0x01, 0x01, // message ID (integer value 1)
|
||||
0x77, 0x0b, // protocol op (0x77), 11 bytes
|
||||
|
||||
0x80, 0x09, // string (request name), 9 bytes
|
||||
// "1.3.6.1.1"
|
||||
0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x31
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents an EXTENSION request to modify a password.
|
||||
* Does not include a new password.
|
||||
* Taken from https://web.archive.org/web/20220518193613/https://nawilson.com/ldapv3-wire-protocol-reference-extended/
|
||||
*/
|
||||
module.exports.extensionChangePasswordRequestBytes = [
|
||||
0x30, 0x53, // start sequence, 83 bytes
|
||||
0x02, 0x01, 0x01, // message ID (integer value 1)
|
||||
0x77, 0x4e, // protocol op (0x77), 78 bytes
|
||||
|
||||
0x80, 0x17, // string (request name; "oid"), 23 bytes
|
||||
// "1.3.6.1.4.1.4203.1.11.1"
|
||||
0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, // The extended request OID
|
||||
0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33,
|
||||
0x2e, 0x31, 0x2e, 0x31, 0x31, 0x2e, 0x31,
|
||||
|
||||
0x81, 0x33, // sequence (request value), 51 bytes
|
||||
0x30, 0x31, // sequence, 49 bytes
|
||||
|
||||
0x80, 0x24, // extension specific string (entry), 36 bytes
|
||||
// "uid=jdoe,ou=People,dc=example,dc=com"
|
||||
0x75, 0x69, 0x64, 0x3d, 0x6a, 0x64, 0x6f, 0x65,
|
||||
0x2c, 0x6f, 0x75, 0x3d, 0x50, 0x65, 0x6f, 0x70,
|
||||
0x6c, 0x65, 0x2c, 0x64, 0x63, 0x3d, 0x65, 0x78,
|
||||
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x64, 0x63,
|
||||
0x3d, 0x63, 0x6f, 0x6d,
|
||||
|
||||
0x81, 0x09, // extension specific string (value), 9 bytes
|
||||
// "secret123"
|
||||
0x73, 0x65, 0x63, 0x72,
|
||||
0x65, 0x74, 0x31, 0x32,
|
||||
0x33
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents an EXTENSION request to modify a password.
|
||||
* Only provides the new password.
|
||||
*/
|
||||
module.exports.extensionChangePasswordWithNewPasswordBytes = [
|
||||
0x30, 0x2d, // start sequence, 45 bytes
|
||||
0x02, 0x01, 0x01, // message ID (integer value 1)
|
||||
0x77, 0x28, // protocol op (0x77), 40 bytes
|
||||
|
||||
0x80, 0x17, // string (request name; "oid"), 23 bytes
|
||||
// "1.3.6.1.4.1.4203.1.11.1"
|
||||
0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, // The extended request OID
|
||||
0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33,
|
||||
0x2e, 0x31, 0x2e, 0x31, 0x31, 0x2e, 0x31,
|
||||
|
||||
0x81, 0x0d, // sequence (request value), 13 bytes
|
||||
0x30, 0x0b, // sequence, 11 bytes
|
||||
|
||||
0x82, 0x09, // extension specific string (value), 9 bytes
|
||||
// "secret123"
|
||||
0x73, 0x65, 0x63, 0x72,
|
||||
0x65, 0x74, 0x31, 0x32,
|
||||
0x33
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents an EXTENSION response that is an unsolicited response.
|
||||
* Taken from https://web.archive.org/web/20220518193613/https://nawilson.com/ldapv3-wire-protocol-reference-extended/
|
||||
*/
|
||||
module.exports.extensionDisconnectionNotificationResponseBytes = [
|
||||
0x30, 0x49, // start sequence, 73 bytes
|
||||
0x02, 0x01, 0x00, // message ID (integer value 0)
|
||||
0x78, 0x44, // protocol op (0x78), 68 bytes
|
||||
0x0a, 0x01, 0x34, // unavailable result code (enumerated value 52)
|
||||
0x04, 0x00, // no matched DN (0-byte octet string)
|
||||
|
||||
0x04, 0x25, // string (diagnostic message), 37 bytes
|
||||
// "The Directory Server is shutting down"
|
||||
0x54, 0x68, 0x65, 0x20, 0x44, 0x69, 0x72, 0x65,
|
||||
0x63, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x53, 0x65,
|
||||
0x72, 0x76, 0x65, 0x72, 0x20, 0x69, 0x73, 0x20,
|
||||
0x73, 0x68, 0x75, 0x74, 0x74, 0x69, 0x6e, 0x67,
|
||||
0x20, 0x64, 0x6f, 0x77, 0x6e,
|
||||
|
||||
0x8a, 0x16, // string (oid)
|
||||
// "1.3.6.1.4.1.1466.20036"
|
||||
0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e,
|
||||
0x34, 0x2e, 0x31, 0x2e, 0x31, 0x34, 0x36, 0x36,
|
||||
0x2e, 0x32, 0x30, 0x30, 0x33, 0x36
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents an EXTENSION request for a Who Am I? request.
|
||||
* Taken from https://www.rfc-editor.org/rfc/rfc4532#section-2.1
|
||||
*/
|
||||
module.exports.extensionWhoAmIRequestBytes = [
|
||||
0x30, 0x1e, // start sequence, 30 bytes
|
||||
0x02, 0x01, 0x02, // message id, 2
|
||||
0x77, 0x19, // protocol op (0x77), 25 bytes
|
||||
0x80, 0x17, // string (oid), 23 bytes
|
||||
// "1.3.6.1.4.1.4203.1.11.3"
|
||||
0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31,
|
||||
0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32,
|
||||
0x30, 0x33, 0x2e, 0x31, 0x2e, 0x31, 0x31,
|
||||
0x2e, 0x33
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a CANCEL request as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc3909#section-2.1.
|
||||
*/
|
||||
module.exports.extensionCancelRequestBytes = [
|
||||
0x30, 0x19, // start sequence, 25 bytes
|
||||
0x02, 0x01, 0x02, // message id, 2
|
||||
0x77, 0x14, // protocol op (0x77), 20 bytes
|
||||
|
||||
0x80, 0x0b, // string (oid), 11 bytes
|
||||
0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e,
|
||||
0x31, 0x2e, 0x31, 0x2e, 0x38,
|
||||
|
||||
0x81, 0x05, // value sequence, 5 bytes
|
||||
0x30, 0x03, // sequence, 3 bytes
|
||||
0x02, 0x01, 0x01 // message id 1
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a CANCEL response as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc3909#section-2.2.
|
||||
*/
|
||||
module.exports.extensionCancelResponseBytes = [
|
||||
0x30, 0x0c, // sequence, 12 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer 2)
|
||||
0x78, 0x07, // protocol op (0x78), 7 bytes
|
||||
0x0a, 0x01, 0x76, // status code, 118 (canceled)
|
||||
0x04, 0x00, // no matched dn
|
||||
0x04, 0x00 // no diagnostic message
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a Start TLS request as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.14.1.
|
||||
*
|
||||
* Taken from
|
||||
* https://web.archive.org/web/20220518193613/https://nawilson.com/ldapv3-wire-protocol-reference-extended/
|
||||
*/
|
||||
module.exports.extenstionStartTLSRequestBytes = [
|
||||
0x30, 0x1d, // sequence, 29 bytes
|
||||
0x02, 0x01, 0x01, // message id (integer value 1)
|
||||
0x77, 0x18, // protocol op (0x77), 24 bytes
|
||||
|
||||
0x80, 0x16, // name sequence, 22 bytes
|
||||
// "1.3.6.1.4.1.1466.20037"
|
||||
0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e,
|
||||
0x34, 0x2e, 0x31, 0x2e, 0x31, 0x34, 0x36, 0x36,
|
||||
0x2e, 0x32, 0x30, 0x30, 0x33, 0x37
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a Start TLS response as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.14.2.
|
||||
*
|
||||
* Taken from
|
||||
* https://web.archive.org/web/20220518193613/https://nawilson.com/ldapv3-wire-protocol-reference-extended/
|
||||
*/
|
||||
module.exports.extensionStartTLSResponseBytes = [
|
||||
0x30, 0x24, // sequence, 36 bytes
|
||||
0x02, 0x01, 0x01, // message id (integer value 1)
|
||||
0x78, 0x1f, // protocol op (0x78), 31 bytes
|
||||
0x0a, 0x01, 0x00, // success result code (enumerated value 0)
|
||||
0x04, 0x00, // no matched dn (0-byte octet string)
|
||||
0x04, 0x00, // do diagnostic message (0-byte octet string)
|
||||
|
||||
0x8a, 0x16, // value string, 22 bytes
|
||||
// "1.3.6.1.4.1.1466.20037"
|
||||
0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e,
|
||||
0x34, 0x2e, 0x31, 0x2e, 0x31, 0x34, 0x36, 0x36,
|
||||
0x2e, 0x32, 0x30, 0x30, 0x33, 0x37
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a MODIFY request.
|
||||
* Taken from https://web.archive.org/web/20220518184303/https://nawilson.com/ldapv3-wire-protocol-reference-modify/.
|
||||
*/
|
||||
module.exports.modifyRequestBytes = [
|
||||
0x30, 0x81, 0x80, // sequence start, 128 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x66, 0x7b, // protocol op (0x66), 123 bytes
|
||||
|
||||
0x04, 0x24, // string, 36 bytes
|
||||
// "uid=jdoe,ou=People,dc=example,dc=com"
|
||||
0x75, 0x69, 0x64, 0x3d, 0x6a, 0x64, 0x6f, 0x65,
|
||||
0x2c, 0x6f, 0x75, 0x3d, 0x50, 0x65, 0x6f, 0x70,
|
||||
0x6c, 0x65, 0x2c, 0x64, 0x63, 0x3d, 0x65, 0x78,
|
||||
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x64, 0x63,
|
||||
0x3d, 0x63, 0x6f, 0x6d,
|
||||
|
||||
0x30, 0x53, // start sequence (modifications), 83 bytes
|
||||
|
||||
0x30, 0x18, // sequence, 24 bytes
|
||||
0x0a, 0x01, 0x01, // delete modification type (enumerated value 1)
|
||||
0x30, 0x13, // sequence, 19 bytes
|
||||
0x04, 0x09, // string, 9 bytes
|
||||
// "givenName"
|
||||
0x67, 0x69, 0x76, 0x65,
|
||||
0x6e, 0x4e, 0x61, 0x6d,
|
||||
0x65,
|
||||
0x31, 0x06, // string, 6 bytes
|
||||
// "John"
|
||||
0x04, 0x04, 0x4a,
|
||||
0x6f, 0x68, 0x6e,
|
||||
|
||||
0x30, 0x1c, // sequence, 28 bytes
|
||||
0x0a, 0x01, 0x00, // add modification type (enumerated value 0)
|
||||
0x30, 0x17, // sequence, 23 bytes
|
||||
0x04, 0x09, // string, 9 bytes
|
||||
// "givenName"
|
||||
0x67, 0x69, 0x76, 0x65,
|
||||
0x6e, 0x4e, 0x61, 0x6d,
|
||||
0x65,
|
||||
0x31, 0x0a, // string, 10 bytes
|
||||
// "Jonathan"
|
||||
0x04, 0x08, 0x4a, 0x6f, 0x6e,
|
||||
0x61, 0x74, 0x68, 0x61, 0x6e,
|
||||
|
||||
0x30, 0x19, // sequence, 25 bytes
|
||||
0x0a, 0x01, 0x02, // replace modification type (enumerated value 2)
|
||||
0x30, 0x14, // sequence, 20 bytes
|
||||
0x04, 0x02, // string, 2 bytes
|
||||
// "cn"
|
||||
0x63, 0x6e,
|
||||
0x31, 0x0e, // string, 14 bytes
|
||||
// "Jonathan Doe"
|
||||
0x04, 0x0c, 0x4a, 0x6f,
|
||||
0x6e, 0x61, 0x74, 0x68,
|
||||
0x61, 0x6e, 0x20, 0x44,
|
||||
0x6f, 0x65
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a MODIFY response.
|
||||
* Taken from https://web.archive.org/web/20220518184303/https://nawilson.com/ldapv3-wire-protocol-reference-modify/.
|
||||
*/
|
||||
module.exports.modifyResponseBytes = [
|
||||
0x30, 0x0c, // sequence, 12 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x67, 0x07, // protocol op (0x67), 7 bytes
|
||||
0x0a, 0x01, 0x00, // success result code (enumerated value 0)
|
||||
0x04, 0x00, // no matched dn
|
||||
0x04, 0x00 // no diagnostic message
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a MODIFYDN request.
|
||||
* Taken from https://web.archive.org/web/20220630073359/https://nawilson.com/ldapv3-wire-protocol-reference-modify-dn/.
|
||||
*/
|
||||
module.exports.modifyDnRequestBytes = [
|
||||
0x30, 0x58, // sequence, 88 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x6c, 0x53, // protocol op (0x6c), 83 bytes
|
||||
|
||||
0x04, 0x24, // string, 36 bytes
|
||||
// "uid=jdoe,ou=People,dc=example,dc=com"
|
||||
0x75, 0x69, 0x64, 0x3d, 0x6a, 0x64, 0x6f, 0x65,
|
||||
0x2c, 0x6f, 0x75, 0x3d, 0x50, 0x65, 0x6f, 0x70,
|
||||
0x6c, 0x65, 0x2c, 0x64, 0x63, 0x3d, 0x65, 0x78,
|
||||
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x64, 0x63,
|
||||
0x3d, 0x63, 0x6f, 0x6d,
|
||||
|
||||
0x04, 0x0c, // string, 12 bytes
|
||||
// "uid=john.doe"
|
||||
0x75, 0x69, 0x64, 0x3d,
|
||||
0x6a, 0x6f, 0x68, 0x6e,
|
||||
0x2e, 0x64, 0x6f, 0x65,
|
||||
|
||||
0x01, 0x01, 0xff, // Delete the old RDN value (boolean true)
|
||||
|
||||
0x80, 0x1a, // context specific string, 26 bytes
|
||||
// "ou=Users,dc=example,dc=com"
|
||||
0x6f, 0x75, 0x3d, 0x55, 0x73, 0x65, 0x72, 0x73,
|
||||
0x2c, 0x64, 0x63, 0x3d, 0x65, 0x78, 0x61, 0x6d,
|
||||
0x70, 0x6c, 0x65, 0x2c, 0x64, 0x63, 0x3d, 0x63,
|
||||
0x6f, 0x6d
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a MODIFYDN response.
|
||||
* Taken from https://web.archive.org/web/20220630073359/https://nawilson.com/ldapv3-wire-protocol-reference-modify-dn/.
|
||||
*/
|
||||
module.exports.modifyDnResponseBytes = [
|
||||
0x30, 0x0c, // sequence, 12 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x6d, 0x07, // protocol op (0x6d), 7 bytes
|
||||
0x0a, 0x01, 0x00, // success result code (enumerated value 0)
|
||||
0x04, 0x00, // no matched dn
|
||||
0x04, 0x00 // no diagnostic message
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents an UNBIND request.
|
||||
* Taken from https://web.archive.org/web/20220518231541/https://nawilson.com/ldapv3-wire-protocol-reference-unbind/.
|
||||
*/
|
||||
module.exports.unbindRequestBytes = [
|
||||
0x30, 0x05, // sequence, 5 bytes
|
||||
0x02, 0x01, 0x03, // message id (integer value 3)
|
||||
0x42, 0x00 // protocol op (0x42), 0 bytes
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a SEARCHREQUEST request.
|
||||
* Taken from https://web.archive.org/web/20220518215838/https://nawilson.com/ldapv3-wire-protocol-reference-search/.
|
||||
*/
|
||||
module.exports.searchRequestBytes = [
|
||||
0x30, 0x56, // sequence, 86 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x63, 0x51, // protocol op, 81 bytes
|
||||
|
||||
0x04, 0x11, // string sequence, 17 bytes
|
||||
// "dc=example,dc=com"
|
||||
0x64, 0x63, 0x3d, 0x65,
|
||||
0x78, 0x61, 0x6d, 0x70,
|
||||
0x6c, 0x65, 0x2c, 0x64,
|
||||
0x63, 0x3d, 0x63, 0x6f,
|
||||
0x6d,
|
||||
|
||||
0x0a, 0x01, 0x02, // wholeSubtree scope (enumerated value 2)
|
||||
|
||||
0x0a, 0x01, 0x00, // neverDerefAliases (enumerated value 0)
|
||||
|
||||
0x02, 0x02, 0x03, 0xe8, // size limit (integer value 1000)
|
||||
|
||||
0x02, 0x01, 0x1e, // time limit (integer value 30)
|
||||
|
||||
0x01, 0x01, 0x00, // typesOnly (boolean false)
|
||||
|
||||
0xa0, 0x24, // begin AND filter, 36 bytes
|
||||
0xa3, 0x15, // begin an EQUALITY filter, 21 bytes
|
||||
0x04, 0x0b, // string, 11 bytes
|
||||
// "objectClass"
|
||||
0x6f, 0x62, 0x6a, 0x65,
|
||||
0x63, 0x74, 0x43, 0x6c,
|
||||
0x61, 0x73, 0x73,
|
||||
0x04, 0x06, // string, 6 bytes
|
||||
// "person"
|
||||
0x70, 0x65, 0x72, 0x73,
|
||||
0x6f, 0x6e, // The assertion value (octet string "person")
|
||||
0xa3, 0x0b, // begin an EQUALITY filter, 11 bytes
|
||||
0x04, 0x03, // string, 3 bytes
|
||||
// "uid"
|
||||
0x75, 0x69, 0x64,
|
||||
0x04, 0x04, // string, 4 bytes
|
||||
// "jdoe"
|
||||
0x6a, 0x64, 0x6f, 0x65,
|
||||
|
||||
0x30, 0x06, // begin the set of requested attributes, 6 bytes
|
||||
0x04, 0x01, // string, 1 byte
|
||||
// "*"
|
||||
0x2a,
|
||||
0x04, 0x01, // string, 1 byte
|
||||
// "+"
|
||||
0x2b
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a SEARCHRESULTENTRY response.
|
||||
* Taken from https://web.archive.org/web/20220518215838/https://nawilson.com/ldapv3-wire-protocol-reference-search/.
|
||||
*/
|
||||
module.exports.searchResultEntryBytes = [
|
||||
0x30, 0x49, // sequence, 73 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x64, 0x44, // protocol op (0x64), 68 bytes
|
||||
|
||||
0x04, 0x11, // string, 17 bytes
|
||||
// "dc=example,dc=com"
|
||||
0x64, 0x63, 0x3d, 0x65,
|
||||
0x78, 0x61, 0x6d, 0x70,
|
||||
0x6c, 0x65, 0x2c, 0x64,
|
||||
0x63, 0x3d, 0x63, 0x6f,
|
||||
0x6d,
|
||||
|
||||
0x30, 0x2f, // sequence, 47 bytes (attributes list)
|
||||
0x30, 0x1c, // sequence, 28 bytes (first attribute)
|
||||
0x04, 0x0b, // string, 11 bytes
|
||||
// "objectClass"
|
||||
0x6f, 0x62, 0x6a, 0x65,
|
||||
0x63, 0x74, 0x43, 0x6c,
|
||||
0x61, 0x73, 0x73,
|
||||
0x31, 0x0d, // set sequence, 13 bytes
|
||||
0x04, 0x03, // string, 3 bytes
|
||||
// "top"
|
||||
0x74, 0x6f, 0x70,
|
||||
0x04, 0x06, // string, 6 bytes
|
||||
// "domain"
|
||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||
|
||||
0x30, 0x0f, // sequence, 15 bytes (second attribute)
|
||||
0x04, 0x02, // string, 2 bytes
|
||||
// "dc"
|
||||
0x64, 0x63,
|
||||
0x31, 0x09, // set sequence, 9 bytes
|
||||
0x04, 0x07, // string, 7 bytes
|
||||
// "example"
|
||||
0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a SEARCHRESULTENTRY response that only returns attribute names.
|
||||
* Taken from https://web.archive.org/web/20220518215838/https://nawilson.com/ldapv3-wire-protocol-reference-search/.
|
||||
*/
|
||||
module.exports.searchResultEntryNoValuesBytes = [
|
||||
0x30, 0x33, // sequence, 51 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x64, 0x2e, // protocol op (0x64), 46 bytes
|
||||
|
||||
0x04, 0x11, // string, 17 bytes
|
||||
// "dc=example,dc=com"
|
||||
0x64, 0x63, 0x3d, 0x65,
|
||||
0x78, 0x61, 0x6d, 0x70,
|
||||
0x6c, 0x65, 0x2c, 0x64,
|
||||
0x63, 0x3d, 0x63, 0x6f,
|
||||
0x6d,
|
||||
|
||||
0x30, 0x19, // sequence, 25 bytes (attributes list)
|
||||
0x30, 0x0f, // sequence, 15 bytes (first attribute)
|
||||
0x04, 0x0b, // string, 11 bytes
|
||||
// "objectClass"
|
||||
0x6f, 0x62, 0x6a, 0x65,
|
||||
0x63, 0x74, 0x43, 0x6c,
|
||||
0x61, 0x73, 0x73,
|
||||
0x31, 0x00, // sequence, 0 bytes
|
||||
|
||||
0x30, 0x06, // sequence, 6 bytes (second attribute)
|
||||
0x04, 0x02, // string, 2 bytes
|
||||
// "dc"
|
||||
0x64, 0x63,
|
||||
0x31, 0x00 // sequence, 0 bytes
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a SEARCHRESULTREFERENCE response.
|
||||
* Taken from https://web.archive.org/web/20220518215838/https://nawilson.com/ldapv3-wire-protocol-reference-search/.
|
||||
*/
|
||||
module.exports.searchResultReferenceBytes = [
|
||||
0x30, 0x6d, // sequence, 109 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x73, 0x68, // protocol op (0x73), 104 bytes
|
||||
|
||||
0x04, 0x32, // string, 50 bytes
|
||||
// "ldap://ds1.example.com:389/dc=example,dc=com??sub?"
|
||||
0x6c, 0x64, 0x61, 0x70, 0x3a, 0x2f, 0x2f, 0x64,
|
||||
0x73, 0x31, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70,
|
||||
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3a, 0x33,
|
||||
0x38, 0x39, 0x2f, 0x64, 0x63, 0x3d, 0x65, 0x78,
|
||||
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x64, 0x63,
|
||||
0x3d, 0x63, 0x6f, 0x6d, 0x3f, 0x3f, 0x73, 0x75,
|
||||
0x62, 0x3f,
|
||||
|
||||
0x04, 0x32, // string, 50 bytes
|
||||
// "ldap://ds2.example.com:389/dc=example,dc=com??sub?"
|
||||
0x6c, 0x64, 0x61, 0x70, 0x3a, 0x2f, 0x2f, 0x64,
|
||||
0x73, 0x32, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70,
|
||||
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3a, 0x33,
|
||||
0x38, 0x39, 0x2f, 0x64, 0x63, 0x3d, 0x65, 0x78,
|
||||
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x64, 0x63,
|
||||
0x3d, 0x63, 0x6f, 0x6d, 0x3f, 0x3f, 0x73, 0x75,
|
||||
0x62, 0x3f
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents a SEARCHRESULTDONE response.
|
||||
* Taken from https://web.archive.org/web/20220518215838/https://nawilson.com/ldapv3-wire-protocol-reference-search/.
|
||||
*/
|
||||
module.exports.searchResultDoneBytes = [
|
||||
0x30, 0x0c, // sequence, 12 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x65, 0x07, // protocol op (0x65), 7 bytes
|
||||
0x0a, 0x01, 0x00, // success result code (enumerated value 0)
|
||||
0x04, 0x00, // no matched DN (0-byte octet string)
|
||||
0x04, 0x00 // no diagnostic message (0-byte octet string)
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents an INTERMEDIATE response with a name and value.
|
||||
*/
|
||||
module.exports.intermediateResponseBytes = [
|
||||
0x30, 0x11, // sequence, 17 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x79, 0x0c, // protocol op (0x79), 12 bytes
|
||||
|
||||
0x80, 0x5, // string, 5 bytes
|
||||
// "1.2.3"
|
||||
0x31, 0x2e, 0x32, 0x2e, 0x33,
|
||||
|
||||
0x81, 0x03, // string, 3 bytes
|
||||
// "foo"
|
||||
0x66, 0x6f, 0x6f
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents an INTERMEDIATE response with a name but no value.
|
||||
*/
|
||||
module.exports.intermediateResponseNoValueBytes = [
|
||||
0x30, 0x0c, // sequence, 12 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x79, 0x07, // protocol op (0x79), 12 bytes
|
||||
|
||||
0x80, 0x5, // string, 5 bytes
|
||||
// "1.2.3"
|
||||
0x31, 0x2e, 0x32, 0x2e, 0x33
|
||||
]
|
||||
|
||||
/**
|
||||
* Represents an INTERMEDIATE response with a value but no name.
|
||||
*/
|
||||
module.exports.intermediateResponseNoNameBytes = [
|
||||
0x30, 0x0a, // sequence, 10 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer value 2)
|
||||
0x79, 0x05, // protocol op (0x79), 5 bytes
|
||||
|
||||
0x81, 0x03, // string, 3 bytes
|
||||
// "foo"
|
||||
0x66, 0x6f, 0x6f
|
||||
]
|
||||
104
node_modules/@ldapjs/messages/lib/messages/abandon-request.js
generated
vendored
Normal file
104
node_modules/@ldapjs/messages/lib/messages/abandon-request.js
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
'use strict'
|
||||
|
||||
const LdapMessage = require('../ldap-message')
|
||||
const Protocol = require('@ldapjs/protocol')
|
||||
const warning = require('../deprecations')
|
||||
|
||||
/**
|
||||
* Implements the abandon request message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.11
|
||||
*/
|
||||
class AbandonRequest extends LdapMessage {
|
||||
#abandonId
|
||||
|
||||
/**
|
||||
* @typedef {LdapMessageOptions} AbandonRequestOptions
|
||||
* @property {number} [abandonId=0] The message id of the request to abandon.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {AbandonRequestOptions} [options]
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = Protocol.operations.LDAP_REQ_ABANDON
|
||||
super(options)
|
||||
|
||||
const abandonId = options.abandonId || options.abandonID || 0
|
||||
if (options.abandonID) {
|
||||
warning.emit('LDAP_MESSAGE_DEP_003')
|
||||
}
|
||||
this.#abandonId = abandonId
|
||||
}
|
||||
|
||||
/**
|
||||
* The identifier for the request that the instance will request be abandoned.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get abandonId () {
|
||||
return this.#abandonId
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link abandonId} instead.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
get abandonID () {
|
||||
warning.emit('LDAP_MESSAGE_DEP_003')
|
||||
return this.#abandonId
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'AbandonRequest'
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.writeInt(this.#abandonId, Protocol.operations.LDAP_REQ_ABANDON)
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {object}
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
_pojo (obj = {}) {
|
||||
obj.abandonId = this.#abandonId
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const protocolOp = ber.peek()
|
||||
if (protocolOp !== Protocol.operations.LDAP_REQ_ABANDON) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
const abandonId = ber.readInt(Protocol.operations.LDAP_REQ_ABANDON)
|
||||
return { protocolOp, abandonId }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AbandonRequest
|
||||
125
node_modules/@ldapjs/messages/lib/messages/abandon-request.test.js
generated
vendored
Normal file
125
node_modules/@ldapjs/messages/lib/messages/abandon-request.test.js
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const warning = require('../deprecations')
|
||||
const AbandonRequest = require('./abandon-request')
|
||||
|
||||
// Silence the standard warning logs. We will test the messages explicitly.
|
||||
process.removeAllListeners('warning')
|
||||
|
||||
const { abandonRequestBytes } = require('./_fixtures/message-byte-arrays')
|
||||
|
||||
tap.test('basic', t => {
|
||||
t.test('constructor no args', async t => {
|
||||
const req = new AbandonRequest()
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: 0x50,
|
||||
type: 'AbandonRequest',
|
||||
abandonId: 0,
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('emits warning for abandonID', t => {
|
||||
process.on('warning', handler)
|
||||
t.teardown(async () => {
|
||||
process.removeListener('warning', handler)
|
||||
warning.emitted.set('LDAP_MESSAGE_DEP_003', false)
|
||||
})
|
||||
|
||||
const req = new AbandonRequest({
|
||||
abandonID: 1
|
||||
})
|
||||
t.ok(req)
|
||||
|
||||
function handler (error) {
|
||||
t.equal(
|
||||
error.message,
|
||||
'abandonID is deprecated. Use abandonId instead.'
|
||||
)
|
||||
t.end()
|
||||
}
|
||||
})
|
||||
|
||||
t.test('properties return correct values', t => {
|
||||
process.on('warning', handler)
|
||||
t.teardown(async () => {
|
||||
process.removeListener('warning', handler)
|
||||
warning.emitted.set('LDAP_MESSAGE_DEP_003', false)
|
||||
})
|
||||
|
||||
const req = new AbandonRequest({
|
||||
abandonId: 1
|
||||
})
|
||||
|
||||
t.equal(req.abandonId, 1)
|
||||
t.equal(req.abandonID, 1)
|
||||
|
||||
function handler (error) {
|
||||
t.equal(
|
||||
error.message,
|
||||
'abandonID is deprecated. Use abandonId instead.'
|
||||
)
|
||||
t.end()
|
||||
}
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('toBer', t => {
|
||||
t.test('converts AbandonRequest to BER', async t => {
|
||||
const reqBuffer = Buffer.from(abandonRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
const message = AbandonRequest.parse(reader)
|
||||
|
||||
const ber = message.toBer()
|
||||
t.equal('[object BerReader]', Object.prototype.toString.call(ber))
|
||||
t.equal(reqBuffer.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_pojo', t => {
|
||||
t.test('returns implementation properties', async t => {
|
||||
const message = new AbandonRequest()
|
||||
const pojo = message._pojo()
|
||||
t.strictSame(pojo, { abandonId: 0 })
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws if operation incorrect', async t => {
|
||||
const reqBuffer = Buffer.from(abandonRequestBytes)
|
||||
reqBuffer[5] = 0x61
|
||||
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
t.throws(
|
||||
() => AbandonRequest.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x61')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const reqBuffer = Buffer.from(abandonRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = AbandonRequest.parseToPojo(reader)
|
||||
t.strictSame(pojo, {
|
||||
protocolOp: 0x50,
|
||||
abandonId: 5
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
43
node_modules/@ldapjs/messages/lib/messages/abandon-response.js
generated
vendored
Normal file
43
node_modules/@ldapjs/messages/lib/messages/abandon-response.js
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
'use strict'
|
||||
|
||||
const LdapResult = require('../ldap-result')
|
||||
|
||||
/**
|
||||
* Implements the ldapjs specific ABANDON response object.
|
||||
*/
|
||||
class AbandonResponse extends LdapResult {
|
||||
/**
|
||||
* @param {LdapResultOptions} options
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = 0x00
|
||||
super(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'AbandonResponse'
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
return LdapResult._parseToPojo({
|
||||
opCode: 0x00,
|
||||
berReader: ber
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AbandonResponse
|
||||
53
node_modules/@ldapjs/messages/lib/messages/abandon-response.test.js
generated
vendored
Normal file
53
node_modules/@ldapjs/messages/lib/messages/abandon-response.test.js
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const {
|
||||
abandonResponseBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
const AbandonResponse = require('./abandon-response')
|
||||
|
||||
tap.test('basic', async t => {
|
||||
const res = new AbandonResponse()
|
||||
t.equal(res.protocolOp, 0x00)
|
||||
t.equal(res.type, 'AbandonResponse')
|
||||
})
|
||||
|
||||
tap.test('toBer', t => {
|
||||
tap.test('returns basic bytes', async t => {
|
||||
const res = new AbandonResponse({ messageId: 1 })
|
||||
const ber = res.toBer()
|
||||
const expected = Buffer.from(abandonResponseBytes)
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('parses a basic object', async t => {
|
||||
const bytes = abandonResponseBytes.slice(5)
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
const pojo = AbandonResponse.parseToPojo(reader)
|
||||
t.strictSame(pojo, {
|
||||
status: 0,
|
||||
matchedDN: '',
|
||||
diagnosticMessage: '',
|
||||
referrals: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('throws if protocol op is wrong', async t => {
|
||||
const bytes = abandonResponseBytes.slice(5)
|
||||
bytes[0] = 0x68
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
t.throws(
|
||||
() => AbandonResponse.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x68')
|
||||
)
|
||||
})
|
||||
|
||||
t.comment('see the LdapResult test suite for further tests')
|
||||
|
||||
t.end()
|
||||
})
|
||||
277
node_modules/@ldapjs/messages/lib/messages/add-request.js
generated
vendored
Normal file
277
node_modules/@ldapjs/messages/lib/messages/add-request.js
generated
vendored
Normal file
@ -0,0 +1,277 @@
|
||||
'use strict'
|
||||
|
||||
const LdapMessage = require('../ldap-message')
|
||||
const Attribute = require('@ldapjs/attribute')
|
||||
const Protocol = require('@ldapjs/protocol')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
|
||||
/**
|
||||
* Implements the add request message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.7
|
||||
*/
|
||||
class AddRequest extends LdapMessage {
|
||||
/**
|
||||
* Path to the LDAP object.
|
||||
*
|
||||
* @type {null | import('@ldapjs/dn').DN}
|
||||
*/
|
||||
#entry
|
||||
|
||||
/**
|
||||
* A set of attribute objects.
|
||||
*
|
||||
* @type {import('@ldapjs/attribute')[]}
|
||||
*/
|
||||
#attributes = []
|
||||
|
||||
/**
|
||||
* @typedef {LdapMessageOptions} AddRequestOptions
|
||||
* @property {string} [entry=null] The path to the LDAP object.
|
||||
* @property {import('@ldapjs/attribute')[]} [attributes=[]] A set of
|
||||
* attributes to store at the `entry` path.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {AddRequestOptions} [options]
|
||||
*
|
||||
* @throws When the provided attributes list is invalid.
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = Protocol.operations.LDAP_REQ_ADD
|
||||
super(options)
|
||||
|
||||
this.entry = options.entry || null
|
||||
this.attributes = options.attributes || []
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of the attributes associated with the request.
|
||||
*
|
||||
* @returns {import('@ldapjs/attribute')[]}
|
||||
*/
|
||||
get attributes () {
|
||||
return this.#attributes.slice(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the attributes to be added to the entry. Replaces any existing
|
||||
* attributes.
|
||||
*
|
||||
* @param {import('@ldapjs/attribute')[]} attrs
|
||||
*
|
||||
* @throws If the input is not an array, or any element is not an
|
||||
* {@link Attribute} or attribute-like object.
|
||||
*/
|
||||
set attributes (attrs) {
|
||||
if (Array.isArray(attrs) === false) {
|
||||
throw Error('attrs must be an array')
|
||||
}
|
||||
const newAttrs = []
|
||||
for (const attr of attrs) {
|
||||
if (Attribute.isAttribute(attr) === false) {
|
||||
throw Error('attr must be an Attribute instance or Attribute-like object')
|
||||
}
|
||||
if (Object.prototype.toString.call(attr) !== '[object LdapAttribute]') {
|
||||
newAttrs.push(new Attribute(attr))
|
||||
continue
|
||||
}
|
||||
newAttrs.push(attr)
|
||||
}
|
||||
this.#attributes = newAttrs
|
||||
}
|
||||
|
||||
/**
|
||||
* The directory path to the object to add.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get entry () {
|
||||
return this.#entry ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the entry path to the LDAP object.
|
||||
*
|
||||
* @param {string | import('@ldapjs/dn').DN} path
|
||||
*/
|
||||
set entry (path) {
|
||||
if (path === null) return
|
||||
if (typeof path === 'string') {
|
||||
this.#entry = DN.fromString(path)
|
||||
} else if (Object.prototype.toString.call(path) === '[object LdapDn]') {
|
||||
this.#entry = path
|
||||
} else {
|
||||
throw Error('entry must be a valid DN string or instance of LdapDn')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of {@link entry}.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get _dn () {
|
||||
return this.entry
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'AddRequest'
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new {@link Attribute} to the list of request attributes.
|
||||
*
|
||||
* @param {import('@ldapjs/attribute')} attr
|
||||
*
|
||||
* @throws When the input is not an {@link Attribute} instance.
|
||||
*/
|
||||
addAttribute (attr) {
|
||||
if (Object.prototype.toString.call(attr) !== '[object LdapAttribute]') {
|
||||
throw Error('attr must be an instance of Attribute')
|
||||
}
|
||||
|
||||
this.#attributes.push(attr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of attribute names for the attributes in the
|
||||
* request.
|
||||
*
|
||||
* @returns {string[]}
|
||||
*/
|
||||
attributeNames () {
|
||||
return this.#attributes.map(attr => attr.type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an attribute by name from the attributes associated with
|
||||
* the request.
|
||||
*
|
||||
* @param {string} attributeName
|
||||
*
|
||||
* @returns {import('@ldapjs/attribute')|null}
|
||||
*
|
||||
* @throws When `attributeName` is not a string.
|
||||
*/
|
||||
getAttribute (attributeName) {
|
||||
if (typeof attributeName !== 'string') {
|
||||
throw Error('attributeName must be a string')
|
||||
}
|
||||
|
||||
for (const attr of this.#attributes) {
|
||||
if (attr.type === attributeName) {
|
||||
return attr
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of an {@link Attribute} in the request's
|
||||
* attribute set.
|
||||
*
|
||||
* @param {string} attributeName
|
||||
*
|
||||
* @returns {number} The index of the attribute, or `-1` if not
|
||||
* found.
|
||||
*
|
||||
* @throws When `attributeName` is not a string.
|
||||
*/
|
||||
indexOf (attributeName) {
|
||||
if (typeof attributeName !== 'string') {
|
||||
throw Error('attributeName must be a string')
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.#attributes.length; i += 1) {
|
||||
if (this.#attributes[i].type === attributeName) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.startSequence(Protocol.operations.LDAP_REQ_ADD)
|
||||
ber.writeString(this.#entry.toString())
|
||||
ber.startSequence()
|
||||
for (const attr of this.#attributes) {
|
||||
const attrBer = attr.toBer()
|
||||
ber.appendBuffer(attrBer.buffer)
|
||||
}
|
||||
ber.endSequence()
|
||||
ber.endSequence()
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {object}
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
_pojo (obj = {}) {
|
||||
obj.entry = this.#entry ? this.#entry.toString() : null
|
||||
obj.attributes = []
|
||||
for (const attr of this.#attributes) {
|
||||
obj.attributes.push(attr.pojo)
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const protocolOp = ber.readSequence()
|
||||
if (protocolOp !== Protocol.operations.LDAP_REQ_ADD) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
const entry = ber.readString()
|
||||
const attributes = []
|
||||
|
||||
// Advance to the first attribute sequence in the set
|
||||
// of attribute sequences.
|
||||
ber.readSequence()
|
||||
|
||||
const endOfAttributesPos = ber.offset + ber.length
|
||||
while (ber.offset < endOfAttributesPos) {
|
||||
const attribute = Attribute.fromBer(ber)
|
||||
attribute.type = attribute.type.toLowerCase()
|
||||
|
||||
if (attribute.type === 'objectclass') {
|
||||
for (let i = 0; i < attribute.values.length; i++) {
|
||||
attribute.values[i] = attribute.values[i].toLowerCase()
|
||||
}
|
||||
}
|
||||
|
||||
attributes.push(attribute)
|
||||
}
|
||||
|
||||
return { protocolOp, entry, attributes }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AddRequest
|
||||
316
node_modules/@ldapjs/messages/lib/messages/add-request.test.js
generated
vendored
Normal file
316
node_modules/@ldapjs/messages/lib/messages/add-request.test.js
generated
vendored
Normal file
@ -0,0 +1,316 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const Attribute = require('@ldapjs/attribute')
|
||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
const AddRequest = require('./add-request')
|
||||
|
||||
const { addRequestBytes } = require('./_fixtures/message-byte-arrays')
|
||||
|
||||
tap.test('basic', t => {
|
||||
t.test('constructor no args', async t => {
|
||||
const req = new AddRequest()
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: 0x68,
|
||||
type: 'AddRequest',
|
||||
entry: null,
|
||||
attributes: [],
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('constructor with args', async t => {
|
||||
const req = new AddRequest({
|
||||
entry: 'dn=foo,dc=example,dc=com',
|
||||
attributes: [{
|
||||
type: 'cn',
|
||||
values: ['foo']
|
||||
}]
|
||||
})
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: 0x68,
|
||||
type: 'AddRequest',
|
||||
entry: 'dn=foo,dc=example,dc=com',
|
||||
attributes: [{
|
||||
type: 'cn',
|
||||
values: ['foo']
|
||||
}],
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.attributes', t => {
|
||||
t.test('returns a copy of the attributes', async t => {
|
||||
const inputAttrs = [{ type: 'cn', values: ['foo'] }]
|
||||
const req = new AddRequest({
|
||||
entry: 'cn=foo',
|
||||
attributes: inputAttrs
|
||||
})
|
||||
const attrs = req.attributes
|
||||
t.not(attrs, inputAttrs)
|
||||
t.equal(attrs.length, inputAttrs.length)
|
||||
})
|
||||
|
||||
t.test('replaces attributes list', async t => {
|
||||
const req = new AddRequest({
|
||||
entry: 'cn=foo',
|
||||
attributes: [new Attribute({ type: 'cn', values: ['foo'] })]
|
||||
})
|
||||
t.strictSame(req.attributes[0].pojo, { type: 'cn', values: ['foo'] })
|
||||
|
||||
req.attributes = [new Attribute({ type: 'sn', values: ['bar'] })]
|
||||
t.strictSame(req.attributes[0].pojo, { type: 'sn', values: ['bar'] })
|
||||
})
|
||||
|
||||
t.test('throws if not an array', async t => {
|
||||
const input = { type: 'cn', values: ['foo'] }
|
||||
t.throws(
|
||||
() => new AddRequest({ entry: 'cn=foo', attributes: input }),
|
||||
Error('attrs must be an array')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('throws if attribute is invalid', async t => {
|
||||
const input = [{ type: 42, values: ['foo'] }]
|
||||
t.throws(
|
||||
() => new AddRequest({ entry: 'cn=foo', attributes: input }),
|
||||
Error('attr must be an Attribute instance or Attribute-like object')
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.entry', t => {
|
||||
t.test('gets and sets', async t => {
|
||||
let req = new AddRequest({ entry: 'cn=foo' })
|
||||
t.equal(req.entry.toString(), 'cn=foo')
|
||||
t.equal(req._dn.toString(), 'cn=foo')
|
||||
|
||||
req.entry = 'sn=bar'
|
||||
t.equal(req.entry.toString(), 'sn=bar')
|
||||
t.equal(req._dn.toString(), 'sn=bar')
|
||||
|
||||
req.entry = DN.fromString('cn=baz')
|
||||
t.equal(req.entry.toString(), 'cn=baz')
|
||||
|
||||
req = new AddRequest()
|
||||
t.equal(req.entry, null)
|
||||
})
|
||||
|
||||
t.test('throws for bad value', async t => {
|
||||
const req = new AddRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.entry = { cn: 'foo' }
|
||||
},
|
||||
'entry must be a valid DN string or instance of LdapDn'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('addAttribute', t => {
|
||||
t.test('throws for invalid input', async t => {
|
||||
const req = new AddRequest({ entry: 'cn=foo' })
|
||||
t.throws(
|
||||
() => req.addAttribute({ type: 'foo', values: ['foo'] }),
|
||||
Error('attr must be an instance of Attribute')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('adds an attribute', async t => {
|
||||
const req = new AddRequest({ entry: 'cn=foo' })
|
||||
req.addAttribute(new Attribute({ type: 'bar', values: ['baz'] }))
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: 0x68,
|
||||
type: 'AddRequest',
|
||||
entry: 'cn=foo',
|
||||
attributes: [{
|
||||
type: 'bar',
|
||||
values: ['baz']
|
||||
}],
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('attributeNames', t => {
|
||||
t.test('returns the names list', async t => {
|
||||
const req = new AddRequest({
|
||||
entry: 'cn=foo',
|
||||
attributes: [
|
||||
new Attribute({ type: 'bar' }),
|
||||
new Attribute({ type: 'baz' }),
|
||||
new Attribute({ type: 'foobar' }),
|
||||
new Attribute({ type: 'barfoo' })
|
||||
]
|
||||
})
|
||||
t.strictSame(req.attributeNames(), [
|
||||
'bar',
|
||||
'baz',
|
||||
'foobar',
|
||||
'barfoo'
|
||||
])
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('getAttribute', t => {
|
||||
t.test('throws for invalid parameter', async t => {
|
||||
const req = new AddRequest({ entry: 'cn=foo' })
|
||||
t.throws(
|
||||
() => req.getAttribute(42),
|
||||
Error('attributeName must be a string')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('returns null for not found', async t => {
|
||||
const req = new AddRequest({ entry: 'cn=foo' })
|
||||
t.equal(req.getAttribute('bar'), null)
|
||||
})
|
||||
|
||||
t.test('returns the correct attribute', async t => {
|
||||
const req = new AddRequest({
|
||||
entry: 'cn=foo',
|
||||
attributes: [
|
||||
new Attribute({ type: 'bar' }),
|
||||
new Attribute({ type: 'baz', values: ['baz', 'baz', 'baz'] }),
|
||||
new Attribute({ type: 'foobar' }),
|
||||
new Attribute({ type: 'barfoo' })
|
||||
]
|
||||
})
|
||||
const attr = req.getAttribute('baz')
|
||||
t.strictSame(attr.pojo, {
|
||||
type: 'baz',
|
||||
values: ['baz', 'baz', 'baz']
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('indexOf', t => {
|
||||
t.test('throws for invalid parameter', async t => {
|
||||
const req = new AddRequest({ entry: 'cn=foo' })
|
||||
t.throws(
|
||||
() => req.indexOf(42),
|
||||
Error('attributeName must be a string')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('finds an attribute', async t => {
|
||||
const req = new AddRequest({
|
||||
entry: 'cn=foo',
|
||||
attributes: [
|
||||
new Attribute({ type: 'bar' }),
|
||||
new Attribute({ type: 'baz' }),
|
||||
new Attribute({ type: 'foobar' }),
|
||||
new Attribute({ type: 'barfoo' })
|
||||
]
|
||||
})
|
||||
t.equal(req.indexOf('foobar'), 2)
|
||||
})
|
||||
|
||||
t.test('returns for not found', async t => {
|
||||
const req = new AddRequest({
|
||||
entry: 'cn=foo',
|
||||
attributes: [
|
||||
new Attribute({ type: 'bar' }),
|
||||
new Attribute({ type: 'baz' }),
|
||||
new Attribute({ type: 'foobar' }),
|
||||
new Attribute({ type: 'barfoo' })
|
||||
]
|
||||
})
|
||||
t.equal(req.indexOf('zifbang'), -1)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_toBer', t => {
|
||||
tap.test('converts instance to BER', async t => {
|
||||
const req = new AddRequest({
|
||||
entry: 'dc=example,dc=com',
|
||||
attributes: [
|
||||
new Attribute({ type: 'objectclass', values: ['top', 'domain'] }),
|
||||
new Attribute({ type: 'dc', values: ['example'] })
|
||||
]
|
||||
})
|
||||
const writer = new BerWriter()
|
||||
req._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(addRequestBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_pojo', t => {
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const req = new AddRequest({
|
||||
entry: 'cn=foo',
|
||||
attributes: [{ type: 'bar', values: ['baz'] }]
|
||||
})
|
||||
t.strictSame(req._pojo(), {
|
||||
entry: 'cn=foo',
|
||||
attributes: [{
|
||||
type: 'bar',
|
||||
values: ['baz']
|
||||
}]
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws if operation incorrect', async t => {
|
||||
const reqBuffer = Buffer.from(addRequestBytes)
|
||||
reqBuffer[5] = 0x61
|
||||
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
t.throws(
|
||||
() => AddRequest.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x61')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const reqBuffer = Buffer.from(addRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = AddRequest.parseToPojo(reader)
|
||||
t.equal(pojo.protocolOp, 0x68)
|
||||
t.equal(pojo.entry, 'dc=example,dc=com')
|
||||
t.strictSame(pojo.attributes[0].pojo, {
|
||||
type: 'objectclass',
|
||||
values: ['top', 'domain']
|
||||
})
|
||||
t.strictSame(pojo.attributes[1].pojo, {
|
||||
type: 'dc',
|
||||
values: ['example']
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
45
node_modules/@ldapjs/messages/lib/messages/add-response.js
generated
vendored
Normal file
45
node_modules/@ldapjs/messages/lib/messages/add-response.js
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
'use strict'
|
||||
|
||||
const LdapResult = require('../ldap-result')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
|
||||
/**
|
||||
* Implements the add response message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.7
|
||||
*/
|
||||
class AddResponse extends LdapResult {
|
||||
/**
|
||||
* @param {LdapResultOptions} options
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_RES_ADD
|
||||
super(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'AddResponse'
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
return LdapResult._parseToPojo({
|
||||
opCode: operations.LDAP_RES_ADD,
|
||||
berReader: ber
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AddResponse
|
||||
56
node_modules/@ldapjs/messages/lib/messages/add-response.test.js
generated
vendored
Normal file
56
node_modules/@ldapjs/messages/lib/messages/add-response.test.js
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const {
|
||||
addResponseBasicBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
const AddResponse = require('./add-response')
|
||||
|
||||
tap.test('basic', async t => {
|
||||
const res = new AddResponse()
|
||||
t.equal(res.protocolOp, operations.LDAP_RES_ADD)
|
||||
t.equal(res.type, 'AddResponse')
|
||||
})
|
||||
|
||||
tap.test('toBer', t => {
|
||||
tap.test('returns basic bytes', async t => {
|
||||
const res = new AddResponse({ messageId: 2 })
|
||||
const ber = res.toBer()
|
||||
const expected = Buffer.from(addResponseBasicBytes)
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.comment('see the LdapResult test suite for further tests')
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('parses a basic object', async t => {
|
||||
const bytes = addResponseBasicBytes.slice(5)
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
const pojo = AddResponse.parseToPojo(reader)
|
||||
t.strictSame(pojo, {
|
||||
status: 0,
|
||||
matchedDN: '',
|
||||
diagnosticMessage: '',
|
||||
referrals: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('throws if protocol op is wrong', async t => {
|
||||
const bytes = addResponseBasicBytes.slice(5)
|
||||
bytes[0] = 0x68
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
t.throws(
|
||||
() => AddResponse.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x68')
|
||||
)
|
||||
})
|
||||
|
||||
t.comment('see the LdapResult test suite for further tests')
|
||||
|
||||
t.end()
|
||||
})
|
||||
167
node_modules/@ldapjs/messages/lib/messages/bind-request.js
generated
vendored
Normal file
167
node_modules/@ldapjs/messages/lib/messages/bind-request.js
generated
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
'use strict'
|
||||
|
||||
const LdapMessage = require('../ldap-message')
|
||||
const Protocol = require('@ldapjs/protocol')
|
||||
const { BerTypes } = require('@ldapjs/asn1')
|
||||
|
||||
/**
|
||||
* Implements the bind request message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.2.
|
||||
*
|
||||
* The bind request is further defined by:
|
||||
* https://www.rfc-editor.org/rfc/rfc4513#section-5.
|
||||
*/
|
||||
class BindRequest extends LdapMessage {
|
||||
static SIMPLE_BIND = 'simple'
|
||||
static SASL_BIND = 'sasl'
|
||||
|
||||
#version = 0x03
|
||||
#name
|
||||
#authentication = BindRequest.SIMPLE_BIND
|
||||
#credentials = ''
|
||||
|
||||
/**
|
||||
* @typedef {LdapMessageOptions} BindRequestOptions
|
||||
* @property {number} [version=3] Version of the protocol being used.
|
||||
* @property {string} [name=null] The "username" (dn) to connect with.
|
||||
* @property {string} [authentication='simple'] The authentication
|
||||
* mechanism to use. Currently, only `simple` is supported.
|
||||
* @property {string} [credentials=''] The password to use.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {BindRequestOptions} [options]
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = Protocol.operations.LDAP_REQ_BIND
|
||||
super(options)
|
||||
|
||||
const {
|
||||
version = 0x03,
|
||||
name = null,
|
||||
authentication = BindRequest.SIMPLE_BIND,
|
||||
credentials = ''
|
||||
} = options
|
||||
this.#version = version
|
||||
this.#name = name
|
||||
this.#authentication = authentication
|
||||
this.#credentials = credentials
|
||||
}
|
||||
|
||||
/**
|
||||
* The authentication credentials for the request.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
get credentials () {
|
||||
return this.#credentials
|
||||
}
|
||||
|
||||
/**
|
||||
* The DN, or "username", that is to be used in the bind request.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get name () {
|
||||
return this.#name
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'BindRequest'
|
||||
}
|
||||
|
||||
/**
|
||||
* The version number that the bind request conforms to.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get version () {
|
||||
return this.#version
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link name} instead.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get _dn () {
|
||||
return this.#name
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.startSequence(Protocol.operations.LDAP_REQ_BIND)
|
||||
ber.writeInt(this.#version)
|
||||
ber.writeString(this.#name || '')
|
||||
// TODO add support for SASL et al
|
||||
ber.writeString(this.#credentials || '', BerTypes.Context)
|
||||
ber.endSequence()
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {object}
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
_pojo (obj = {}) {
|
||||
obj.version = this.#version
|
||||
obj.name = this.#name
|
||||
obj.authenticationType = this.#authentication
|
||||
obj.credentials = this.#credentials
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const protocolOp = ber.readSequence()
|
||||
if (protocolOp !== Protocol.operations.LDAP_REQ_BIND) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
const version = ber.readInt()
|
||||
const name = ber.readString()
|
||||
|
||||
const tag = ber.peek()
|
||||
|
||||
// TODO: add support for SASL et al
|
||||
if (tag !== BerTypes.Context) {
|
||||
// Currently only support 0x80. To support SASL, must support 0x83.
|
||||
const authType = tag.toString(16).padStart(2, '0')
|
||||
throw Error(`authentication 0x${authType} not supported`)
|
||||
}
|
||||
|
||||
const authentication = BindRequest.SIMPLE_BIND
|
||||
const credentials = ber.readString(BerTypes.Context)
|
||||
|
||||
return {
|
||||
protocolOp,
|
||||
version,
|
||||
name,
|
||||
authentication,
|
||||
credentials
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BindRequest
|
||||
128
node_modules/@ldapjs/messages/lib/messages/bind-request.test.js
generated
vendored
Normal file
128
node_modules/@ldapjs/messages/lib/messages/bind-request.test.js
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const BindRequest = require('./bind-request')
|
||||
|
||||
const {
|
||||
bindRequestBytes,
|
||||
bindRequestAnonymousBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
|
||||
tap.test('basic', t => {
|
||||
t.test('constructor no args', async t => {
|
||||
const req = new BindRequest()
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: 0x60,
|
||||
type: 'BindRequest',
|
||||
version: 3,
|
||||
name: null,
|
||||
authenticationType: 'simple',
|
||||
credentials: '',
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('properties return correct values', async t => {
|
||||
const req = new BindRequest({
|
||||
messageId: 1,
|
||||
version: 999,
|
||||
name: 'foobar',
|
||||
authentication: 'nonsense',
|
||||
credentials: 'secret'
|
||||
})
|
||||
|
||||
t.equal(req.credentials, 'secret')
|
||||
t.equal(req.name, 'foobar')
|
||||
t.equal(req.type, 'BindRequest')
|
||||
t.equal(req.version, 999)
|
||||
t.equal(req._dn, 'foobar')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('toBer', t => {
|
||||
t.test('converts BindRequest to BER', async t => {
|
||||
const reqBuffer = Buffer.from(bindRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
const message = BindRequest.parse(reader)
|
||||
|
||||
const ber = message.toBer()
|
||||
t.equal('[object BerReader]', Object.prototype.toString.call(ber))
|
||||
t.equal(reqBuffer.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.test('converts an anonymous bind request', async t => {
|
||||
const reqBuffer = Buffer.from(bindRequestAnonymousBytes)
|
||||
const message = new BindRequest()
|
||||
const ber = message.toBer()
|
||||
t.equal(reqBuffer.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_pojo', t => {
|
||||
t.test('returns implementation properties', async t => {
|
||||
const message = new BindRequest()
|
||||
const pojo = message._pojo()
|
||||
t.strictSame(pojo, {
|
||||
version: 3,
|
||||
name: null,
|
||||
authenticationType: 'simple',
|
||||
credentials: ''
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws if operation incorrect', async t => {
|
||||
const reqBuffer = Buffer.from(bindRequestBytes)
|
||||
reqBuffer[5] = 0x61
|
||||
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
t.throws(
|
||||
() => BindRequest.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x61')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('throws if simple auth credentials is tagged wrong', async t => {
|
||||
const reqBuffer = Buffer.from(bindRequestBytes)
|
||||
reqBuffer[31] = 0x83
|
||||
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
t.throws(
|
||||
() => BindRequest.parseToPojo(reader),
|
||||
Error('authentication 0x83 not supported')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const reqBuffer = Buffer.from(bindRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = BindRequest.parseToPojo(reader)
|
||||
t.strictSame(pojo, {
|
||||
protocolOp: 0x60,
|
||||
version: 3,
|
||||
name: 'uid=admin,ou=system',
|
||||
authentication: 'simple',
|
||||
credentials: 'secret'
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
45
node_modules/@ldapjs/messages/lib/messages/bind-response.js
generated
vendored
Normal file
45
node_modules/@ldapjs/messages/lib/messages/bind-response.js
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
'use strict'
|
||||
|
||||
const LdapResult = require('../ldap-result')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
|
||||
/**
|
||||
* Implements the bind response message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.2.2
|
||||
*/
|
||||
class BindResponse extends LdapResult {
|
||||
/**
|
||||
* @param {LdapResultOptions} options
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_RES_BIND
|
||||
super(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'BindResponse'
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
return LdapResult._parseToPojo({
|
||||
opCode: operations.LDAP_RES_BIND,
|
||||
berReader: ber
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BindResponse
|
||||
54
node_modules/@ldapjs/messages/lib/messages/bind-response.test.js
generated
vendored
Normal file
54
node_modules/@ldapjs/messages/lib/messages/bind-response.test.js
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const {
|
||||
bindResponseBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
const BindResponse = require('./bind-response')
|
||||
|
||||
tap.test('basic', async t => {
|
||||
const res = new BindResponse()
|
||||
t.equal(res.protocolOp, operations.LDAP_RES_BIND)
|
||||
t.equal(res.type, 'BindResponse')
|
||||
})
|
||||
|
||||
tap.test('toBer', t => {
|
||||
tap.test('returns basic bytes', async t => {
|
||||
const res = new BindResponse({ messageId: 1 })
|
||||
const ber = res.toBer()
|
||||
const expected = Buffer.from(bindResponseBytes)
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('parses a basic object', async t => {
|
||||
const bytes = bindResponseBytes.slice(5)
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
const pojo = BindResponse.parseToPojo(reader)
|
||||
t.strictSame(pojo, {
|
||||
status: 0,
|
||||
matchedDN: '',
|
||||
diagnosticMessage: '',
|
||||
referrals: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('throws if protocol op is wrong', async t => {
|
||||
const bytes = bindResponseBytes.slice(5)
|
||||
bytes[0] = 0x68
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
t.throws(
|
||||
() => BindResponse.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x68')
|
||||
)
|
||||
})
|
||||
|
||||
t.comment('see the LdapResult test suite for further tests')
|
||||
|
||||
t.end()
|
||||
})
|
||||
173
node_modules/@ldapjs/messages/lib/messages/compare-request.js
generated
vendored
Normal file
173
node_modules/@ldapjs/messages/lib/messages/compare-request.js
generated
vendored
Normal file
@ -0,0 +1,173 @@
|
||||
'use strict'
|
||||
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
const LdapMessage = require('../ldap-message')
|
||||
|
||||
/**
|
||||
* Implements the compare request message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.10.
|
||||
*/
|
||||
class CompareRequest extends LdapMessage {
|
||||
#attribute
|
||||
#entry
|
||||
#value
|
||||
|
||||
/**
|
||||
* @typedef {LdapMessageOptions} CompareRequestOptions
|
||||
* @property {string|null} [attribute] The attribute name to compare
|
||||
* against.
|
||||
* @property {string} [entry] The target LDAP entity whose attribute
|
||||
* will be compared.
|
||||
* @property {string} [value] The value of the attribute to compare.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {CompareRequestOptions} [options]
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_REQ_COMPARE
|
||||
super(options)
|
||||
|
||||
this.attribute = options.attribute || ''
|
||||
this.entry = options.entry || null
|
||||
this.value = options.value || ''
|
||||
}
|
||||
|
||||
/**
|
||||
* The property of an LDAP entry to compare against.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
get attribute () {
|
||||
return this.#attribute
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the LDAP entry property to compare against.
|
||||
*
|
||||
* @param {string} value
|
||||
*/
|
||||
set attribute (value) {
|
||||
this.#attribute = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The LDAP entry that will be inspected.
|
||||
*
|
||||
* @returns {string | null}
|
||||
*/
|
||||
get entry () {
|
||||
return this.#entry ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the LDAP entity to inspect.
|
||||
*
|
||||
* @param {string | null} value
|
||||
*/
|
||||
set entry (value) {
|
||||
if (value === null) return
|
||||
if (typeof value === 'string') {
|
||||
this.#entry = DN.fromString(value)
|
||||
} else if (Object.prototype.toString.call(value) === '[object LdapDn]') {
|
||||
this.#entry = value
|
||||
} else {
|
||||
throw Error('entry must be a valid DN string or instance of LdapDn')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'CompareRequest'
|
||||
}
|
||||
|
||||
/**
|
||||
* The value the attribute should be set to.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
get value () {
|
||||
return this.#value
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the value the attribute should match.
|
||||
*
|
||||
* @param {string} value
|
||||
*/
|
||||
set value (value) {
|
||||
this.#value = value
|
||||
}
|
||||
|
||||
get _dn () {
|
||||
return this.#entry
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.startSequence(operations.LDAP_REQ_COMPARE)
|
||||
|
||||
ber.writeString(this.#entry.toString())
|
||||
ber.startSequence()
|
||||
ber.writeString(this.#attribute)
|
||||
ber.writeString(this.#value)
|
||||
ber.endSequence()
|
||||
|
||||
ber.endSequence()
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {object}
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
_pojo (obj = {}) {
|
||||
obj.attribute = this.#attribute
|
||||
obj.entry = this.#entry ? this.#entry.toString() : null
|
||||
obj.value = this.#value
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const protocolOp = ber.readSequence()
|
||||
if (protocolOp !== operations.LDAP_REQ_COMPARE) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
const entry = ber.readString()
|
||||
ber.readSequence()
|
||||
const attribute = ber.readString()
|
||||
const value = ber.readString()
|
||||
|
||||
return {
|
||||
protocolOp,
|
||||
entry,
|
||||
attribute,
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CompareRequest
|
||||
168
node_modules/@ldapjs/messages/lib/messages/compare-request.test.js
generated
vendored
Normal file
168
node_modules/@ldapjs/messages/lib/messages/compare-request.test.js
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
const CompareRequest = require('./compare-request')
|
||||
|
||||
const { compareRequestBytes } = require('./_fixtures/message-byte-arrays')
|
||||
|
||||
tap.test('basic', t => {
|
||||
t.test('constructor no args', async t => {
|
||||
const req = new CompareRequest()
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_REQ_COMPARE,
|
||||
type: 'CompareRequest',
|
||||
entry: null,
|
||||
attribute: '',
|
||||
value: '',
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('constructor with args', async t => {
|
||||
const req = new CompareRequest({
|
||||
entry: 'dn=foo,dc=example,dc=com',
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
})
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_REQ_COMPARE,
|
||||
type: 'CompareRequest',
|
||||
entry: 'dn=foo,dc=example,dc=com',
|
||||
attribute: 'foo',
|
||||
value: 'bar',
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('.type', async t => {
|
||||
const req = new CompareRequest()
|
||||
t.equal(req.type, 'CompareRequest')
|
||||
})
|
||||
|
||||
t.test('.dn', async t => {
|
||||
const req = new CompareRequest({ entry: 'dn=foo,dc=example,dc=com' })
|
||||
t.equal(req.dn.toString(), 'dn=foo,dc=example,dc=com')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.attribute', t => {
|
||||
t.test('gets and sets', async t => {
|
||||
const req = new CompareRequest()
|
||||
t.equal(req.attribute, '')
|
||||
req.attribute = 'foo'
|
||||
t.equal(req.attribute, 'foo')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.entry', t => {
|
||||
t.test('gets and sets', async t => {
|
||||
const req = new CompareRequest()
|
||||
t.equal(req.entry, null)
|
||||
|
||||
req.entry = 'cn=foo'
|
||||
t.equal(req.entry.toString(), 'cn=foo')
|
||||
|
||||
req.entry = DN.fromString('sn=bar')
|
||||
t.equal(req.entry.toString(), 'sn=bar')
|
||||
})
|
||||
|
||||
t.test('throws for bad value', async t => {
|
||||
const req = new CompareRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.entry = { cn: 'foo' }
|
||||
},
|
||||
'entry must be a valid DN string or instance of LdapDn'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.value', t => {
|
||||
t.test('gets and sets', async t => {
|
||||
const req = new CompareRequest()
|
||||
t.equal(req.value, '')
|
||||
req.value = 'foo'
|
||||
t.equal(req.value, 'foo')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_toBer', t => {
|
||||
t.test('converts instance to BER', async t => {
|
||||
const req = new CompareRequest({
|
||||
messageId: 2,
|
||||
entry: 'uid=jdoe,ou=People,dc=example,dc=com',
|
||||
attribute: 'employeeType',
|
||||
value: 'salaried'
|
||||
})
|
||||
const writer = new BerWriter()
|
||||
req._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(compareRequestBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_pojo', t => {
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const req = new CompareRequest({
|
||||
entry: 'cn=foo',
|
||||
attribute: 'bar',
|
||||
value: 'baz'
|
||||
})
|
||||
t.strictSame(req._pojo(), {
|
||||
entry: 'cn=foo',
|
||||
attribute: 'bar',
|
||||
value: 'baz'
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws if operation incorrect', async t => {
|
||||
const reqBuffer = Buffer.from(compareRequestBytes)
|
||||
reqBuffer[5] = 0x61
|
||||
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
t.throws(
|
||||
() => CompareRequest.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x61')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const reqBuffer = Buffer.from(compareRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = CompareRequest.parseToPojo(reader)
|
||||
t.equal(pojo.protocolOp, operations.LDAP_REQ_COMPARE)
|
||||
t.equal(pojo.entry, 'uid=jdoe,ou=People,dc=example,dc=com')
|
||||
t.equal(pojo.attribute, 'employeeType')
|
||||
t.equal(pojo.value, 'salaried')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
45
node_modules/@ldapjs/messages/lib/messages/compare-response.js
generated
vendored
Normal file
45
node_modules/@ldapjs/messages/lib/messages/compare-response.js
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
'use strict'
|
||||
|
||||
const LdapResult = require('../ldap-result')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
|
||||
/**
|
||||
* Implements the compare response message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.10.
|
||||
*/
|
||||
class CompareResponse extends LdapResult {
|
||||
/**
|
||||
* @param {LdapResultOptions} options
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_RES_COMPARE
|
||||
super(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'CompareResponse'
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
return LdapResult._parseToPojo({
|
||||
opCode: operations.LDAP_RES_COMPARE,
|
||||
berReader: ber
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CompareResponse
|
||||
55
node_modules/@ldapjs/messages/lib/messages/compare-response.test.js
generated
vendored
Normal file
55
node_modules/@ldapjs/messages/lib/messages/compare-response.test.js
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const { operations, resultCodes } = require('@ldapjs/protocol')
|
||||
const {
|
||||
compareResponseBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
const CompareResponse = require('./compare-response')
|
||||
|
||||
tap.test('basic', async t => {
|
||||
const res = new CompareResponse()
|
||||
t.equal(res.protocolOp, operations.LDAP_RES_COMPARE)
|
||||
t.equal(res.type, 'CompareResponse')
|
||||
})
|
||||
|
||||
tap.test('toBer', t => {
|
||||
tap.test('returns basic bytes', async t => {
|
||||
const res = new CompareResponse({
|
||||
messageId: 2,
|
||||
status: resultCodes.COMPARE_TRUE
|
||||
})
|
||||
const ber = res.toBer()
|
||||
const expected = Buffer.from(compareResponseBytes)
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('parses a basic object', async t => {
|
||||
const bytes = compareResponseBytes.slice(5)
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
const pojo = CompareResponse.parseToPojo(reader)
|
||||
t.strictSame(pojo, {
|
||||
status: resultCodes.COMPARE_TRUE,
|
||||
matchedDN: '',
|
||||
diagnosticMessage: '',
|
||||
referrals: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('throws if protocol op is wrong', async t => {
|
||||
const bytes = compareResponseBytes.slice(5)
|
||||
bytes[0] = 0x68
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
t.throws(
|
||||
() => CompareResponse.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x68')
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
116
node_modules/@ldapjs/messages/lib/messages/delete-request.js
generated
vendored
Normal file
116
node_modules/@ldapjs/messages/lib/messages/delete-request.js
generated
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
'use strict'
|
||||
|
||||
const LdapMessage = require('../ldap-message')
|
||||
const Protocol = require('@ldapjs/protocol')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
|
||||
/**
|
||||
* Implements the delete request message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.8
|
||||
*/
|
||||
class DeleteRequest extends LdapMessage {
|
||||
#entry
|
||||
|
||||
/**
|
||||
* @typedef {LdapMessageOptions} DeleteRequestOptions
|
||||
* @property {string} [entry=null] The LDAP entry path to remove.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {DeleteRequestOptions} [options]
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = Protocol.operations.LDAP_REQ_DELETE
|
||||
super(options)
|
||||
|
||||
this.entry = options.entry ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of {@link name}.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get _dn () {
|
||||
return this.entry
|
||||
}
|
||||
|
||||
/**
|
||||
* The identifier for the request that the instance will request be abandoned.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get entry () {
|
||||
return this.#entry ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the path to the LDAP object that will be deleted.
|
||||
*
|
||||
* @param {string | null | import('@ldapjs/dn').DN} value
|
||||
*/
|
||||
set entry (value) {
|
||||
if (value === null) return
|
||||
if (typeof value === 'string') {
|
||||
this.#entry = DN.fromString(value)
|
||||
} else if (Object.prototype.toString.call(value) === '[object LdapDn]') {
|
||||
this.#entry = value
|
||||
} else {
|
||||
throw Error('entry must be a valid DN string or instance of LdapDn')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'DeleteRequest'
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.writeString(this.#entry.toString(), Protocol.operations.LDAP_REQ_DELETE)
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {object}
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
_pojo (obj = {}) {
|
||||
obj.protocolOp = Protocol.operations.LDAP_REQ_DELETE
|
||||
obj.entry = this.#entry ? this.#entry.toString() : null
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const protocolOp = ber.peek()
|
||||
if (protocolOp !== Protocol.operations.LDAP_REQ_DELETE) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
const entry = ber.readString(Protocol.operations.LDAP_REQ_DELETE)
|
||||
return { protocolOp, entry }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DeleteRequest
|
||||
115
node_modules/@ldapjs/messages/lib/messages/delete-request.test.js
generated
vendored
Normal file
115
node_modules/@ldapjs/messages/lib/messages/delete-request.test.js
generated
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
const DeleteRequest = require('./delete-request')
|
||||
|
||||
tap.test('constructor', t => {
|
||||
t.test('no args', async t => {
|
||||
const req = new DeleteRequest()
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
type: 'DeleteRequest',
|
||||
protocolOp: 0x4a,
|
||||
entry: null,
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('with options', async t => {
|
||||
const req = new DeleteRequest({
|
||||
messageId: 5,
|
||||
entry: 'dc=example,dc=com'
|
||||
})
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 5,
|
||||
type: 'DeleteRequest',
|
||||
protocolOp: 0x4a,
|
||||
entry: 'dc=example,dc=com',
|
||||
controls: []
|
||||
})
|
||||
|
||||
t.equal(req._dn.toString(), 'dc=example,dc=com')
|
||||
t.equal(req.entry.toString(), 'dc=example,dc=com')
|
||||
t.equal(req.type, 'DeleteRequest')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.entry', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const req = new DeleteRequest()
|
||||
t.equal(req.entry, null)
|
||||
t.equal(req._dn, null)
|
||||
|
||||
req.entry = 'cn=foo'
|
||||
t.equal(req.entry.toString(), 'cn=foo')
|
||||
t.equal(req._dn.toString(), 'cn=foo')
|
||||
|
||||
req.entry = DN.fromString('sn=bar')
|
||||
t.equal(req.entry.toString(), 'sn=bar')
|
||||
})
|
||||
|
||||
t.test('throws for bad value', async t => {
|
||||
const req = new DeleteRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.entry = { cn: 'foo' }
|
||||
},
|
||||
'entry must be a valid DN string or instance of LdapDn'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_toBer', t => {
|
||||
t.test('writes a correct sequence', async t => {
|
||||
const req = new DeleteRequest({ entry: 'cn=foo' })
|
||||
const ber = new BerWriter()
|
||||
req._toBer(ber)
|
||||
|
||||
const expected = Buffer.from([
|
||||
0x4a, 0x06, 0x63, 0x6e,
|
||||
0x3d, 0x66, 0x6f, 0x6f
|
||||
])
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_pojo', t => {
|
||||
t.test('returns implementation properties', async t => {
|
||||
const req = new DeleteRequest({ entry: 'cn=foo' })
|
||||
t.strictSame(req._pojo(), {
|
||||
protocolOp: 0x4a,
|
||||
entry: 'cn=foo'
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws if tag is wrong', async t => {
|
||||
const input = Buffer.from([0x4b, 0x03, 0x66, 0x6f, 0x6f])
|
||||
t.throws(
|
||||
() => DeleteRequest.parseToPojo(new BerReader(input)),
|
||||
Error('found wrong protocol operation: 0x4b')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('returns a pojo', async t => {
|
||||
const input = Buffer.from([0x4a, 0x03, 0x66, 0x6f, 0x6f])
|
||||
const pojo = DeleteRequest.parseToPojo(new BerReader(input))
|
||||
t.strictSame(pojo, {
|
||||
protocolOp: 0x4a,
|
||||
entry: 'foo'
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
45
node_modules/@ldapjs/messages/lib/messages/delete-response.js
generated
vendored
Normal file
45
node_modules/@ldapjs/messages/lib/messages/delete-response.js
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
'use strict'
|
||||
|
||||
const LdapResult = require('../ldap-result')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
|
||||
/**
|
||||
* Implements the delete response message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.8.
|
||||
*/
|
||||
class DeleteResponse extends LdapResult {
|
||||
/**
|
||||
* @param {LdapResultOptions} options
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_RES_DELETE
|
||||
super(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'DeleteResponse'
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
return LdapResult._parseToPojo({
|
||||
opCode: operations.LDAP_RES_DELETE,
|
||||
berReader: ber
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DeleteResponse
|
||||
52
node_modules/@ldapjs/messages/lib/messages/delete-response.test.js
generated
vendored
Normal file
52
node_modules/@ldapjs/messages/lib/messages/delete-response.test.js
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const {
|
||||
deleteResponseBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
const DeleteResponse = require('./delete-response')
|
||||
|
||||
tap.test('basic', async t => {
|
||||
const res = new DeleteResponse()
|
||||
t.equal(res.protocolOp, operations.LDAP_RES_DELETE)
|
||||
t.equal(res.type, 'DeleteResponse')
|
||||
})
|
||||
|
||||
tap.test('toBer', t => {
|
||||
tap.test('returns basic bytes', async t => {
|
||||
const res = new DeleteResponse({ messageId: 2 })
|
||||
const ber = res.toBer()
|
||||
const expected = Buffer.from(deleteResponseBytes)
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('parses a basic object', async t => {
|
||||
const bytes = deleteResponseBytes.slice(5)
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
const pojo = DeleteResponse.parseToPojo(reader)
|
||||
t.strictSame(pojo, {
|
||||
status: 0,
|
||||
matchedDN: '',
|
||||
diagnosticMessage: '',
|
||||
referrals: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('throws if protocol op is wrong', async t => {
|
||||
const bytes = deleteResponseBytes.slice(5)
|
||||
bytes[0] = 0x68
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
t.throws(
|
||||
() => DeleteResponse.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x68')
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
284
node_modules/@ldapjs/messages/lib/messages/extension-request.js
generated
vendored
Normal file
284
node_modules/@ldapjs/messages/lib/messages/extension-request.js
generated
vendored
Normal file
@ -0,0 +1,284 @@
|
||||
'use strict'
|
||||
|
||||
const LdapMessage = require('../ldap-message')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const RECOGNIZED_OIDS = require('./extension-utils/recognized-oids')
|
||||
|
||||
/**
|
||||
* Implements the extension request message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.12.
|
||||
*
|
||||
* There is a set of supported extension request OIDs supported. Any
|
||||
* unrecognized OID will be treated a simple string pair, i.e. both
|
||||
* `requestName` and `requestValue` will be assumed to be simple strings.
|
||||
*/
|
||||
class ExtensionRequest extends LdapMessage {
|
||||
#requestName
|
||||
#requestValue
|
||||
|
||||
/**
|
||||
* @typedef {LdapMessageOptions} ExtensionRequestOptions
|
||||
* @property {string} [requestName=''] The name of the extension, i.e.
|
||||
* OID for the request.
|
||||
* @property {string|object} [requestValue] The value for the request.
|
||||
* If `undefined`, no value will be sent. If the request requires a simple
|
||||
* string value, provide such a string. For complex valued requests, e.g.
|
||||
* for a password modify request, it should be a plain object with the
|
||||
* appropriate properties. See the implementation of {@link parseToPojo}
|
||||
* for the set of supported objects.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {ExtensionRequestOptions} [options]
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_REQ_EXTENSION
|
||||
super(options)
|
||||
|
||||
this.requestName = options.requestName || ''
|
||||
this.requestValue = options.requestValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of {@link requestName}.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get _dn () {
|
||||
return this.#requestName
|
||||
}
|
||||
|
||||
/**
|
||||
* The name (OID) of the request.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
get requestName () {
|
||||
return this.#requestName
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name for the request. Should be an OID that
|
||||
* matches a specification.
|
||||
*
|
||||
* @param {string} value
|
||||
*/
|
||||
set requestName (value) {
|
||||
this.#requestName = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'ExtensionRequest'
|
||||
}
|
||||
|
||||
/**
|
||||
* The value, if any, for the request.
|
||||
*
|
||||
* @returns {undefined|string|object} value
|
||||
*/
|
||||
get requestValue () {
|
||||
return this.#requestValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value for the request. The value should conform
|
||||
* to the specification identified by the {@link requestName}.
|
||||
* See the implemenation of {@link parseToPojo} for valid
|
||||
* value shapes.
|
||||
*
|
||||
* @param {undefined|string|object} value
|
||||
*/
|
||||
set requestValue (val) {
|
||||
this.#requestValue = val
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.startSequence(operations.LDAP_REQ_EXTENSION)
|
||||
ber.writeString(this.requestName, 0x80)
|
||||
|
||||
if (this.requestValue) {
|
||||
switch (this.requestName) {
|
||||
case RECOGNIZED_OIDS.get('CANCEL_REQUEST'): {
|
||||
encodeCancelRequest({ ber, requestValue: this.requestValue })
|
||||
break
|
||||
}
|
||||
|
||||
case RECOGNIZED_OIDS.get('PASSWORD_MODIFY'): {
|
||||
encodePasswordModify({
|
||||
ber,
|
||||
requestValue: this.requestValue
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
// We assume the value is a plain string since
|
||||
// we do not recognize the request OID, or we know
|
||||
// that the OID uses a plain string value.
|
||||
ber.writeString(this.requestValue, 0x81)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ber.endSequence()
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {object}
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
_pojo (obj = {}) {
|
||||
obj.requestName = this.requestName
|
||||
obj.requestValue = this.requestValue
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const protocolOp = ber.readSequence()
|
||||
if (protocolOp !== operations.LDAP_REQ_EXTENSION) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
// While the requestName is an OID, it is not an
|
||||
// _encoded_ OID. It is a plain string. So we do
|
||||
// not use `.readOID` here.
|
||||
const requestName = ber.readString(0x80)
|
||||
if (ber.peek() !== 0x81) {
|
||||
// There is not a request value present, so we just
|
||||
// return an empty value representation.
|
||||
return { protocolOp, requestName }
|
||||
}
|
||||
|
||||
let requestValue
|
||||
switch (requestName) {
|
||||
case RECOGNIZED_OIDS.get('CANCEL_REQUEST'): {
|
||||
requestValue = readCancelRequest(ber)
|
||||
break
|
||||
}
|
||||
|
||||
case RECOGNIZED_OIDS.get('PASSWORD_MODIFY'): {
|
||||
requestValue = readPasswordModify(ber)
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
// We will assume it is a plain string value
|
||||
// since we do not recognize the OID, or we know
|
||||
// that the OID uses a plain string value.
|
||||
requestValue = ber.readString(0x81)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return { protocolOp, requestName, requestValue }
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of EXTENDED operation OIDs that this module
|
||||
* recognizes. Key names are named according to the common name
|
||||
* of the extension. Key values are the OID associated with that
|
||||
* extension. For example, key `PASSWORD_MODIFY` corresponds to
|
||||
* OID `1.3.6.1.4.1.4203.1.11.1`.
|
||||
*
|
||||
* @returns {Map<string, string>}
|
||||
*/
|
||||
static recognizedOIDs () {
|
||||
return RECOGNIZED_OIDS
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ExtensionRequest
|
||||
|
||||
/**
|
||||
* @param {object} input
|
||||
* @param {@import('@ldapjs/asn1').BerWriter} input.ber
|
||||
* @param {object} requestValue
|
||||
*/
|
||||
function encodeCancelRequest ({ ber, requestValue }) {
|
||||
ber.startSequence(0x81)
|
||||
ber.startSequence()
|
||||
ber.writeInt(requestValue)
|
||||
ber.endSequence()
|
||||
ber.endSequence()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {@import('@ldapjs/asn1').BerReader} ber
|
||||
* @returns {number}
|
||||
*/
|
||||
function readCancelRequest (ber) {
|
||||
ber.readSequence(0x81)
|
||||
ber.readSequence()
|
||||
return ber.readInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} input
|
||||
* @param {@import('@ldapjs/asn1').BerWriter} input.ber
|
||||
* @param {object} requestValue
|
||||
*/
|
||||
function encodePasswordModify ({ ber, requestValue }) {
|
||||
// start the value sequence
|
||||
ber.startSequence(0x81)
|
||||
// start the generic packed sequence
|
||||
ber.startSequence()
|
||||
if (requestValue.userIdentity) {
|
||||
ber.writeString(requestValue.userIdentity, 0x80)
|
||||
}
|
||||
if (requestValue.oldPassword) {
|
||||
ber.writeString(requestValue.oldPassword, 0x81)
|
||||
}
|
||||
if (requestValue.newPassword) {
|
||||
ber.writeString(requestValue.newPassword, 0x82)
|
||||
}
|
||||
ber.endSequence()
|
||||
ber.endSequence()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {@import('@ldapjs/asn1').BerReader} ber
|
||||
* @returns {object}
|
||||
*/
|
||||
function readPasswordModify (ber) {
|
||||
// advance to the embedded sequence
|
||||
ber.readSequence(0x81)
|
||||
// advance to the value of the embedded sequence
|
||||
ber.readSequence()
|
||||
let userIdentity
|
||||
if (ber.peek() === 0x80) {
|
||||
userIdentity = ber.readString(0x80)
|
||||
}
|
||||
let oldPassword
|
||||
if (ber.peek() === 0x81) {
|
||||
oldPassword = ber.readString(0x81)
|
||||
}
|
||||
let newPassword
|
||||
if (ber.peek() === 0x82) {
|
||||
newPassword = ber.readString(0x82)
|
||||
}
|
||||
return { userIdentity, oldPassword, newPassword }
|
||||
}
|
||||
254
node_modules/@ldapjs/messages/lib/messages/extension-request.test.js
generated
vendored
Normal file
254
node_modules/@ldapjs/messages/lib/messages/extension-request.test.js
generated
vendored
Normal file
@ -0,0 +1,254 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const ExtensionRequest = require('./extension-request')
|
||||
|
||||
const {
|
||||
extensionCancelRequestBytes,
|
||||
extensionNameAndValueRequestBytes,
|
||||
extensionNameAndNoValueRequestBytes,
|
||||
extensionChangePasswordRequestBytes,
|
||||
extensionChangePasswordWithNewPasswordBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
||||
|
||||
tap.test('basic', t => {
|
||||
t.test('constructor no args', async t => {
|
||||
const req = new ExtensionRequest()
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_REQ_EXTENSION,
|
||||
type: 'ExtensionRequest',
|
||||
requestName: '',
|
||||
requestValue: undefined,
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('constructor with args', async t => {
|
||||
const req = new ExtensionRequest({
|
||||
requestName: 'foo',
|
||||
requestValue: 'bar'
|
||||
})
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_REQ_EXTENSION,
|
||||
type: 'ExtensionRequest',
|
||||
requestName: 'foo',
|
||||
requestValue: 'bar',
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('.type', async t => {
|
||||
const req = new ExtensionRequest()
|
||||
t.equal(req.type, 'ExtensionRequest')
|
||||
})
|
||||
|
||||
t.test('.dn', async t => {
|
||||
const req = new ExtensionRequest({ requestName: 'foo' })
|
||||
t.equal(req.dn, 'foo')
|
||||
})
|
||||
|
||||
t.test('#recognizedOIDs returns map', async t => {
|
||||
const oids = ExtensionRequest.recognizedOIDs()
|
||||
t.type(oids, 'Map')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.name', t => {
|
||||
t.test('gets and sets', async t => {
|
||||
const req = new ExtensionRequest()
|
||||
t.equal(req.requestName, '')
|
||||
req.requestName = 'foo'
|
||||
t.equal(req.requestName, 'foo')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.value', t => {
|
||||
t.test('gets and sets', async t => {
|
||||
const req = new ExtensionRequest()
|
||||
t.equal(req.requestValue, undefined)
|
||||
req.requestValue = 'foo'
|
||||
t.equal(req.requestValue, 'foo')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_toBer', t => {
|
||||
t.test('converts simple instance to BER', async t => {
|
||||
const req = new ExtensionRequest({
|
||||
requestName: '1.3.6.1.1',
|
||||
requestValue: 'foobar'
|
||||
})
|
||||
const writer = new BerWriter()
|
||||
req._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(extensionNameAndValueRequestBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
t.test('converts a simple instance with no value to BER', async t => {
|
||||
const req = new ExtensionRequest({ requestName: '1.3.6.1.1' })
|
||||
const writer = new BerWriter()
|
||||
req._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(extensionNameAndNoValueRequestBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
t.test('converts a cancel request', async t => {
|
||||
const req = new ExtensionRequest({
|
||||
requestName: '1.3.6.1.1.8',
|
||||
requestValue: 1
|
||||
})
|
||||
const writer = new BerWriter()
|
||||
req._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(extensionCancelRequestBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
t.test('converts a modify password request with userIdentity and oldPassword', async t => {
|
||||
const req = new ExtensionRequest({
|
||||
requestName: '1.3.6.1.4.1.4203.1.11.1',
|
||||
requestValue: {
|
||||
userIdentity: 'uid=jdoe,ou=People,dc=example,dc=com',
|
||||
oldPassword: 'secret123'
|
||||
}
|
||||
})
|
||||
const writer = new BerWriter()
|
||||
req._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(extensionChangePasswordRequestBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
t.test('converts a modify password request with newPassword', async t => {
|
||||
const req = new ExtensionRequest({
|
||||
requestName: '1.3.6.1.4.1.4203.1.11.1',
|
||||
requestValue: {
|
||||
newPassword: 'secret123'
|
||||
}
|
||||
})
|
||||
const writer = new BerWriter()
|
||||
req._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(extensionChangePasswordWithNewPasswordBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_pojo', t => {
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const req = new ExtensionRequest({
|
||||
requestName: 'foo',
|
||||
requestValue: 'bar'
|
||||
})
|
||||
t.strictSame(req._pojo(), {
|
||||
requestName: 'foo',
|
||||
requestValue: 'bar'
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws if operation incorrect', async t => {
|
||||
const reqBuffer = Buffer.from(extensionNameAndValueRequestBytes)
|
||||
reqBuffer[5] = 0x61
|
||||
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
t.throws(
|
||||
() => ExtensionRequest.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x61')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('returns a pojo for a name only operation', async t => {
|
||||
const reqBuffer = Buffer.from(extensionNameAndNoValueRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = ExtensionRequest.parseToPojo(reader)
|
||||
t.equal(pojo.requestName, '1.3.6.1.1')
|
||||
t.equal(pojo.requestValue, undefined)
|
||||
})
|
||||
|
||||
t.test('returns a pojo for simple string name and value operation', async t => {
|
||||
const reqBuffer = Buffer.from(extensionNameAndValueRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = ExtensionRequest.parseToPojo(reader)
|
||||
t.equal(pojo.requestName, '1.3.6.1.1')
|
||||
t.equal(pojo.requestValue, 'foobar')
|
||||
})
|
||||
|
||||
t.test('returns pojo for a cancel request', async t => {
|
||||
const reqBuffer = Buffer.from(extensionCancelRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = ExtensionRequest.parseToPojo(reader)
|
||||
t.equal(pojo.requestName, '1.3.6.1.1.8')
|
||||
t.equal(pojo.requestValue, 1)
|
||||
})
|
||||
|
||||
t.test('returns a pojo for a modify password with user and old pass', async t => {
|
||||
const reqBuffer = Buffer.from(extensionChangePasswordRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = ExtensionRequest.parseToPojo(reader)
|
||||
t.equal(pojo.requestName, '1.3.6.1.4.1.4203.1.11.1')
|
||||
t.strictSame(pojo.requestValue, {
|
||||
userIdentity: 'uid=jdoe,ou=People,dc=example,dc=com',
|
||||
oldPassword: 'secret123',
|
||||
newPassword: undefined
|
||||
})
|
||||
})
|
||||
|
||||
t.test('returns a pojo for a modify password new pass', async t => {
|
||||
const reqBuffer = Buffer.from(extensionChangePasswordWithNewPasswordBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = ExtensionRequest.parseToPojo(reader)
|
||||
t.equal(pojo.requestName, '1.3.6.1.4.1.4203.1.11.1')
|
||||
t.strictSame(pojo.requestValue, {
|
||||
userIdentity: undefined,
|
||||
oldPassword: undefined,
|
||||
newPassword: 'secret123'
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
156
node_modules/@ldapjs/messages/lib/messages/extension-response.js
generated
vendored
Normal file
156
node_modules/@ldapjs/messages/lib/messages/extension-response.js
generated
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
'use strict'
|
||||
|
||||
const LdapResult = require('../ldap-result')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
|
||||
/**
|
||||
* Implements the extension response message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.12.
|
||||
*
|
||||
* The type of response is impossible to determine in isolation.
|
||||
* Most EXTENSION responses do not include the request OID. And they
|
||||
* all encode their values in unique ways. Therefore, this object's
|
||||
* {@link parseToPojo} never attempts to parse the response value.
|
||||
* Instead, if it is present, it reads the value as a buffer and
|
||||
* encodes it into a hexadecimal string prefixed with a `<buffer>`
|
||||
* token. This string is then used by the `#fromExtension` method
|
||||
* on specific implementations to build a new object. It is left up to
|
||||
* the implementor to know when certain responses are expected and
|
||||
* to act accordingly.
|
||||
*/
|
||||
class ExtensionResponse extends LdapResult {
|
||||
#responseName
|
||||
#responseValue
|
||||
|
||||
/**
|
||||
* @typedef {LdapResultOptions} ExtensionResponseOptions
|
||||
* @property {string|undefined} [responseName] The name of the extension, i.e.
|
||||
* OID for the response.
|
||||
* @property {string|undefined} [responseValue] The value for the
|
||||
* response. It may be a buffer string; such a string is a series of
|
||||
* hexadecimal pairs preceded by the token `<buffer>`. Buffer strings
|
||||
* are used by specific response object types to get that type's specific
|
||||
* encoded value.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {ExtensionResponseOptions} [options]
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_RES_EXTENSION
|
||||
super(options)
|
||||
|
||||
this.responseName = options.responseName
|
||||
this.responseValue = options.responseValue
|
||||
}
|
||||
|
||||
/**
|
||||
* The OID, if any, of the response.
|
||||
*
|
||||
* @returns {string|undefined}
|
||||
*/
|
||||
get responseName () {
|
||||
return this.#responseName
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the name (OID) of the response.
|
||||
*
|
||||
* @param {string} value
|
||||
*/
|
||||
set responseName (value) {
|
||||
this.#responseName = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The response value, if any. For specific extensions that
|
||||
* are not simple string values, the initial value is a buffer string.
|
||||
* That is, it is a hexadecimal string of bytes prefixed with `<buffer>`.
|
||||
* To parse this value, use a specific extension's `#fromResponse` method.
|
||||
*
|
||||
* @returns {string|undefined}
|
||||
*/
|
||||
get responseValue () {
|
||||
return this.#responseValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the response value. Should be a buffer string if the value is
|
||||
* an encoded value.
|
||||
*
|
||||
* @param {string} value
|
||||
*/
|
||||
set responseValue (value) {
|
||||
this.#responseValue = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'ExtensionResponse'
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only. Used to write the response name and
|
||||
* response value into the BER object.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_writeResponse (ber) {
|
||||
if (this.responseName) {
|
||||
ber.writeString(this.responseName, 0x8a)
|
||||
}
|
||||
|
||||
if (this.responseValue === undefined) {
|
||||
return ber
|
||||
}
|
||||
|
||||
switch (this.responseName) {
|
||||
default: {
|
||||
// We assume the value is a plain string since
|
||||
// we do not recognize the response OID, or we
|
||||
// know it would be a plain string.
|
||||
ber.writeString(this.responseValue, 0x8b)
|
||||
}
|
||||
}
|
||||
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const pojo = LdapResult._parseToPojo({
|
||||
opCode: operations.LDAP_RES_EXTENSION,
|
||||
berReader: ber
|
||||
})
|
||||
|
||||
let responseName
|
||||
if (ber.peek() === 0x8a) {
|
||||
responseName = ber.readString(0x8a)
|
||||
}
|
||||
|
||||
if (ber.peek() !== 0x8b) {
|
||||
return { ...pojo, responseName }
|
||||
}
|
||||
|
||||
const valueBuffer = ber.readTag(0x8b)
|
||||
const responseValue = `<buffer>${valueBuffer.toString('hex')}`
|
||||
|
||||
return { ...pojo, responseName, responseValue }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ExtensionResponse
|
||||
117
node_modules/@ldapjs/messages/lib/messages/extension-response.test.js
generated
vendored
Normal file
117
node_modules/@ldapjs/messages/lib/messages/extension-response.test.js
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
||||
const { operations, resultCodes } = require('@ldapjs/protocol')
|
||||
const {
|
||||
extensionDisconnectionNotificationResponseBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
const ExtensionResponse = require('./extension-response')
|
||||
|
||||
tap.test('basic', async t => {
|
||||
const res = new ExtensionResponse()
|
||||
t.equal(res.protocolOp, operations.LDAP_RES_EXTENSION)
|
||||
t.equal(res.type, 'ExtensionResponse')
|
||||
})
|
||||
|
||||
tap.test('.responseName', t => {
|
||||
t.test('gets and sets', async t => {
|
||||
const res = new ExtensionResponse()
|
||||
t.equal(res.responseName, undefined)
|
||||
res.responseName = 'foo'
|
||||
t.equal(res.responseName, 'foo')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.responseValue', t => {
|
||||
t.test('gets and sets', async t => {
|
||||
const res = new ExtensionResponse()
|
||||
t.equal(res.responseValue, undefined)
|
||||
res.responseValue = 'foo'
|
||||
t.equal(res.responseValue, 'foo')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('toBer', t => {
|
||||
tap.test('returns basic bytes', async t => {
|
||||
const res = new ExtensionResponse({
|
||||
messageId: 0,
|
||||
status: resultCodes.UNAVAILABLE,
|
||||
diagnosticMessage: 'The Directory Server is shutting down',
|
||||
responseName: '1.3.6.1.4.1.1466.20036'
|
||||
})
|
||||
const ber = res.toBer()
|
||||
const expected = Buffer.from(extensionDisconnectionNotificationResponseBytes)
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_writeResponse', t => {
|
||||
t.test('writes a response with a name', async t => {
|
||||
const req = new ExtensionResponse({ responseName: 'foo' })
|
||||
const expected = Buffer.from([0x8a, 0x03, 0x66, 0x6f, 0x6f])
|
||||
const writer = new BerWriter()
|
||||
req._writeResponse(writer)
|
||||
t.equal(expected.compare(writer.buffer), 0)
|
||||
})
|
||||
|
||||
t.test('writes response with value', async t => {
|
||||
const req = new ExtensionResponse({ responseValue: 'foo' })
|
||||
const expected = Buffer.from([0x8b, 0x03, 0x66, 0x6f, 0x6f])
|
||||
const writer = new BerWriter()
|
||||
req._writeResponse(writer)
|
||||
t.equal(expected.compare(writer.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws if protocol op is wrong', async t => {
|
||||
const bytes = extensionDisconnectionNotificationResponseBytes.slice(5)
|
||||
bytes[0] = 0x68
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
t.throws(
|
||||
() => ExtensionResponse.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x68')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('parses a basic object', async t => {
|
||||
const bytes = extensionDisconnectionNotificationResponseBytes.slice(5)
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
const pojo = ExtensionResponse.parseToPojo(reader)
|
||||
t.strictSame(pojo, {
|
||||
status: resultCodes.UNAVAILABLE,
|
||||
matchedDN: '',
|
||||
diagnosticMessage: 'The Directory Server is shutting down',
|
||||
referrals: [],
|
||||
responseName: '1.3.6.1.4.1.1466.20036'
|
||||
})
|
||||
})
|
||||
|
||||
t.test('values are buffer strings', async t => {
|
||||
const {
|
||||
withGeneratedPasswordBytes
|
||||
} = require('./extension-responses/_fixtures/password-modify')
|
||||
const bytes = withGeneratedPasswordBytes.slice(5)
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
const pojo = ExtensionResponse.parseToPojo(reader)
|
||||
t.strictSame(pojo, {
|
||||
status: 0,
|
||||
matchedDN: '',
|
||||
diagnosticMessage: '',
|
||||
referrals: [],
|
||||
responseName: undefined,
|
||||
responseValue: '<buffer>301f801d4164617074657253746576656e736f6e477261696e506c61796d617465'
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
34
node_modules/@ldapjs/messages/lib/messages/extension-responses/_fixtures/password-modify.js
generated
vendored
Normal file
34
node_modules/@ldapjs/messages/lib/messages/extension-responses/_fixtures/password-modify.js
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Represents an EXTENSION response that is for a password change
|
||||
* without providing a new one (server generates password).
|
||||
* Taken from https://web.archive.org/web/20220518193613/https://nawilson.com/ldapv3-wire-protocol-reference-extended/
|
||||
*/
|
||||
module.exports.withGeneratedPasswordBytes = [
|
||||
0x30, 0x2f, // start sequence, 47 bytes
|
||||
0x02, 0x01, 0x01, // message ID (integer value 1)
|
||||
0x78, 0x2a, // protocol op (0x78), 42 bytes
|
||||
0x0a, 0x01, 0x00, // success result code (enumerated value 0)
|
||||
0x04, 0x00, // no matched DN (0-byte octet string)
|
||||
0x04, 0x00, // no diagnostic message (0-byte octet string)
|
||||
|
||||
0x8b, 0x21, // sequence (response value), 33 bytes
|
||||
0x30, 0x1f, // sequence, 31 bytes
|
||||
|
||||
0x80, 0x1d, // extension specific string, 29 bytes
|
||||
// "AdapterStevensonGrainPlaymate"
|
||||
0x41, 0x64, 0x61, 0x70, 0x74, 0x65, 0x72, 0x53,
|
||||
0x74, 0x65, 0x76, 0x65, 0x6e, 0x73, 0x6f, 0x6e,
|
||||
0x47, 0x72, 0x61, 0x69, 0x6e, 0x50, 0x6c, 0x61,
|
||||
0x79, 0x6d, 0x61, 0x74, 0x65
|
||||
]
|
||||
|
||||
module.exports.basicResponse = [
|
||||
0x30, 0x0c, // start sequence, 47 bytes
|
||||
0x02, 0x01, 0x01, // message ID (integer value 1)
|
||||
0x78, 0x2a, // protocol op (0x78), 42 bytes
|
||||
0x0a, 0x01, 0x00, // success result code (enumerated value 0)
|
||||
0x04, 0x00, // no matched DN (0-byte octet string)
|
||||
0x04, 0x00 // no diagnostic message (0-byte octet string)
|
||||
]
|
||||
24
node_modules/@ldapjs/messages/lib/messages/extension-responses/_fixtures/who-am-i.js
generated
vendored
Normal file
24
node_modules/@ldapjs/messages/lib/messages/extension-responses/_fixtures/who-am-i.js
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
'use strict'
|
||||
|
||||
module.exports.withId = [
|
||||
0x30, 0x21, // sequence, 33 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer 2)
|
||||
0x78, 0x1c, // protocol op (0x78), 28 bytes
|
||||
0x0a, 0x01, 0x00, // status code, 0
|
||||
0x04, 0x00, // no matched dn
|
||||
0x04, 0x00, // no diagnostic message
|
||||
0x8b, 0x13, // string response value
|
||||
// "u:xxyyz@EXAMPLE.NET"
|
||||
0x75, 0x3a, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x40,
|
||||
0x45, 0x58, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x2e,
|
||||
0x4e, 0x45, 0x54
|
||||
]
|
||||
|
||||
module.exports.withoutId = [
|
||||
0x30, 0x0c, // sequence, 12 bytes
|
||||
0x02, 0x01, 0x02, // message id (integer 2)
|
||||
0x78, 0x07, // protocol op (0x78), 7 bytes
|
||||
0x0a, 0x01, 0x00, // status code, 0
|
||||
0x04, 0x00, // no matched dn
|
||||
0x04, 0x00 // no diagnostic message
|
||||
]
|
||||
33
node_modules/@ldapjs/messages/lib/messages/extension-responses/password-modify.js
generated
vendored
Normal file
33
node_modules/@ldapjs/messages/lib/messages/extension-responses/password-modify.js
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
'use strict'
|
||||
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const ExtensionResponse = require('../extension-response')
|
||||
|
||||
/**
|
||||
* Implements the password modify extension defined by
|
||||
* https://www.rfc-editor.org/rfc/rfc3062.
|
||||
*/
|
||||
class PasswordModifyResponse extends ExtensionResponse {
|
||||
/**
|
||||
* Given a basic {@link ExtensionResponse} with a buffer string in
|
||||
* `responseValue`, parse into a specific {@link PasswordModifyResponse}
|
||||
* instance.
|
||||
*
|
||||
* @param {ExtensionResponse} response
|
||||
*
|
||||
* @returns {PasswordModifyResponse}
|
||||
*/
|
||||
static fromResponse (response) {
|
||||
if (response.responseValue === undefined) {
|
||||
return new PasswordModifyResponse()
|
||||
}
|
||||
|
||||
const valueBuffer = Buffer.from(response.responseValue.substring(8), 'hex')
|
||||
const reader = new BerReader(valueBuffer)
|
||||
reader.readSequence()
|
||||
const responseValue = reader.readString(0x80)
|
||||
return new PasswordModifyResponse({ responseValue })
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PasswordModifyResponse
|
||||
28
node_modules/@ldapjs/messages/lib/messages/extension-responses/password-modify.test.js
generated
vendored
Normal file
28
node_modules/@ldapjs/messages/lib/messages/extension-responses/password-modify.test.js
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const LdapMessage = require('../../ldap-message')
|
||||
const {
|
||||
basicResponse,
|
||||
withGeneratedPasswordBytes
|
||||
} = require('./_fixtures/password-modify')
|
||||
const PasswordModifyResponse = require('./password-modify')
|
||||
|
||||
tap.test('parses a response with a generated password', async t => {
|
||||
const reader = new BerReader(Buffer.from(withGeneratedPasswordBytes))
|
||||
let res = LdapMessage.parse(reader)
|
||||
res = PasswordModifyResponse.fromResponse(res)
|
||||
t.type(res, PasswordModifyResponse)
|
||||
t.equal(res.responseName, undefined)
|
||||
t.equal(res.responseValue, 'AdapterStevensonGrainPlaymate')
|
||||
})
|
||||
|
||||
tap.test('parses a response with an empty value', async t => {
|
||||
const reader = new BerReader(Buffer.from(basicResponse))
|
||||
let res = LdapMessage.parse(reader)
|
||||
res = PasswordModifyResponse.fromResponse(res)
|
||||
t.type(res, PasswordModifyResponse)
|
||||
t.equal(res.responseName, undefined)
|
||||
t.equal(res.responseValue, undefined)
|
||||
})
|
||||
30
node_modules/@ldapjs/messages/lib/messages/extension-responses/who-am-i.js
generated
vendored
Normal file
30
node_modules/@ldapjs/messages/lib/messages/extension-responses/who-am-i.js
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
'use strict'
|
||||
|
||||
const ExtensionResponse = require('../extension-response')
|
||||
|
||||
/**
|
||||
* Implements the "Who Am I" extension defined by
|
||||
* https://www.rfc-editor.org/rfc/rfc4532.
|
||||
*/
|
||||
class WhoAmIResponse extends ExtensionResponse {
|
||||
/**
|
||||
* Given a basic {@link ExtensionResponse} with a buffer string in
|
||||
* `responseValue`, parse into a specific {@link WhoAmIResponse}
|
||||
* instance.
|
||||
*
|
||||
* @param {ExtensionResponse} response
|
||||
*
|
||||
* @returns {WhoAmIResponse}
|
||||
*/
|
||||
static fromResponse (response) {
|
||||
if (response.responseValue === undefined) {
|
||||
return new WhoAmIResponse()
|
||||
}
|
||||
|
||||
const valueBuffer = Buffer.from(response.responseValue.substring(8), 'hex')
|
||||
const responseValue = valueBuffer.toString('utf8')
|
||||
return new WhoAmIResponse({ responseValue })
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WhoAmIResponse
|
||||
25
node_modules/@ldapjs/messages/lib/messages/extension-responses/who-am-i.test.js
generated
vendored
Normal file
25
node_modules/@ldapjs/messages/lib/messages/extension-responses/who-am-i.test.js
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const LdapMessage = require('../../ldap-message')
|
||||
const { withId, withoutId } = require('./_fixtures/who-am-i')
|
||||
const WhoAmIResponse = require('./who-am-i')
|
||||
|
||||
tap.test('parses a response with a returned id', async t => {
|
||||
const reader = new BerReader(Buffer.from(withId))
|
||||
let res = LdapMessage.parse(reader)
|
||||
res = WhoAmIResponse.fromResponse(res)
|
||||
t.type(res, WhoAmIResponse)
|
||||
t.equal(res.responseName, undefined)
|
||||
t.equal(res.responseValue, 'u:xxyyz@EXAMPLE.NET')
|
||||
})
|
||||
|
||||
tap.test('parses a response with a returned id', async t => {
|
||||
const reader = new BerReader(Buffer.from(withoutId))
|
||||
let res = LdapMessage.parse(reader)
|
||||
res = WhoAmIResponse.fromResponse(res)
|
||||
t.type(res, WhoAmIResponse)
|
||||
t.equal(res.responseName, undefined)
|
||||
t.equal(res.responseValue, undefined)
|
||||
})
|
||||
29
node_modules/@ldapjs/messages/lib/messages/extension-utils/recognized-oids.js
generated
vendored
Normal file
29
node_modules/@ldapjs/messages/lib/messages/extension-utils/recognized-oids.js
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
'use strict'
|
||||
|
||||
const OIDS = new Map([
|
||||
['CANCEL_REQUEST', '1.3.6.1.1.8'], // RFC 3909
|
||||
['DISCONNECTION_NOTIFICATION', '1.3.6.1.4.1.1466.20036'], // RFC 4511
|
||||
['PASSWORD_MODIFY', '1.3.6.1.4.1.4203.1.11.1'], // RFC 3062
|
||||
['START_TLS', '1.3.6.1.4.1.1466.20037'], // RFC 4511
|
||||
['WHO_AM_I', '1.3.6.1.4.1.4203.1.11.3'] // RFC 4532
|
||||
])
|
||||
|
||||
Object.defineProperty(OIDS, 'lookupName', {
|
||||
value: function (oid) {
|
||||
for (const [key, value] of this.entries()) {
|
||||
/* istanbul ignore else */
|
||||
if (value === oid) return key
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Object.defineProperty(OIDS, 'lookupOID', {
|
||||
value: function (name) {
|
||||
for (const [key, value] of this.entries()) {
|
||||
/* istanbul ignore else */
|
||||
if (key === name) return value
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = OIDS
|
||||
14
node_modules/@ldapjs/messages/lib/messages/extension-utils/recognized-oids.test.js
generated
vendored
Normal file
14
node_modules/@ldapjs/messages/lib/messages/extension-utils/recognized-oids.test.js
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const RECOGNIZED_OIDS = require('./recognized-oids')
|
||||
|
||||
tap.test('lookupName returns correct name', async t => {
|
||||
const name = RECOGNIZED_OIDS.lookupName('1.3.6.1.4.1.4203.1.11.1')
|
||||
t.equal(name, 'PASSWORD_MODIFY')
|
||||
})
|
||||
|
||||
tap.test('lookupOID returns correct OID', async t => {
|
||||
const name = RECOGNIZED_OIDS.lookupOID('PASSWORD_MODIFY')
|
||||
t.equal(name, '1.3.6.1.4.1.4203.1.11.1')
|
||||
})
|
||||
189
node_modules/@ldapjs/messages/lib/messages/intermediate-response.js
generated
vendored
Normal file
189
node_modules/@ldapjs/messages/lib/messages/intermediate-response.js
generated
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
'use strict'
|
||||
|
||||
const LdapMessage = require('../ldap-message')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
|
||||
const partIsNotNumeric = part => /^\d+$/.test(part) === false
|
||||
|
||||
/**
|
||||
* Determines if a passed in string is a dotted decimal string.
|
||||
*
|
||||
* Copied from `@ldapjs/dn`.
|
||||
*
|
||||
* @param {string} value
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isDottedDecimal (value) {
|
||||
if (typeof value !== 'string') return false
|
||||
|
||||
const parts = value.split('.')
|
||||
const nonNumericParts = parts.filter(partIsNotNumeric)
|
||||
|
||||
return nonNumericParts.length === 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the intermediate response message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.13.
|
||||
*
|
||||
* TODO: actual implementations of this, e.g. RFC 4533 §2.5, seem to encode
|
||||
* sequences in the responseValue. That means this needs a more robust
|
||||
* implementation like is found in the ExtensionResponse implementation (i.e.
|
||||
* detection of recognized OIDs and specific sub-implementations). As of now,
|
||||
* this implementation follows the baseline spec without any sub-implementations.
|
||||
*/
|
||||
class IntermediateResponse extends LdapMessage {
|
||||
#responseName
|
||||
#responseValue
|
||||
|
||||
/**
|
||||
* @typedef {LdapMessageOptions} IntermediateResponseOptions
|
||||
* @property {string} responseName
|
||||
* @property {string} responseValue
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {IntermediateResponseOptions} options
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_RES_INTERMEDIATE
|
||||
super(options)
|
||||
|
||||
this.responseName = options.responseName ?? null
|
||||
this.responseValue = options.responseValue ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'IntermediateResponse'
|
||||
}
|
||||
|
||||
/**
|
||||
* The numeric OID that identifies the type of intermediate response.
|
||||
*
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
get responseName () {
|
||||
return this.#responseName
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the numeric OID that identifies the type of intermediate response.
|
||||
*
|
||||
* @param {string | null} value
|
||||
*
|
||||
* @throws For an invalid value.
|
||||
*/
|
||||
set responseName (value) {
|
||||
if (value === null) return
|
||||
if (isDottedDecimal(value) === false) {
|
||||
throw Error('responseName must be a numeric OID')
|
||||
}
|
||||
this.#responseName = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The value for the intermidate response if any.
|
||||
*
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
get responseValue () {
|
||||
return this.#responseValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the value for the intermediate response.
|
||||
*
|
||||
* @param {string | null} value
|
||||
*
|
||||
* @throws For an invalid value.
|
||||
*/
|
||||
set responseValue (value) {
|
||||
if (value === null) return
|
||||
if (typeof value !== 'string') {
|
||||
throw Error('responseValue must be a string')
|
||||
}
|
||||
this.#responseValue = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.startSequence(operations.LDAP_RES_INTERMEDIATE)
|
||||
|
||||
if (this.#responseName) {
|
||||
ber.writeString(this.#responseName, 0x80)
|
||||
}
|
||||
if (this.#responseValue) {
|
||||
ber.writeString(this.#responseValue, 0x81)
|
||||
}
|
||||
|
||||
ber.endSequence()
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {object}
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
_pojo (obj = {}) {
|
||||
obj.responseName = this.#responseName
|
||||
obj.responseValue = this.#responseValue
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const protocolOp = ber.readSequence()
|
||||
if (protocolOp !== operations.LDAP_RES_INTERMEDIATE) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
let responseName
|
||||
let responseValue
|
||||
|
||||
let tag = ber.peek()
|
||||
switch (tag) {
|
||||
case 0x80: {
|
||||
responseName = ber.readString(tag)
|
||||
|
||||
tag = ber.peek()
|
||||
/* istanbul ignore else */
|
||||
if (tag === 0x81) {
|
||||
responseValue = ber.readString(tag)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 0x81: {
|
||||
responseValue = ber.readString(tag)
|
||||
}
|
||||
}
|
||||
|
||||
return { protocolOp, responseName, responseValue }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IntermediateResponse
|
||||
191
node_modules/@ldapjs/messages/lib/messages/intermediate-response.test.js
generated
vendored
Normal file
191
node_modules/@ldapjs/messages/lib/messages/intermediate-response.test.js
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const { BerWriter, BerReader } = require('@ldapjs/asn1')
|
||||
const IntermediateResponse = require('./intermediate-response')
|
||||
|
||||
const {
|
||||
intermediateResponseBytes,
|
||||
intermediateResponseNoValueBytes,
|
||||
intermediateResponseNoNameBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
|
||||
tap.test('basic', t => {
|
||||
t.test('constructor no args', async t => {
|
||||
const res = new IntermediateResponse()
|
||||
t.strictSame(res.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_RES_INTERMEDIATE,
|
||||
type: 'IntermediateResponse',
|
||||
responseName: undefined,
|
||||
responseValue: undefined,
|
||||
controls: []
|
||||
})
|
||||
|
||||
t.equal(res.type, 'IntermediateResponse')
|
||||
})
|
||||
|
||||
t.test('constructor with args', async t => {
|
||||
const res = new IntermediateResponse({
|
||||
responseName: '1.2.3',
|
||||
responseValue: 'foo'
|
||||
})
|
||||
t.strictSame(res.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_RES_INTERMEDIATE,
|
||||
type: 'IntermediateResponse',
|
||||
responseName: '1.2.3',
|
||||
responseValue: 'foo',
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.responseName', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const res = new IntermediateResponse()
|
||||
t.equal(res.responseName, undefined)
|
||||
|
||||
res.responseName = '1.2.3'
|
||||
t.equal(res.responseName, '1.2.3')
|
||||
})
|
||||
|
||||
t.test('rejects bad value', async t => {
|
||||
const res = new IntermediateResponse()
|
||||
t.throws(
|
||||
() => {
|
||||
res.responseName = 'foo bar'
|
||||
},
|
||||
'responseName must be a numeric OID'
|
||||
)
|
||||
t.throws(
|
||||
() => {
|
||||
res.responseName = 1.2
|
||||
},
|
||||
'responseName must be a numeric OID'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.responseValue', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const res = new IntermediateResponse()
|
||||
t.equal(res.responseValue, undefined)
|
||||
|
||||
res.responseValue = '1.2.3'
|
||||
t.equal(res.responseValue, '1.2.3')
|
||||
})
|
||||
|
||||
t.test('rejects bad value', async t => {
|
||||
const res = new IntermediateResponse()
|
||||
t.throws(
|
||||
() => {
|
||||
res.responseValue = { foo: 'foo' }
|
||||
},
|
||||
'responseValue must be a string'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_toBer', t => {
|
||||
t.test('converts instance to BER', async t => {
|
||||
let res = new IntermediateResponse({
|
||||
messageId: 2,
|
||||
responseName: '1.2.3',
|
||||
responseValue: 'foo'
|
||||
})
|
||||
let writer = new BerWriter()
|
||||
res._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(intermediateResponseBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
|
||||
res = new IntermediateResponse({
|
||||
messageId: 2,
|
||||
responseName: '1.2.3'
|
||||
})
|
||||
writer = new BerWriter()
|
||||
res._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(intermediateResponseNoValueBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
|
||||
res = new IntermediateResponse({
|
||||
messageId: 2,
|
||||
responseValue: 'foo'
|
||||
})
|
||||
writer = new BerWriter()
|
||||
res._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(intermediateResponseNoNameBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_pojo', t => {
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const req = new IntermediateResponse({
|
||||
responseName: '1.2.3',
|
||||
responseValue: 'foo'
|
||||
})
|
||||
t.strictSame(req._pojo(), {
|
||||
responseName: '1.2.3',
|
||||
responseValue: 'foo'
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws if operation incorrect', async t => {
|
||||
const reqBuffer = Buffer.from(intermediateResponseBytes)
|
||||
reqBuffer[5] = 0x61
|
||||
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
t.throws(
|
||||
() => IntermediateResponse.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x61')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('returns a pojo representation', async t => {
|
||||
let reqBuffer = Buffer.from(intermediateResponseBytes)
|
||||
let reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
let pojo = IntermediateResponse.parseToPojo(reader)
|
||||
t.equal(pojo.protocolOp, operations.LDAP_RES_INTERMEDIATE)
|
||||
t.equal(pojo.responseName, '1.2.3')
|
||||
t.equal(pojo.responseValue, 'foo')
|
||||
|
||||
reqBuffer = Buffer.from(intermediateResponseNoNameBytes)
|
||||
reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
pojo = IntermediateResponse.parseToPojo(reader)
|
||||
t.equal(pojo.responseName, undefined)
|
||||
t.equal(pojo.responseValue, 'foo')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
172
node_modules/@ldapjs/messages/lib/messages/modify-request.js
generated
vendored
Normal file
172
node_modules/@ldapjs/messages/lib/messages/modify-request.js
generated
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
'use strict'
|
||||
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const Change = require('@ldapjs/change')
|
||||
const LdapMessage = require('../ldap-message')
|
||||
|
||||
/**
|
||||
* Implements the MODIFY request message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.6.
|
||||
*
|
||||
* Changes should be in the order of operation as described in
|
||||
* the spec. If sorting is desired, sort the array prior to
|
||||
* adding it to the request.
|
||||
*
|
||||
* @example <caption>Sorting Changes</caption>
|
||||
* const {ModifyRequest} = require('@ldapjs/messages')
|
||||
* const Change = require('@ldapjs/change')
|
||||
* const changes = someArrayOfChanges.sort(Change.sort)
|
||||
* const req = new ModifyRequest({
|
||||
* object: 'dn=foo,dc=example,dc=com',
|
||||
* changes
|
||||
* })
|
||||
*/
|
||||
class ModifyRequest extends LdapMessage {
|
||||
#object
|
||||
#changes
|
||||
|
||||
/**
|
||||
* @typedef {LdapMessageOptions} ModifyRequestOptions
|
||||
* @property {string|null} [object] The LDAP object (DN) to modify.
|
||||
* @property {import('@ldapjs/change')[]} [changes] The set of changes to
|
||||
* apply.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {ModifyRequestOptions} [options]
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_REQ_MODIFY
|
||||
super(options)
|
||||
|
||||
this.#object = options.object || null
|
||||
this.changes = options.changes || []
|
||||
}
|
||||
|
||||
/**
|
||||
* A copy of the set of changes to be applied to the LDAP object.
|
||||
*
|
||||
* @returns {import('@ldapjs/change')[]}
|
||||
*/
|
||||
get changes () {
|
||||
return this.#changes.slice(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the set of changes to apply to the LDAP object.
|
||||
*
|
||||
* @param {import('@ldapjs/change')[]} values
|
||||
*
|
||||
* @throws When `values` is not an array or contains any elements that
|
||||
* are not changes.
|
||||
*/
|
||||
set changes (values) {
|
||||
this.#changes = []
|
||||
if (Array.isArray(values) === false) {
|
||||
throw Error('changes must be an array')
|
||||
}
|
||||
for (let change of values) {
|
||||
if (Change.isChange(change) === false) {
|
||||
throw Error('change must be an instance of Change or a Change-like object')
|
||||
}
|
||||
if (Object.prototype.toString.call(change) !== '[object LdapChange]') {
|
||||
change = new Change(change)
|
||||
}
|
||||
this.#changes.push(change)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The object (DN) to be modified.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
get object () {
|
||||
return this.#object
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the object (DN) to be modified.
|
||||
*
|
||||
* @param {string} value
|
||||
*/
|
||||
set object (value) {
|
||||
this.#object = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'ModifyRequest'
|
||||
}
|
||||
|
||||
get _dn () {
|
||||
return this.#object
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.startSequence(operations.LDAP_REQ_MODIFY)
|
||||
|
||||
ber.writeString(this.#object.toString())
|
||||
ber.startSequence()
|
||||
for (const change of this.#changes) {
|
||||
ber.appendBuffer(change.toBer().buffer)
|
||||
}
|
||||
ber.endSequence()
|
||||
|
||||
ber.endSequence()
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {object}
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
_pojo (obj = {}) {
|
||||
obj.object = this.#object
|
||||
obj.changes = this.#changes.map(c => c.pojo)
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const protocolOp = ber.readSequence()
|
||||
if (protocolOp !== operations.LDAP_REQ_MODIFY) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
const object = ber.readString()
|
||||
const changes = []
|
||||
|
||||
ber.readSequence()
|
||||
const end = ber.offset + ber.length
|
||||
while (ber.offset < end) {
|
||||
const change = Change.fromBer(ber)
|
||||
changes.push(change.pojo)
|
||||
}
|
||||
|
||||
return { protocolOp, object, changes }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ModifyRequest
|
||||
207
node_modules/@ldapjs/messages/lib/messages/modify-request.test.js
generated
vendored
Normal file
207
node_modules/@ldapjs/messages/lib/messages/modify-request.test.js
generated
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const {
|
||||
modifyRequestBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const Attribute = require('@ldapjs/attribute')
|
||||
const Change = require('@ldapjs/change')
|
||||
const ModifyRequest = require('./modify-request')
|
||||
|
||||
tap.test('constructor', t => {
|
||||
t.test('with empty params', async t => {
|
||||
const req = new ModifyRequest()
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_REQ_MODIFY,
|
||||
type: 'ModifyRequest',
|
||||
object: null,
|
||||
changes: [],
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('with invalid changes options', async t => {
|
||||
t.throws(
|
||||
() => new ModifyRequest({ changes: 'foo' }),
|
||||
Error('changes must be an array')
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.changes', t => {
|
||||
t.test('gets a copy of the set of changes', async t => {
|
||||
const changes = [new Change({
|
||||
modification: new Attribute()
|
||||
})]
|
||||
const req = new ModifyRequest({ changes })
|
||||
const found = req.changes
|
||||
t.not(changes, found)
|
||||
t.equal(changes.length, 1)
|
||||
t.equal(Change.isChange(changes[0]), true)
|
||||
})
|
||||
|
||||
t.test('set throws for non-array', async t => {
|
||||
const req = new ModifyRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.changes = 42
|
||||
},
|
||||
Error('changes must be an array')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('throws for non-change in array', async t => {
|
||||
const req = new ModifyRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.changes = [42]
|
||||
},
|
||||
Error('change must be an instance of Change or a Change-like object')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('converts change-likes to changes', async t => {
|
||||
const req = new ModifyRequest()
|
||||
req.changes = [{
|
||||
operation: 'add',
|
||||
modification: {
|
||||
type: 'cn',
|
||||
values: ['foo']
|
||||
}
|
||||
}]
|
||||
t.equal(req.changes.length, 1)
|
||||
t.equal(Object.prototype.toString.call(req.changes[0]), '[object LdapChange]')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.object', t => {
|
||||
t.test('gets and sets', async t => {
|
||||
const req = new ModifyRequest()
|
||||
t.equal(req.object, null)
|
||||
req.object = 'foo'
|
||||
t.equal(req.object, 'foo')
|
||||
t.equal(req.dn, 'foo')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_toBer', t => {
|
||||
t.test('serializes to ber', async t => {
|
||||
const req = new ModifyRequest({
|
||||
messageId: 2,
|
||||
object: 'uid=jdoe,ou=People,dc=example,dc=com',
|
||||
changes: [
|
||||
new Change({
|
||||
operation: 'delete',
|
||||
modification: new Attribute({
|
||||
type: 'givenName',
|
||||
values: ['John']
|
||||
})
|
||||
}),
|
||||
new Change({
|
||||
operation: 'add',
|
||||
modification: new Attribute({
|
||||
type: 'givenName',
|
||||
values: ['Jonathan']
|
||||
})
|
||||
}),
|
||||
new Change({
|
||||
operation: 'replace',
|
||||
modification: new Attribute({
|
||||
type: 'cn',
|
||||
values: ['Jonathan Doe']
|
||||
})
|
||||
})
|
||||
]
|
||||
})
|
||||
const expected = Buffer.from(modifyRequestBytes)
|
||||
const ber = req.toBer()
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_pojo', t => {
|
||||
t.test('serializes to plain object', async t => {
|
||||
const req = new ModifyRequest({
|
||||
object: 'foo',
|
||||
changes: [
|
||||
new Change({
|
||||
modification: new Attribute({
|
||||
type: 'cn',
|
||||
values: ['bar']
|
||||
})
|
||||
})
|
||||
]
|
||||
})
|
||||
t.strictSame(req._pojo(), {
|
||||
object: 'foo',
|
||||
changes: [{
|
||||
operation: 'add',
|
||||
modification: {
|
||||
type: 'cn',
|
||||
values: ['bar']
|
||||
}
|
||||
}]
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws for wrong op', async t => {
|
||||
const bytes = Buffer.from(modifyRequestBytes.slice(6))
|
||||
bytes[0] = 0x61
|
||||
const reader = new BerReader(bytes)
|
||||
t.throws(
|
||||
() => ModifyRequest.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x61')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('parses bytes to an object', async t => {
|
||||
const bytes = Buffer.from(modifyRequestBytes.slice(6))
|
||||
const reader = new BerReader(bytes)
|
||||
const pojo = ModifyRequest.parseToPojo(reader)
|
||||
t.type(pojo, 'Object')
|
||||
t.strictSame(pojo, {
|
||||
protocolOp: operations.LDAP_REQ_MODIFY,
|
||||
object: 'uid=jdoe,ou=People,dc=example,dc=com',
|
||||
changes: [
|
||||
{
|
||||
operation: 'delete',
|
||||
modification: {
|
||||
type: 'givenName',
|
||||
values: ['John']
|
||||
}
|
||||
},
|
||||
{
|
||||
operation: 'add',
|
||||
modification: {
|
||||
type: 'givenName',
|
||||
values: ['Jonathan']
|
||||
}
|
||||
},
|
||||
{
|
||||
operation: 'replace',
|
||||
modification: {
|
||||
type: 'cn',
|
||||
values: ['Jonathan Doe']
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
45
node_modules/@ldapjs/messages/lib/messages/modify-response.js
generated
vendored
Normal file
45
node_modules/@ldapjs/messages/lib/messages/modify-response.js
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
'use strict'
|
||||
|
||||
const LdapResult = require('../ldap-result')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
|
||||
/**
|
||||
* Implements the MODIFY response message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.6.
|
||||
*/
|
||||
class ModifyResponse extends LdapResult {
|
||||
/**
|
||||
* @param {LdapResultOptions} options
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_RES_MODIFY
|
||||
super(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'ModifyResponse'
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
return LdapResult._parseToPojo({
|
||||
opCode: operations.LDAP_RES_MODIFY,
|
||||
berReader: ber
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ModifyResponse
|
||||
55
node_modules/@ldapjs/messages/lib/messages/modify-response.test.js
generated
vendored
Normal file
55
node_modules/@ldapjs/messages/lib/messages/modify-response.test.js
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const { operations, resultCodes } = require('@ldapjs/protocol')
|
||||
const {
|
||||
modifyResponseBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
const ModifyResponse = require('./modify-response')
|
||||
|
||||
tap.test('basic', async t => {
|
||||
const res = new ModifyResponse()
|
||||
t.equal(res.protocolOp, operations.LDAP_RES_MODIFY)
|
||||
t.equal(res.type, 'ModifyResponse')
|
||||
})
|
||||
|
||||
tap.test('toBer', t => {
|
||||
tap.test('returns basic bytes', async t => {
|
||||
const res = new ModifyResponse({
|
||||
messageId: 2,
|
||||
status: resultCodes.SUCCESS
|
||||
})
|
||||
const ber = res.toBer()
|
||||
const expected = Buffer.from(modifyResponseBytes)
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('parses a basic object', async t => {
|
||||
const bytes = modifyResponseBytes.slice(5)
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
const pojo = ModifyResponse.parseToPojo(reader)
|
||||
t.strictSame(pojo, {
|
||||
status: resultCodes.SUCCESS,
|
||||
matchedDN: '',
|
||||
diagnosticMessage: '',
|
||||
referrals: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('throws if protocol op is wrong', async t => {
|
||||
const bytes = modifyResponseBytes.slice(5)
|
||||
bytes[0] = 0x68
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
t.throws(
|
||||
() => ModifyResponse.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x68')
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
222
node_modules/@ldapjs/messages/lib/messages/modifydn-request.js
generated
vendored
Normal file
222
node_modules/@ldapjs/messages/lib/messages/modifydn-request.js
generated
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
'use strict'
|
||||
|
||||
const LdapMessage = require('../ldap-message')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
|
||||
/**
|
||||
* Implements the modifydn request message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.9.
|
||||
*/
|
||||
class ModifyDnRequest extends LdapMessage {
|
||||
#entry
|
||||
#newRdn
|
||||
#deleteOldRdn
|
||||
#newSuperior
|
||||
|
||||
/**
|
||||
* @typedef {LdapMessageOptions} ModifyDnRequestOptions
|
||||
* @property {string|null} [entry=null] The path to the LDAP object.
|
||||
* @property {string|null} [newRdn=null] Path to the new object for the
|
||||
* entry.
|
||||
* @property {boolean} [deleteOldRdn=false] Indicates if attributes
|
||||
* should be removed in the new RDN that were in the old RDN but not the
|
||||
* new one.
|
||||
* @property {string} [newSuperior] Path for the new parent for
|
||||
* the RDN.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {ModifyDnRequestOptions} [options]
|
||||
*
|
||||
* @throws When an option is invalid (e.g. `deleteOldRdn` is not a boolean
|
||||
* value).
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_REQ_MODRDN
|
||||
super(options)
|
||||
|
||||
this.entry = options.entry || ''
|
||||
this.newRdn = options.newRdn || ''
|
||||
this.deleteOldRdn = options.deleteOldRdn ?? false
|
||||
this.newSuperior = options.newSuperior
|
||||
}
|
||||
|
||||
/**
|
||||
* The directory path to the object to modify.
|
||||
*
|
||||
* @type {import('@ldapjs/dn').DN}
|
||||
*/
|
||||
get entry () {
|
||||
return this.#entry
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the entry path to the LDAP object.
|
||||
*
|
||||
* @param {string | import('@ldapjs/dn').dn} value
|
||||
*/
|
||||
set entry (value) {
|
||||
if (typeof value === 'string') {
|
||||
this.#entry = DN.fromString(value)
|
||||
} else if (Object.prototype.toString.call(value) === '[object LdapDn]') {
|
||||
this.#entry = value
|
||||
} else {
|
||||
throw Error('entry must be a valid DN string or instance of LdapDn')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of {@link entry}.
|
||||
*
|
||||
* @type {import('@ldapjs/dn').DN}
|
||||
*/
|
||||
get _dn () {
|
||||
return this.#entry
|
||||
}
|
||||
|
||||
/**
|
||||
* The new directory path for the object.
|
||||
*
|
||||
* @returns {import('@ldapjs/dn').DN}
|
||||
*/
|
||||
get newRdn () {
|
||||
return this.#newRdn
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the new entry path to the LDAP object.
|
||||
*
|
||||
* @param {string | import('@ldapjs/dn').DN} value
|
||||
*/
|
||||
set newRdn (value) {
|
||||
if (typeof value === 'string') {
|
||||
this.#newRdn = DN.fromString(value)
|
||||
} else if (Object.prototype.toString.call(value) === '[object LdapDn]') {
|
||||
this.#newRdn = value
|
||||
} else {
|
||||
throw Error('newRdn must be a valid DN string or instance of LdapDn')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the old RDN should be removed or not.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get deleteOldRdn () {
|
||||
return this.#deleteOldRdn
|
||||
}
|
||||
|
||||
set deleteOldRdn (value) {
|
||||
if (typeof value !== 'boolean') {
|
||||
throw Error('deleteOldRdn must be a boolean value')
|
||||
}
|
||||
this.#deleteOldRdn = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The new superior for the entry, if any is defined.
|
||||
*
|
||||
* @returns {undefined | import('@ldapjs/dn').DN}
|
||||
*/
|
||||
get newSuperior () {
|
||||
return this.#newSuperior
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the new superior path.
|
||||
*
|
||||
* @param {undefined | string | import('@ldapjs/dn').DN} value
|
||||
*/
|
||||
set newSuperior (value) {
|
||||
if (value) {
|
||||
if (typeof value === 'string') {
|
||||
this.#newSuperior = DN.fromString(value)
|
||||
} else if (Object.prototype.toString.call(value) === '[object LdapDn]') {
|
||||
this.#newSuperior = value
|
||||
} else {
|
||||
throw Error('newSuperior must be a valid DN string or instance of LdapDn')
|
||||
}
|
||||
} else {
|
||||
this.#newSuperior = undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'ModifyDnRequest'
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.startSequence(operations.LDAP_REQ_MODRDN)
|
||||
|
||||
ber.writeString(this.#entry.toString())
|
||||
ber.writeString(this.#newRdn.toString())
|
||||
ber.writeBoolean(this.#deleteOldRdn)
|
||||
/* istanbul ignore else */
|
||||
if (this.#newSuperior !== undefined) {
|
||||
ber.writeString(this.#newSuperior.toString(), 0x80)
|
||||
}
|
||||
|
||||
ber.endSequence()
|
||||
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {object}
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
_pojo (obj = {}) {
|
||||
obj.entry = this.#entry.toString()
|
||||
obj.newRdn = this.#newRdn.toString()
|
||||
obj.deleteOldRdn = this.#deleteOldRdn
|
||||
obj.newSuperior = this.#newSuperior ? this.#newSuperior.toString() : undefined
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const protocolOp = ber.readSequence()
|
||||
if (protocolOp !== operations.LDAP_REQ_MODRDN) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
const entry = ber.readString()
|
||||
const newRdn = ber.readString()
|
||||
const deleteOldRdn = ber.readBoolean()
|
||||
let newSuperior
|
||||
/* istanbul ignore else */
|
||||
if (ber.peek() === 0x80) {
|
||||
newSuperior = ber.readString(0x80)
|
||||
}
|
||||
|
||||
return { protocolOp, entry, newRdn, deleteOldRdn, newSuperior }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ModifyDnRequest
|
||||
233
node_modules/@ldapjs/messages/lib/messages/modifydn-request.test.js
generated
vendored
Normal file
233
node_modules/@ldapjs/messages/lib/messages/modifydn-request.test.js
generated
vendored
Normal file
@ -0,0 +1,233 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
const ModifyDnRequest = require('./modifydn-request')
|
||||
|
||||
const {
|
||||
modifyDnRequestBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
|
||||
tap.test('basic', t => {
|
||||
t.test('constructor no args', async t => {
|
||||
const req = new ModifyDnRequest()
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_REQ_MODRDN,
|
||||
type: 'ModifyDnRequest',
|
||||
entry: '',
|
||||
newRdn: '',
|
||||
deleteOldRdn: false,
|
||||
newSuperior: undefined,
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('constructor with args', async t => {
|
||||
const req = new ModifyDnRequest({
|
||||
entry: 'dc=to-move,dc=example,dc=com',
|
||||
newRdn: 'dc=moved,dc=example,dc=com',
|
||||
deleteOldRdn: true,
|
||||
newSuperior: 'dc=example,dc=net'
|
||||
})
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_REQ_MODRDN,
|
||||
type: 'ModifyDnRequest',
|
||||
entry: 'dc=to-move,dc=example,dc=com',
|
||||
newRdn: 'dc=moved,dc=example,dc=com',
|
||||
deleteOldRdn: true,
|
||||
newSuperior: 'dc=example,dc=net',
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('.type', async t => {
|
||||
const req = new ModifyDnRequest()
|
||||
t.equal(req.type, 'ModifyDnRequest')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.entry', t => {
|
||||
t.test('sets and gets', async t => {
|
||||
const req = new ModifyDnRequest()
|
||||
|
||||
req.entry = 'foo=bar'
|
||||
t.equal(Object.prototype.toString.call(req.entry), '[object LdapDn]')
|
||||
t.equal(req.entry.toString(), 'foo=bar')
|
||||
t.equal(req._dn.toString(), 'foo=bar')
|
||||
|
||||
req.entry = DN.fromString('cn=foo')
|
||||
t.equal(req.entry.toString(), 'cn=foo')
|
||||
})
|
||||
|
||||
t.test('throws for bad value', async t => {
|
||||
const req = new ModifyDnRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.entry = { cn: 'foo' }
|
||||
},
|
||||
'entry must be a valid DN string or instance of LdapDN'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.newRdn', t => {
|
||||
t.test('sets and gets', async t => {
|
||||
const req = new ModifyDnRequest()
|
||||
|
||||
req.newRdn = 'foo=bar'
|
||||
t.equal(Object.prototype.toString.call(req.newRdn), '[object LdapDn]')
|
||||
t.equal(req.newRdn.toString(), 'foo=bar')
|
||||
|
||||
req.newRdn = DN.fromString('cn=foo')
|
||||
t.equal(req.newRdn.toString(), 'cn=foo')
|
||||
})
|
||||
|
||||
t.test('throws for bad value', async t => {
|
||||
const req = new ModifyDnRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.newRdn = { cn: 'foo' }
|
||||
},
|
||||
'newRdn must be a valid DN string or instance of LdapDN'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.deleteOldRdn', t => {
|
||||
t.test('throws for wrong type', async t => {
|
||||
t.throws(
|
||||
() => new ModifyDnRequest({ deleteOldRdn: 'false' }),
|
||||
'deleteOldRdn must be a boolean value'
|
||||
)
|
||||
})
|
||||
|
||||
t.test('sets and gets', async t => {
|
||||
const req = new ModifyDnRequest()
|
||||
t.equal(req.deleteOldRdn, false)
|
||||
|
||||
req.deleteOldRdn = true
|
||||
t.type(req.deleteOldRdn, 'boolean')
|
||||
t.equal(req.deleteOldRdn, true)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.newSuperior', t => {
|
||||
t.test('sets and gets', async t => {
|
||||
const req = new ModifyDnRequest()
|
||||
|
||||
req.newSuperior = 'foo=bar'
|
||||
t.equal(Object.prototype.toString.call(req.newSuperior), '[object LdapDn]')
|
||||
t.equal(req.newSuperior.toString(), 'foo=bar')
|
||||
|
||||
req.newSuperior = null
|
||||
t.equal(req.newSuperior, undefined)
|
||||
|
||||
req.newSuperior = DN.fromString('cn=foo')
|
||||
t.equal(req.newSuperior.toString(), 'cn=foo')
|
||||
})
|
||||
|
||||
t.test('throws for bad value', async t => {
|
||||
const req = new ModifyDnRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.newSuperior = { cn: 'foo' }
|
||||
},
|
||||
'newSuperior must be a valid DN string or instance of LdapDN'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_toBer', async t => {
|
||||
t.test('converts instance to BER', async t => {
|
||||
const req = new ModifyDnRequest({
|
||||
entry: 'uid=jdoe,ou=People,dc=example,dc=com',
|
||||
newRdn: 'uid=john.doe',
|
||||
deleteOldRdn: true,
|
||||
newSuperior: 'ou=Users,dc=example,dc=com'
|
||||
})
|
||||
const writer = new BerWriter()
|
||||
req._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(modifyDnRequestBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_pojo', t => {
|
||||
t.test('returns a pojo representation', async t => {
|
||||
let req = new ModifyDnRequest({
|
||||
entry: 'cn=bar,dc=example,dc=com',
|
||||
newRdn: 'cn=foo'
|
||||
})
|
||||
t.strictSame(req._pojo(), {
|
||||
entry: 'cn=bar,dc=example,dc=com',
|
||||
newRdn: 'cn=foo',
|
||||
deleteOldRdn: false,
|
||||
newSuperior: undefined
|
||||
})
|
||||
|
||||
req = new ModifyDnRequest({
|
||||
entry: 'cn=bar,dc=example,dc=com',
|
||||
newRdn: 'cn=foo',
|
||||
newSuperior: 'ou=people,dc=example,dc=com'
|
||||
})
|
||||
t.strictSame(req._pojo(), {
|
||||
entry: 'cn=bar,dc=example,dc=com',
|
||||
newRdn: 'cn=foo',
|
||||
deleteOldRdn: false,
|
||||
newSuperior: 'ou=people,dc=example,dc=com'
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws if operation incorrect', async t => {
|
||||
const reqBuffer = Buffer.from(modifyDnRequestBytes)
|
||||
reqBuffer[5] = 0x61
|
||||
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
t.throws(
|
||||
() => ModifyDnRequest.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x61')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const reqBuffer = Buffer.from(modifyDnRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = ModifyDnRequest.parseToPojo(reader)
|
||||
t.equal(pojo.protocolOp, operations.LDAP_REQ_MODRDN)
|
||||
t.equal(pojo.entry, 'uid=jdoe,ou=People,dc=example,dc=com')
|
||||
t.equal(pojo.newRdn, 'uid=john.doe')
|
||||
t.equal(pojo.deleteOldRdn, true)
|
||||
t.equal(pojo.newSuperior, 'ou=Users,dc=example,dc=com')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
45
node_modules/@ldapjs/messages/lib/messages/modifydn-response.js
generated
vendored
Normal file
45
node_modules/@ldapjs/messages/lib/messages/modifydn-response.js
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
'use strict'
|
||||
|
||||
const LdapResult = require('../ldap-result')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
|
||||
/**
|
||||
* Implements the modifydn response message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.9.
|
||||
*/
|
||||
class ModifyDnResponse extends LdapResult {
|
||||
/**
|
||||
* @param {LdapResultOptions} options
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_RES_MODRDN
|
||||
super(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'ModifyDnResponse'
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
return LdapResult._parseToPojo({
|
||||
opCode: operations.LDAP_RES_MODRDN,
|
||||
berReader: ber
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ModifyDnResponse
|
||||
56
node_modules/@ldapjs/messages/lib/messages/modifydn-response.test.js
generated
vendored
Normal file
56
node_modules/@ldapjs/messages/lib/messages/modifydn-response.test.js
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const {
|
||||
modifyDnResponseBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
const ModifyDnResponse = require('./modifydn-response')
|
||||
|
||||
tap.test('basic', async t => {
|
||||
const res = new ModifyDnResponse()
|
||||
t.equal(res.protocolOp, operations.LDAP_RES_MODRDN)
|
||||
t.equal(res.type, 'ModifyDnResponse')
|
||||
})
|
||||
|
||||
tap.test('toBer', t => {
|
||||
tap.test('returns basic bytes', async t => {
|
||||
const res = new ModifyDnResponse({ messageId: 2 })
|
||||
const ber = res.toBer()
|
||||
const expected = Buffer.from(modifyDnResponseBytes)
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.comment('see the LdapResult test suite for further tests')
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('parses a basic object', async t => {
|
||||
const bytes = modifyDnResponseBytes.slice(5)
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
const pojo = ModifyDnResponse.parseToPojo(reader)
|
||||
t.strictSame(pojo, {
|
||||
status: 0,
|
||||
matchedDN: '',
|
||||
diagnosticMessage: '',
|
||||
referrals: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('throws if protocol op is wrong', async t => {
|
||||
const bytes = modifyDnResponseBytes.slice(5)
|
||||
bytes[0] = 0x68
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
t.throws(
|
||||
() => ModifyDnResponse.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x68')
|
||||
)
|
||||
})
|
||||
|
||||
t.comment('see the LdapResult test suite for further tests')
|
||||
|
||||
t.end()
|
||||
})
|
||||
515
node_modules/@ldapjs/messages/lib/messages/search-request.js
generated
vendored
Normal file
515
node_modules/@ldapjs/messages/lib/messages/search-request.js
generated
vendored
Normal file
@ -0,0 +1,515 @@
|
||||
'use strict'
|
||||
|
||||
const LdapMessage = require('../ldap-message')
|
||||
const { operations, search } = require('@ldapjs/protocol')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
const filter = require('@ldapjs/filter')
|
||||
const { BerReader, BerTypes } = require('@ldapjs/asn1')
|
||||
const warning = require('../deprecations')
|
||||
|
||||
const recognizedScopes = new Map([
|
||||
['base', [search.SCOPE_BASE_OBJECT, 'base']],
|
||||
['single', [search.SCOPE_ONE_LEVEL, 'single', 'one']],
|
||||
['subtree', [search.SCOPE_SUBTREE, 'subtree', 'sub']]
|
||||
])
|
||||
const scopeAliasToScope = alias => {
|
||||
alias = typeof alias === 'string' ? alias.toLowerCase() : alias
|
||||
if (recognizedScopes.has(alias)) {
|
||||
return recognizedScopes.get(alias)[0]
|
||||
}
|
||||
for (const value of recognizedScopes.values()) {
|
||||
if (value.includes(alias)) {
|
||||
return value[0]
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
const isValidAttributeString = str => {
|
||||
// special filter strings
|
||||
if (['*', '1.1', '+'].includes(str) === true) {
|
||||
return true
|
||||
}
|
||||
// "@<object_clas>"
|
||||
if (/^@[a-zA-Z][\w\d.-]*$/.test(str) === true) {
|
||||
return true
|
||||
}
|
||||
// ascii attribute names per RFC 4512 §2.5
|
||||
if (/^[a-zA-Z][\w\d.;-]*$/.test(str) === true) {
|
||||
return true
|
||||
}
|
||||
// Matches the non-standard `range=<low>-<high>` ActiveDirectory
|
||||
// extension as described in §3.1.1.3.1.3.3 (revision 57.0) of
|
||||
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/d2435927-0999-4c62-8c6d-13ba31a52e1a.
|
||||
if (/^[a-zA-Z][\w\d.-]*(;[\w\d.-]+)*;range=\d+-(\d+|\*)(;[\w\d.-]+)*$/.test(str) === true) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the add request message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.5.1.
|
||||
*
|
||||
* Various constants for searching and options can be used from the `search`
|
||||
* object in the `@ldapjs/protocol` package. The same constants are exported
|
||||
* here as static properties for convenience.
|
||||
*/
|
||||
class SearchRequest extends LdapMessage {
|
||||
/**
|
||||
* Limit searches to the specified {@link baseObject}.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
static SCOPE_BASE = search.SCOPE_BASE_OBJECT
|
||||
|
||||
/**
|
||||
* Limit searches to the immediate children of the specified
|
||||
* {@link baseObject}.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
static SCOPE_SINGLE = search.SCOPE_ONE_LEVEL
|
||||
|
||||
/**
|
||||
* Limit searches to the {@link baseObject} and all descendents of that
|
||||
* object.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
static SCOPE_SUBTREE = search.SCOPE_SUBTREE
|
||||
|
||||
/**
|
||||
* Do not perform any dereferencing of aliases at all.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
static DEREF_ALIASES_NEVER = search.NEVER_DEREF_ALIASES
|
||||
|
||||
/**
|
||||
* Dereference aliases in subordinate searches of the {@link baseObject}.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
static DEREF_IN_SEARCHING = search.DEREF_IN_SEARCHING
|
||||
|
||||
/**
|
||||
* Dereference aliases when finding the base object only.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
static DEREF_BASE_OBJECT = search.DEREF_BASE_OBJECT
|
||||
|
||||
/**
|
||||
* Dereference aliases when finding the base object and when searching
|
||||
* subordinates.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
static DEREF_ALWAYS = search.DEREF_ALWAYS
|
||||
|
||||
#baseObject
|
||||
#scope
|
||||
#derefAliases
|
||||
#sizeLimit
|
||||
#timeLimit
|
||||
#typesOnly
|
||||
#filter
|
||||
#attributes = []
|
||||
|
||||
/**
|
||||
* @typedef {LdapMessageOptions} SearchRequestOptions
|
||||
* @property {string | import('@ldapjs/dn').DN} baseObject The path to the
|
||||
* LDAP object that will serve as the basis of the search.
|
||||
* @property {number | string} scope The type of search to be performed.
|
||||
* May be one of {@link SCOPE_BASE}, {@link SCOPE_SINGLE},
|
||||
* {@link SCOPE_SUBTREE}, `'base'`, `'single'` (`'one'`), or `'subtree'`
|
||||
* (`'sub'`).
|
||||
* @property {number} derefAliases Indicates if aliases should be dereferenced
|
||||
* during searches. May be one of {@link DEREF_ALIASES_NEVER},
|
||||
* {@link DEREF_BASE_OBJECT}, {@link DEREF_IN_SEARCHING}, or
|
||||
* {@link DEREF_ALWAYS}.
|
||||
* @property {number} sizeLimit The number of search results the server should
|
||||
* limit the result set to. `0` indicates no desired limit.
|
||||
* @property {number} timeLimit The number of seconds the server should work
|
||||
* before aborting the search request. `0` indicates no desired limit.
|
||||
* @property {boolean} typesOnly Indicates if only attribute names should
|
||||
* be returned (`true`), or both names and values should be returned (`false`).
|
||||
* @property {string | import('@ldapjs/filter').FilterString} filter The
|
||||
* filter to apply when searching.
|
||||
* @property {string[]} attributes A set of attribute filtering strings
|
||||
* to apply. See the docs for the {@link attributes} setter.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {SearchRequestOptions} options
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_REQ_SEARCH
|
||||
super(options)
|
||||
|
||||
this.baseObject = options.baseObject ?? ''
|
||||
this.scope = options.scope ?? search.SCOPE_BASE_OBJECT
|
||||
this.derefAliases = options.derefAliases ?? search.NEVER_DEREF_ALIASES
|
||||
this.sizeLimit = options.sizeLimit ?? 0
|
||||
this.timeLimit = options.timeLimit ?? 0
|
||||
this.typesOnly = options.typesOnly ?? false
|
||||
this.filter = options.filter ?? new filter.PresenceFilter({ attribute: 'objectclass' })
|
||||
this.attributes = options.attributes ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of {@link baseObject}.
|
||||
*
|
||||
* @type {import('@ldapjs/dn').DN}
|
||||
*/
|
||||
get _dn () {
|
||||
return this.#baseObject
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'SearchRequest'
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of attributes to match against.
|
||||
*
|
||||
* @returns {string[]}
|
||||
*/
|
||||
get attributes () {
|
||||
return this.#attributes
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of attributes to match against. Overwrites any existing
|
||||
* attributes. The list is a set of spec defined strings. They are not
|
||||
* instances of `@ldapjs/attribute`.
|
||||
*
|
||||
* See:
|
||||
* + https://www.rfc-editor.org/rfc/rfc4511.html#section-4.5.1.8
|
||||
* + https://www.rfc-editor.org/rfc/rfc3673.html
|
||||
* + https://www.rfc-editor.org/rfc/rfc4529.html
|
||||
*
|
||||
* @param {string)[]} attrs
|
||||
*/
|
||||
set attributes (attrs) {
|
||||
if (Array.isArray(attrs) === false) {
|
||||
throw Error('attributes must be an array of attribute strings')
|
||||
}
|
||||
const newAttrs = []
|
||||
for (const attr of attrs) {
|
||||
if (typeof attr === 'string' && isValidAttributeString(attr) === true) {
|
||||
newAttrs.push(attr)
|
||||
} else if (typeof attr === 'string' && attr === '') {
|
||||
// TODO: emit warning about spec violation via log and/or telemetry
|
||||
warning.emit('LDAP_ATTRIBUTE_SPEC_ERR_001')
|
||||
} else {
|
||||
throw Error('attribute must be a valid string')
|
||||
}
|
||||
}
|
||||
this.#attributes = newAttrs
|
||||
}
|
||||
|
||||
/**
|
||||
* The base LDAP object that the search will start from.
|
||||
*
|
||||
* @returns {import('@ldapjs/dn').DN}
|
||||
*/
|
||||
get baseObject () {
|
||||
return this.#baseObject
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the base LDAP object to start searches from.
|
||||
*
|
||||
* @param {string | import('@ldapjs/dn').DN} obj
|
||||
*/
|
||||
set baseObject (obj) {
|
||||
if (typeof obj === 'string') {
|
||||
this.#baseObject = DN.fromString(obj)
|
||||
} else if (Object.prototype.toString.call(obj) === '[object LdapDn]') {
|
||||
this.#baseObject = obj
|
||||
} else {
|
||||
throw Error('baseObject must be a DN string or DN instance')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The alias dereferencing method that will be provided to the server.
|
||||
* May be one of {@link DEREF_ALIASES_NEVER}, {@link DEREF_IN_SEARCHING},
|
||||
* {@link DEREF_BASE_OBJECT},or {@link DEREF_ALWAYS}.
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get derefAliases () {
|
||||
return this.#derefAliases
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the dereferencing method that will be provided to the server.
|
||||
* May be one of {@link DEREF_ALIASES_NEVER}, {@link DEREF_IN_SEARCHING},
|
||||
* {@link DEREF_BASE_OBJECT},or {@link DEREF_ALWAYS}.
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set derefAliases (value) {
|
||||
if (Number.isInteger(value) === false) {
|
||||
throw Error('derefAliases must be set to an integer')
|
||||
}
|
||||
this.#derefAliases = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter that will be used in the search.
|
||||
*
|
||||
* @returns {import('@ldapjs/filter').FilterString}
|
||||
*/
|
||||
get filter () {
|
||||
return this.#filter
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the filter to use in the search.
|
||||
*
|
||||
* @param {string | import('@ldapjs/filter').FilterString} value
|
||||
*/
|
||||
set filter (value) {
|
||||
if (
|
||||
typeof value !== 'string' &&
|
||||
Object.prototype.toString.call(value) !== '[object FilterString]'
|
||||
) {
|
||||
throw Error('filter must be a string or a FilterString instance')
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
this.#filter = filter.parseString(value)
|
||||
} else {
|
||||
this.#filter = value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The current search scope value. Can be matched against the exported
|
||||
* scope statics.
|
||||
*
|
||||
* @returns {number}
|
||||
*
|
||||
* @throws When the scope is set to an unrecognized scope constant.
|
||||
*/
|
||||
get scope () {
|
||||
return this.#scope
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the scope of the search.
|
||||
*
|
||||
* @param {number|string} value Accepts one of {@link SCOPE_BASE},
|
||||
* {@link SCOPE_SINGLE}, or {@link SCOPE_SUBTREE}. Or, as a string, one of
|
||||
* "base", "single", "one", "subtree", or "sub".
|
||||
*
|
||||
* @throws When the provided scope does not resolve to a recognized scope.
|
||||
*/
|
||||
set scope (value) {
|
||||
const resolvedScope = scopeAliasToScope(value)
|
||||
if (resolvedScope === undefined) {
|
||||
throw Error(value + ' is an invalid search scope')
|
||||
}
|
||||
this.#scope = resolvedScope
|
||||
}
|
||||
|
||||
/**
|
||||
* The current search scope value as a string name.
|
||||
*
|
||||
* @returns {string} One of 'base', 'single', or 'subtree'.
|
||||
*
|
||||
* @throws When the scope is set to an unrecognized scope constant.
|
||||
*/
|
||||
get scopeName () {
|
||||
switch (this.#scope) {
|
||||
case search.SCOPE_BASE_OBJECT:
|
||||
return 'base'
|
||||
case search.SCOPE_ONE_LEVEL:
|
||||
return 'single'
|
||||
case search.SCOPE_SUBTREE:
|
||||
return 'subtree'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of entries to limit search results to.
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get sizeLimit () {
|
||||
return this.#sizeLimit
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the number of entries to limit search results to.
|
||||
*
|
||||
* @param {number} value `0` indicates no restriction.
|
||||
*/
|
||||
set sizeLimit (value) {
|
||||
if (Number.isInteger(value) === false) {
|
||||
throw Error('sizeLimit must be an integer')
|
||||
}
|
||||
this.#sizeLimit = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of seconds that the search should be limited to for execution.
|
||||
* A value of `0` indicates a willingness to wait as long as the server is
|
||||
* willing to work.
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get timeLimit () {
|
||||
return this.#timeLimit
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the number of seconds to wait for a search result before the server
|
||||
* should abort the search.
|
||||
*
|
||||
* @param {number} value `0` indicates no time limit restriction.
|
||||
*/
|
||||
set timeLimit (value) {
|
||||
if (Number.isInteger(value) === false) {
|
||||
throw Error('timeLimit must be an integer')
|
||||
}
|
||||
this.#timeLimit = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if only attribute names (`true`) should be returned, or if both
|
||||
* attribute names and attribute values (`false`) should be returned.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get typesOnly () {
|
||||
return this.#typesOnly
|
||||
}
|
||||
|
||||
/**
|
||||
* Define if the search results should include only the attributes names
|
||||
* or attribute names and attribute values.
|
||||
*
|
||||
* @param {boolean} value `false` for both names and values, `true` for
|
||||
* names only.
|
||||
*/
|
||||
set typesOnly (value) {
|
||||
if (typeof value !== 'boolean') {
|
||||
throw Error('typesOnly must be set to a boolean value')
|
||||
}
|
||||
this.#typesOnly = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.startSequence(operations.LDAP_REQ_SEARCH)
|
||||
|
||||
ber.writeString(this.#baseObject.toString())
|
||||
ber.writeEnumeration(this.#scope)
|
||||
ber.writeEnumeration(this.#derefAliases)
|
||||
ber.writeInt(this.#sizeLimit)
|
||||
ber.writeInt(this.#timeLimit)
|
||||
ber.writeBoolean(this.#typesOnly)
|
||||
ber.appendBuffer(this.#filter.toBer().buffer)
|
||||
|
||||
ber.startSequence(BerTypes.Sequence | BerTypes.Constructor)
|
||||
for (const attr of this.#attributes) {
|
||||
ber.writeString(attr)
|
||||
}
|
||||
ber.endSequence()
|
||||
|
||||
ber.endSequence()
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {object}
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
_pojo (obj = {}) {
|
||||
obj.baseObject = this.baseObject.toString()
|
||||
obj.scope = this.scopeName
|
||||
obj.derefAliases = this.derefAliases
|
||||
obj.sizeLimit = this.sizeLimit
|
||||
obj.timeLimit = this.timeLimit
|
||||
obj.typesOnly = this.typesOnly
|
||||
obj.filter = this.filter.toString()
|
||||
|
||||
obj.attributes = []
|
||||
for (const attr of this.#attributes) {
|
||||
obj.attributes.push(attr)
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const protocolOp = ber.readSequence()
|
||||
if (protocolOp !== operations.LDAP_REQ_SEARCH) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
const baseObject = ber.readString()
|
||||
const scope = ber.readEnumeration()
|
||||
const derefAliases = ber.readEnumeration()
|
||||
const sizeLimit = ber.readInt()
|
||||
const timeLimit = ber.readInt()
|
||||
const typesOnly = ber.readBoolean()
|
||||
|
||||
const filterTag = ber.peek()
|
||||
const filterBuffer = ber.readRawBuffer(filterTag)
|
||||
const parsedFilter = filter.parseBer(new BerReader(filterBuffer))
|
||||
|
||||
const attributes = []
|
||||
// Advance to the first attribute sequence in the set
|
||||
// of attribute sequences.
|
||||
ber.readSequence()
|
||||
const endOfAttributesPos = ber.offset + ber.length
|
||||
while (ber.offset < endOfAttributesPos) {
|
||||
const attribute = ber.readString()
|
||||
attributes.push(attribute)
|
||||
}
|
||||
|
||||
return {
|
||||
protocolOp,
|
||||
baseObject,
|
||||
scope,
|
||||
derefAliases,
|
||||
sizeLimit,
|
||||
timeLimit,
|
||||
typesOnly,
|
||||
filter: parsedFilter.toString(),
|
||||
attributes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SearchRequest
|
||||
438
node_modules/@ldapjs/messages/lib/messages/search-request.test.js
generated
vendored
Normal file
438
node_modules/@ldapjs/messages/lib/messages/search-request.test.js
generated
vendored
Normal file
@ -0,0 +1,438 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const filter = require('@ldapjs/filter')
|
||||
const SearchRequest = require('./search-request')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
||||
const warning = require('../deprecations')
|
||||
|
||||
// Silence the standard warning logs. We will test the messages explicitly.
|
||||
process.removeAllListeners('warning')
|
||||
|
||||
const {
|
||||
searchRequestBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
|
||||
tap.test('basic', t => {
|
||||
t.test('constructor no args', async t => {
|
||||
const req = new SearchRequest()
|
||||
const pojo = req.pojo
|
||||
t.strictSame(pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_REQ_SEARCH,
|
||||
type: 'SearchRequest',
|
||||
baseObject: '',
|
||||
scope: 'base',
|
||||
derefAliases: SearchRequest.DEREF_ALIASES_NEVER,
|
||||
sizeLimit: 0,
|
||||
timeLimit: 0,
|
||||
typesOnly: false,
|
||||
filter: '(objectclass=*)',
|
||||
attributes: [],
|
||||
controls: []
|
||||
})
|
||||
|
||||
t.equal(req.type, 'SearchRequest')
|
||||
})
|
||||
|
||||
t.test('constructor with args', async t => {
|
||||
const req = new SearchRequest({
|
||||
baseObject: 'cn=foo,dc=example,dc=com',
|
||||
scope: SearchRequest.SCOPE_SUBTREE,
|
||||
derefAliases: SearchRequest.DEREF_BASE_OBJECT,
|
||||
sizeLimit: 1,
|
||||
timeLimit: 1,
|
||||
typesOnly: true,
|
||||
filter: new filter.EqualityFilter({ attribute: 'cn', value: 'foo' }),
|
||||
attributes: ['*']
|
||||
})
|
||||
const pojo = req.pojo
|
||||
t.strictSame(pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_REQ_SEARCH,
|
||||
type: 'SearchRequest',
|
||||
baseObject: 'cn=foo,dc=example,dc=com',
|
||||
scope: 'subtree',
|
||||
derefAliases: SearchRequest.DEREF_BASE_OBJECT,
|
||||
sizeLimit: 1,
|
||||
timeLimit: 1,
|
||||
typesOnly: true,
|
||||
filter: '(cn=foo)',
|
||||
attributes: ['*'],
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.attributes', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.strictSame(req.attributes, [])
|
||||
|
||||
req.attributes = ['*']
|
||||
t.strictSame(req.attributes, ['*'])
|
||||
})
|
||||
|
||||
t.test('set overwrites current list', async t => {
|
||||
const req = new SearchRequest({
|
||||
attributes: ['1.1']
|
||||
})
|
||||
req.attributes = ['1.1', '*', '@foo3-bar.2', 'cn', 'sn;lang-en']
|
||||
|
||||
t.strictSame(req.attributes, ['1.1', '*', '@foo3-bar.2', 'cn', 'sn;lang-en'])
|
||||
})
|
||||
|
||||
t.test('throws if not an array', async t => {
|
||||
t.throws(
|
||||
() => new SearchRequest({ attributes: '*' }),
|
||||
'attributes must be an array of attribute strings'
|
||||
)
|
||||
})
|
||||
|
||||
t.test('throws if array contains non-attribute', async t => {
|
||||
const input = [
|
||||
'*',
|
||||
'not allowed'
|
||||
]
|
||||
t.throws(
|
||||
() => new SearchRequest({ attributes: input }),
|
||||
'attribute must be a valid string'
|
||||
)
|
||||
})
|
||||
|
||||
t.test('supports single character names (issue #2)', async t => {
|
||||
const req = new SearchRequest({
|
||||
attributes: ['a']
|
||||
})
|
||||
t.strictSame(req.attributes, ['a'])
|
||||
})
|
||||
|
||||
t.test('supports multiple attribute options', async t => {
|
||||
const req = new SearchRequest({
|
||||
attributes: ['abc;lang-en;lang-es']
|
||||
})
|
||||
t.strictSame(req.attributes, ['abc;lang-en;lang-es'])
|
||||
})
|
||||
|
||||
t.test('supports range options', async t => {
|
||||
const req = new SearchRequest({
|
||||
attributes: [
|
||||
'a;range=0-*',
|
||||
'abc;range=100-200',
|
||||
'def;range=0-5;lang-en',
|
||||
'ghi;lang-en;range=6-10',
|
||||
'jkl;lang-en;range=11-15;lang-es'
|
||||
]
|
||||
})
|
||||
t.strictSame(req.attributes, [
|
||||
'a;range=0-*',
|
||||
'abc;range=100-200',
|
||||
'def;range=0-5;lang-en',
|
||||
'ghi;lang-en;range=6-10',
|
||||
'jkl;lang-en;range=11-15;lang-es'
|
||||
])
|
||||
})
|
||||
|
||||
t.test('throws if array contains an invalid range', async t => {
|
||||
const input = ['a;range=*-100']
|
||||
t.throws(
|
||||
() => new SearchRequest({ attributes: input }),
|
||||
'attribute must be a valid string'
|
||||
)
|
||||
})
|
||||
|
||||
t.test('skip empty attribute name', async t => {
|
||||
process.on('warning', handler)
|
||||
t.teardown(async () => {
|
||||
process.removeListener('warning', handler)
|
||||
warning.emitted.set('LDAP_ATTRIBUTE_SPEC_ERR_001', false)
|
||||
})
|
||||
|
||||
const req = new SearchRequest({
|
||||
attributes: ['abc', '']
|
||||
})
|
||||
t.strictSame(req.attributes, ['abc'])
|
||||
|
||||
function handler (error) {
|
||||
t.equal(error.message, 'received attempt to define attribute with an empty name: attribute skipped.')
|
||||
t.end()
|
||||
}
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.baseObject', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.equal(req.baseObject.toString(), '')
|
||||
|
||||
req.baseObject = 'dc=example,dc=com'
|
||||
t.equal(req.baseObject.toString(), 'dc=example,dc=com')
|
||||
|
||||
req.baseObject = DN.fromString('dc=example,dc=net')
|
||||
t.equal(req.baseObject.toString(), 'dc=example,dc=net')
|
||||
t.equal(req._dn.toString(), 'dc=example,dc=net')
|
||||
})
|
||||
|
||||
t.test('throws for non-DN object', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.baseObject = ['foo']
|
||||
},
|
||||
'baseObject must be a DN string or DN instance'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.derefAliases', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.equal(req.derefAliases, SearchRequest.DEREF_ALIASES_NEVER)
|
||||
|
||||
req.derefAliases = SearchRequest.DEREF_ALWAYS
|
||||
t.equal(req.derefAliases, SearchRequest.DEREF_ALWAYS)
|
||||
})
|
||||
|
||||
t.test('throws for bad value', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.derefAliases = '0'
|
||||
},
|
||||
'derefAliases must be set to an integer'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.filter', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.equal(req.filter.toString(), '(objectclass=*)')
|
||||
|
||||
req.filter = '(cn=foo)'
|
||||
t.equal(req.filter.toString(), '(cn=foo)')
|
||||
|
||||
req.filter = new filter.EqualityFilter({ attribute: 'sn', value: 'bar' })
|
||||
t.equal(req.filter.toString(), '(sn=bar)')
|
||||
})
|
||||
|
||||
t.test('throws for bad value', async t => {
|
||||
const expected = 'filter must be a string or a FilterString instance'
|
||||
const req = new SearchRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.filter = ['foo']
|
||||
},
|
||||
expected
|
||||
)
|
||||
t.throws(
|
||||
() => {
|
||||
req.filter = { attribute: 'cn', value: 'foo' }
|
||||
},
|
||||
expected
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.scope', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.equal(req.scopeName, 'base')
|
||||
t.equal(req.scope, 0)
|
||||
|
||||
req.scope = SearchRequest.SCOPE_SINGLE
|
||||
t.equal(req.scopeName, 'single')
|
||||
t.equal(req.scope, 1)
|
||||
|
||||
req.scope = SearchRequest.SCOPE_SUBTREE
|
||||
t.equal(req.scopeName, 'subtree')
|
||||
t.equal(req.scope, 2)
|
||||
|
||||
req.scope = 'SUB'
|
||||
t.equal(req.scopeName, 'subtree')
|
||||
t.equal(req.scope, 2)
|
||||
|
||||
req.scope = 'base'
|
||||
t.equal(req.scopeName, 'base')
|
||||
t.equal(req.scope, 0)
|
||||
})
|
||||
|
||||
t.test('throws for invalid value', async t => {
|
||||
const expected = ' is an invalid search scope'
|
||||
const req = new SearchRequest()
|
||||
|
||||
t.throws(
|
||||
() => {
|
||||
req.scope = 'nested'
|
||||
},
|
||||
'nested' + expected
|
||||
)
|
||||
|
||||
t.throws(
|
||||
() => {
|
||||
req.scope = 42
|
||||
},
|
||||
42 + expected
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.sizeLimit', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.equal(req.sizeLimit, 0)
|
||||
|
||||
req.sizeLimit = 15
|
||||
t.equal(req.sizeLimit, 15)
|
||||
})
|
||||
|
||||
t.test('throws for bad value', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.sizeLimit = 15.5
|
||||
},
|
||||
'sizeLimit must be an integer'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.timeLimit', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.equal(req.timeLimit, 0)
|
||||
|
||||
req.timeLimit = 15
|
||||
t.equal(req.timeLimit, 15)
|
||||
})
|
||||
|
||||
t.test('throws for bad value', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.timeLimit = 15.5
|
||||
},
|
||||
'timeLimit must be an integer'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.typesOnly', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.equal(req.typesOnly, false)
|
||||
|
||||
req.typesOnly = true
|
||||
t.equal(req.typesOnly, true)
|
||||
})
|
||||
|
||||
t.test('throws for bad value', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.throws(
|
||||
() => {
|
||||
req.typesOnly = 'true'
|
||||
},
|
||||
'typesOnly must be set to a boolean value'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_toBer', t => {
|
||||
tap.test('converts instance to BER', async t => {
|
||||
const req = new SearchRequest({
|
||||
messageId: 2,
|
||||
baseObject: 'dc=example,dc=com',
|
||||
scope: 'subtree',
|
||||
derefAliases: SearchRequest.DEREF_ALIASES_NEVER,
|
||||
sizeLimit: 1000,
|
||||
timeLimit: 30,
|
||||
typesOnly: false,
|
||||
filter: '(&(objectClass=person)(uid=jdoe))',
|
||||
|
||||
attributes: ['*', '+']
|
||||
})
|
||||
const writer = new BerWriter()
|
||||
req._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(searchRequestBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_pojo', t => {
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const req = new SearchRequest()
|
||||
t.strictSame(req._pojo(), {
|
||||
baseObject: '',
|
||||
scope: 'base',
|
||||
derefAliases: 0,
|
||||
sizeLimit: 0,
|
||||
timeLimit: 0,
|
||||
typesOnly: false,
|
||||
filter: '(objectclass=*)',
|
||||
attributes: []
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws if operation incorrect', async t => {
|
||||
const reqBuffer = Buffer.from(searchRequestBytes)
|
||||
reqBuffer[5] = 0x61
|
||||
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
t.throws(
|
||||
() => SearchRequest.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x61')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const reqBuffer = Buffer.from(searchRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = SearchRequest.parseToPojo(reader)
|
||||
t.equal(pojo.protocolOp, operations.LDAP_REQ_SEARCH)
|
||||
t.equal(pojo.baseObject, 'dc=example,dc=com')
|
||||
t.equal(pojo.scope, SearchRequest.SCOPE_SUBTREE)
|
||||
t.equal(pojo.derefAliases, SearchRequest.DEREF_ALIASES_NEVER)
|
||||
t.equal(pojo.sizeLimit, 1000)
|
||||
t.equal(pojo.timeLimit, 30)
|
||||
t.equal(pojo.typesOnly, false)
|
||||
t.equal(pojo.filter, '(&(objectClass=person)(uid=jdoe))')
|
||||
t.strictSame(pojo.attributes, ['*', '+'])
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
54
node_modules/@ldapjs/messages/lib/messages/search-result-done.js
generated
vendored
Normal file
54
node_modules/@ldapjs/messages/lib/messages/search-result-done.js
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
'use strict'
|
||||
|
||||
const LdapResult = require('../ldap-result')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
|
||||
/**
|
||||
* Implements the search result done response message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.5.2.
|
||||
*/
|
||||
class SearchResultDone extends LdapResult {
|
||||
#uri
|
||||
|
||||
/**
|
||||
* @typedef {LdapResultOptions} SearchResultDoneOptions
|
||||
* @property {string[]} [uri=[]] The set of reference URIs the message is
|
||||
* providing.
|
||||
* @property {string[]} [uris] An alias for uri.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {SearchResultDoneOptions} options
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_RES_SEARCH_DONE
|
||||
super(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'SearchResultDone'
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
return LdapResult._parseToPojo({
|
||||
opCode: operations.LDAP_RES_SEARCH_DONE,
|
||||
berReader: ber
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SearchResultDone
|
||||
52
node_modules/@ldapjs/messages/lib/messages/search-result-done.test.js
generated
vendored
Normal file
52
node_modules/@ldapjs/messages/lib/messages/search-result-done.test.js
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const {
|
||||
searchResultDoneBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
const SearchResultDone = require('./search-result-done')
|
||||
|
||||
tap.test('basic', async t => {
|
||||
const res = new SearchResultDone()
|
||||
t.equal(res.protocolOp, operations.LDAP_RES_SEARCH_DONE)
|
||||
t.equal(res.type, 'SearchResultDone')
|
||||
})
|
||||
|
||||
tap.test('toBer', t => {
|
||||
tap.test('returns basic bytes', async t => {
|
||||
const res = new SearchResultDone({ messageId: 2 })
|
||||
const ber = res.toBer()
|
||||
const expected = Buffer.from(searchResultDoneBytes)
|
||||
t.equal(expected.compare(ber.buffer), 0)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('parses a basic object', async t => {
|
||||
const bytes = searchResultDoneBytes.slice(5)
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
const pojo = SearchResultDone.parseToPojo(reader)
|
||||
t.strictSame(pojo, {
|
||||
status: 0,
|
||||
matchedDN: '',
|
||||
diagnosticMessage: '',
|
||||
referrals: []
|
||||
})
|
||||
})
|
||||
|
||||
t.test('throws if protocol op is wrong', async t => {
|
||||
const bytes = searchResultDoneBytes.slice(5)
|
||||
bytes[0] = 0x68
|
||||
const reader = new BerReader(Buffer.from(bytes))
|
||||
t.throws(
|
||||
() => SearchResultDone.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x68')
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
198
node_modules/@ldapjs/messages/lib/messages/search-result-entry.js
generated
vendored
Normal file
198
node_modules/@ldapjs/messages/lib/messages/search-result-entry.js
generated
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
'use strict'
|
||||
|
||||
const LdapMessage = require('../ldap-message')
|
||||
const Attribute = require('@ldapjs/attribute')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
|
||||
/**
|
||||
* Implements the search result entry message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.5.2.
|
||||
*/
|
||||
class SearchResultEntry extends LdapMessage {
|
||||
/**
|
||||
* Path to the LDAP object.
|
||||
*
|
||||
* @type {import('@ldapjs/dn').DN}
|
||||
*/
|
||||
#objectName
|
||||
|
||||
/**
|
||||
* A set of attribute objects.
|
||||
*
|
||||
* @type {import('@ldapjs/attribute')[]}
|
||||
*/
|
||||
#attributes = []
|
||||
|
||||
/**
|
||||
* @typedef {LdapMessageOptions} SearchResultEntryOptions
|
||||
* @property {string | import('@ldapjs/dn').DN} [objectName=''] The path to
|
||||
* the LDAP object.
|
||||
* @property {import('@ldapjs/attribute')[]} attributes A set of attributes
|
||||
* to store at the `entry` path.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {SearchResultEntryOptions} [options]
|
||||
*
|
||||
* @throws When the provided attributes list is invalid or the object name
|
||||
* is not a valid LdapDn object or DN string.
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_RES_SEARCH_ENTRY
|
||||
super(options)
|
||||
|
||||
this.objectName = options.objectName ?? ''
|
||||
this.attributes = options.attributes ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of {@link objectName}.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get _dn () {
|
||||
return this.#objectName
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'SearchResultEntry'
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of the attributes associated with the request.
|
||||
*
|
||||
* @returns {import('@ldapjs/attribute')[]}
|
||||
*/
|
||||
get attributes () {
|
||||
return this.#attributes.slice(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the attributes to be added to the entry. Replaces any existing
|
||||
* attributes.
|
||||
*
|
||||
* @param {object[] | import('@ldapjs/attribute')[]} attrs
|
||||
*
|
||||
* @throws If the input is not an array, or any element is not an
|
||||
* {@link Attribute} or attribute-like object.
|
||||
*/
|
||||
set attributes (attrs) {
|
||||
if (Array.isArray(attrs) === false) {
|
||||
throw Error('attrs must be an array')
|
||||
}
|
||||
const newAttrs = []
|
||||
for (const attr of attrs) {
|
||||
if (Attribute.isAttribute(attr) === false) {
|
||||
throw Error('attr must be an Attribute instance or Attribute-like object')
|
||||
}
|
||||
if (Object.prototype.toString.call(attr) !== '[object LdapAttribute]') {
|
||||
newAttrs.push(new Attribute(attr))
|
||||
continue
|
||||
}
|
||||
newAttrs.push(attr)
|
||||
}
|
||||
this.#attributes = newAttrs
|
||||
}
|
||||
|
||||
/**
|
||||
* The path to the LDAP entry that matched the search.
|
||||
*
|
||||
* @returns {import('@ldapjs/dn').DN}
|
||||
*/
|
||||
get objectName () {
|
||||
return this.#objectName
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path to the LDAP entry that matched the search.
|
||||
*
|
||||
* @param {string | import('@ldapjs/dn').DN} value
|
||||
*
|
||||
* @throws When the input is invalid.
|
||||
*/
|
||||
set objectName (value) {
|
||||
if (typeof value === 'string') {
|
||||
this.#objectName = DN.fromString(value)
|
||||
} else if (Object.prototype.toString.call(value) === '[object LdapDn]') {
|
||||
this.#objectName = value
|
||||
} else {
|
||||
throw Error('objectName must be a DN string or an instance of LdapDn')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.startSequence(operations.LDAP_RES_SEARCH_ENTRY)
|
||||
ber.writeString(this.#objectName.toString())
|
||||
ber.startSequence()
|
||||
for (const attr of this.#attributes) {
|
||||
const attrBer = attr.toBer()
|
||||
ber.appendBuffer(attrBer.buffer)
|
||||
}
|
||||
ber.endSequence()
|
||||
ber.endSequence()
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {object}
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
_pojo (obj = {}) {
|
||||
obj.objectName = this.#objectName.toString()
|
||||
obj.attributes = []
|
||||
for (const attr of this.#attributes) {
|
||||
obj.attributes.push(attr.pojo)
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const protocolOp = ber.readSequence()
|
||||
if (protocolOp !== operations.LDAP_RES_SEARCH_ENTRY) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
const objectName = ber.readString()
|
||||
const attributes = []
|
||||
|
||||
// Advance to the first attribute sequence in the set
|
||||
// of attribute sequences.
|
||||
ber.readSequence()
|
||||
|
||||
const endOfAttributesPos = ber.offset + ber.length
|
||||
while (ber.offset < endOfAttributesPos) {
|
||||
const attribute = Attribute.fromBer(ber)
|
||||
attributes.push(attribute)
|
||||
}
|
||||
|
||||
return { protocolOp, objectName, attributes }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SearchResultEntry
|
||||
195
node_modules/@ldapjs/messages/lib/messages/search-result-entry.test.js
generated
vendored
Normal file
195
node_modules/@ldapjs/messages/lib/messages/search-result-entry.test.js
generated
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const Attribute = require('@ldapjs/attribute')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
const { BerWriter, BerReader } = require('@ldapjs/asn1')
|
||||
const SearchResultEntry = require('./search-result-entry')
|
||||
|
||||
const {
|
||||
searchResultEntryBytes,
|
||||
searchResultEntryNoValuesBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
|
||||
tap.test('basic', t => {
|
||||
t.test('constructor no args', async t => {
|
||||
const res = new SearchResultEntry()
|
||||
t.strictSame(res.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_RES_SEARCH_ENTRY,
|
||||
type: 'SearchResultEntry',
|
||||
objectName: '',
|
||||
attributes: [],
|
||||
controls: []
|
||||
})
|
||||
|
||||
t.equal(res.type, 'SearchResultEntry')
|
||||
})
|
||||
|
||||
t.test('constructor with args', async t => {
|
||||
const res = new SearchResultEntry({
|
||||
objectName: 'dc=example,dc=com',
|
||||
attributes: [{ type: 'cn', values: ['foo'] }]
|
||||
})
|
||||
t.strictSame(res.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_RES_SEARCH_ENTRY,
|
||||
type: 'SearchResultEntry',
|
||||
objectName: 'dc=example,dc=com',
|
||||
attributes: [{ type: 'cn', values: ['foo'] }],
|
||||
controls: []
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.attributes', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const res = new SearchResultEntry()
|
||||
t.strictSame(res.attributes, [])
|
||||
|
||||
res.attributes = [new Attribute({ type: 'cn', values: 'foo' })]
|
||||
t.strictSame(res.attributes, [new Attribute({ type: 'cn', values: 'foo' })])
|
||||
})
|
||||
|
||||
t.test('rejects non-array', async t => {
|
||||
const res = new SearchResultEntry()
|
||||
t.throws(
|
||||
() => {
|
||||
res.attributes = { type: 'cn', values: ['foo'] }
|
||||
},
|
||||
'attrs must be an array'
|
||||
)
|
||||
})
|
||||
|
||||
t.test('rejects non-attribute objects', async t => {
|
||||
const res = new SearchResultEntry()
|
||||
t.throws(
|
||||
() => {
|
||||
res.attributes = [{ foo: 'bar' }]
|
||||
},
|
||||
'attr must be an Attribute instance or Attribute-like object'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.objectName', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const res = new SearchResultEntry()
|
||||
t.equal(res.objectName.toString(), '')
|
||||
t.equal(res._dn.toString(), '')
|
||||
|
||||
res.objectName = 'cn=foo'
|
||||
t.equal(res.objectName.toString(), 'cn=foo')
|
||||
|
||||
res.objectName = DN.fromString('sn=bar')
|
||||
t.equal(res.objectName.toString(), 'sn=bar')
|
||||
})
|
||||
|
||||
t.test('throws for invalid value', async t => {
|
||||
const res = new SearchResultEntry()
|
||||
t.throws(
|
||||
() => {
|
||||
res.objectName = ['invalid input']
|
||||
},
|
||||
'objectName must be a DN string or instance of LdapDn'
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_toBer', t => {
|
||||
t.test('converts instance to BER', async t => {
|
||||
let res = new SearchResultEntry({
|
||||
objectName: 'dc=example,dc=com',
|
||||
attributes: [
|
||||
{ type: 'objectClass', values: ['top', 'domain'] },
|
||||
{ type: 'dc', values: ['example'] }
|
||||
]
|
||||
})
|
||||
let writer = new BerWriter()
|
||||
res._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(searchResultEntryBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
|
||||
res = new SearchResultEntry({
|
||||
objectName: 'dc=example,dc=com',
|
||||
attributes: [
|
||||
{ type: 'objectClass', values: [] },
|
||||
{ type: 'dc', values: [] }
|
||||
]
|
||||
})
|
||||
writer = new BerWriter()
|
||||
res._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(searchResultEntryNoValuesBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_pojo', t => {
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const req = new SearchResultEntry({
|
||||
objectName: 'cn=foo',
|
||||
attributes: [{ type: 'bar', values: ['baz'] }]
|
||||
})
|
||||
t.strictSame(req._pojo(), {
|
||||
objectName: 'cn=foo',
|
||||
attributes: [{
|
||||
type: 'bar',
|
||||
values: ['baz']
|
||||
}]
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws if operation incorrect', async t => {
|
||||
const reqBuffer = Buffer.from(searchResultEntryBytes)
|
||||
reqBuffer[5] = 0x61
|
||||
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
t.throws(
|
||||
() => SearchResultEntry.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x61')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const reqBuffer = Buffer.from(searchResultEntryBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = SearchResultEntry.parseToPojo(reader)
|
||||
t.equal(pojo.protocolOp, operations.LDAP_RES_SEARCH_ENTRY)
|
||||
t.equal(pojo.objectName, 'dc=example,dc=com')
|
||||
t.strictSame(pojo.attributes[0].pojo, {
|
||||
type: 'objectClass',
|
||||
values: ['top', 'domain']
|
||||
})
|
||||
t.strictSame(pojo.attributes[1].pojo, {
|
||||
type: 'dc',
|
||||
values: ['example']
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
144
node_modules/@ldapjs/messages/lib/messages/search-result-reference.js
generated
vendored
Normal file
144
node_modules/@ldapjs/messages/lib/messages/search-result-reference.js
generated
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
'use strict'
|
||||
|
||||
const LdapMessage = require('../ldap-message')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
|
||||
/**
|
||||
* Implements the search result reference response message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.5.2.
|
||||
*/
|
||||
class SearchResultReference extends LdapMessage {
|
||||
#uri
|
||||
|
||||
/**
|
||||
* @typedef {LdapMessageOptions} SearchResultReferenceOptions
|
||||
* @property {string[]} [uri=[]] The set of reference URIs the message is
|
||||
* providing.
|
||||
* @property {string[]} [uris] An alias for uri.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {SearchResultReferenceOptions} options
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_RES_SEARCH_REF
|
||||
super(options)
|
||||
|
||||
this.uri = (options.uri || options.uris) ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'SearchResultReference'
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of reference URIs associated with the message.
|
||||
*
|
||||
* @returns {string[]}
|
||||
*/
|
||||
get uri () {
|
||||
return this.#uri.slice(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the list of reference URIs associated with the message.
|
||||
*
|
||||
* @param {string[]} value
|
||||
*
|
||||
* @throws When the value is not an array or contains a non-string element.
|
||||
*/
|
||||
set uri (value) {
|
||||
if (
|
||||
Array.isArray(value) === false ||
|
||||
value.some(v => typeof v !== 'string')
|
||||
) {
|
||||
throw Error('uri must be an array of strings')
|
||||
}
|
||||
this.#uri = value.slice(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of {@link uri}.
|
||||
*
|
||||
* @returns {string[]}
|
||||
*/
|
||||
get uris () {
|
||||
return this.uri
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of {@link uri} setter.
|
||||
*
|
||||
* @param {string[]} value
|
||||
*/
|
||||
set uris (value) {
|
||||
this.uri = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.startSequence(operations.LDAP_RES_SEARCH_REF)
|
||||
|
||||
for (const uri of this.#uri) {
|
||||
ber.writeString(uri)
|
||||
}
|
||||
|
||||
ber.endSequence()
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {object}
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
_pojo (obj = {}) {
|
||||
obj.uri = []
|
||||
for (const uri of this.#uri) {
|
||||
obj.uri.push(uri)
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const protocolOp = ber.readSequence()
|
||||
if (protocolOp !== operations.LDAP_RES_SEARCH_REF) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
const uri = []
|
||||
|
||||
const endOfMessagePos = ber.offset + ber.length
|
||||
while (ber.offset < endOfMessagePos) {
|
||||
const u = ber.readString()
|
||||
uri.push(u)
|
||||
}
|
||||
|
||||
return { protocolOp, uri }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SearchResultReference
|
||||
161
node_modules/@ldapjs/messages/lib/messages/search-result-reference.test.js
generated
vendored
Normal file
161
node_modules/@ldapjs/messages/lib/messages/search-result-reference.test.js
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
||||
const SearchResultReference = require('./search-result-reference')
|
||||
|
||||
const {
|
||||
searchResultReferenceBytes
|
||||
} = require('./_fixtures/message-byte-arrays')
|
||||
|
||||
tap.test('basic', t => {
|
||||
t.test('constructor no args', async t => {
|
||||
const expected = {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_RES_SEARCH_REF,
|
||||
type: 'SearchResultReference',
|
||||
uri: [],
|
||||
controls: []
|
||||
}
|
||||
|
||||
const res = new SearchResultReference()
|
||||
t.strictSame(res.pojo, expected)
|
||||
|
||||
t.equal(res.type, 'SearchResultReference')
|
||||
})
|
||||
|
||||
t.test('constructor with args', async t => {
|
||||
const expected = {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_RES_SEARCH_REF,
|
||||
type: 'SearchResultReference',
|
||||
uri: ['ldap://foo', 'ldap://bar'],
|
||||
controls: []
|
||||
}
|
||||
|
||||
let res = new SearchResultReference({
|
||||
uri: ['ldap://foo', 'ldap://bar']
|
||||
})
|
||||
t.strictSame(res.pojo, expected)
|
||||
|
||||
res = new SearchResultReference({
|
||||
uris: ['ldap://foo', 'ldap://bar']
|
||||
})
|
||||
t.strictSame(res.pojo, expected)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('.uri/.uris', t => {
|
||||
t.test('sets/gets', async t => {
|
||||
const res = new SearchResultReference()
|
||||
t.strictSame(res.uri, [])
|
||||
t.strictSame(res.uris, [])
|
||||
|
||||
res.uri = ['ldap://foo']
|
||||
t.strictSame(res.uri, ['ldap://foo'])
|
||||
|
||||
res.uris = ['ldap://bar']
|
||||
t.strictSame(res.uris, ['ldap://bar'])
|
||||
t.strictSame(res.uri, ['ldap://bar'])
|
||||
})
|
||||
|
||||
t.test('throws for bad input', async t => {
|
||||
const res = new SearchResultReference()
|
||||
const expected = Error('uri must be an array of strings')
|
||||
|
||||
t.throws(
|
||||
() => {
|
||||
res.uri = 'ldap://foo'
|
||||
},
|
||||
expected
|
||||
)
|
||||
|
||||
t.throws(
|
||||
() => {
|
||||
res.uris = 'ldap://foo'
|
||||
},
|
||||
expected
|
||||
)
|
||||
|
||||
t.throws(
|
||||
() => {
|
||||
res.uri = ['ldap://foo', { foo: 'foo' }, 'ldap://bar']
|
||||
},
|
||||
expected
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_toBer', t => {
|
||||
tap.test('converts instance to BER', async t => {
|
||||
const res = new SearchResultReference({
|
||||
uri: [
|
||||
'ldap://ds1.example.com:389/dc=example,dc=com??sub?',
|
||||
'ldap://ds2.example.com:389/dc=example,dc=com??sub?'
|
||||
]
|
||||
})
|
||||
const writer = new BerWriter()
|
||||
res._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(searchResultReferenceBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_pojo', t => {
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const res = new SearchResultReference({
|
||||
uri: [
|
||||
'ldap://ds1.example.com:389/dc=example,dc=com??sub?',
|
||||
'ldap://ds2.example.com:389/dc=example,dc=com??sub?'
|
||||
]
|
||||
})
|
||||
t.strictSame(res._pojo(), {
|
||||
uri: [
|
||||
'ldap://ds1.example.com:389/dc=example,dc=com??sub?',
|
||||
'ldap://ds2.example.com:389/dc=example,dc=com??sub?'
|
||||
]
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws if operation incorrect', async t => {
|
||||
const reqBuffer = Buffer.from(searchResultReferenceBytes)
|
||||
reqBuffer[5] = 0x61
|
||||
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
t.throws(
|
||||
() => SearchResultReference.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x61')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const reqBuffer = Buffer.from(searchResultReferenceBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = SearchResultReference.parseToPojo(reader)
|
||||
t.equal(pojo.protocolOp, operations.LDAP_RES_SEARCH_REF)
|
||||
t.equal(pojo.uri[0], 'ldap://ds1.example.com:389/dc=example,dc=com??sub?')
|
||||
t.equal(pojo.uri[1], 'ldap://ds2.example.com:389/dc=example,dc=com??sub?')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
69
node_modules/@ldapjs/messages/lib/messages/unbind-request.js
generated
vendored
Normal file
69
node_modules/@ldapjs/messages/lib/messages/unbind-request.js
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
'use strict'
|
||||
|
||||
const LdapMessage = require('../ldap-message')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
|
||||
/**
|
||||
* Implements the unbind request message as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.3.
|
||||
*/
|
||||
class UnbindRequest extends LdapMessage {
|
||||
/**
|
||||
* @param {LdapMessageOptions} options
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
options.protocolOp = operations.LDAP_REQ_UNBIND
|
||||
super(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the request type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get type () {
|
||||
return 'UnbindRequest'
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerWriter} ber
|
||||
*
|
||||
* @returns {import('@ldapjs/asn1').BerWriter}
|
||||
*/
|
||||
_toBer (ber) {
|
||||
ber.writeString('', operations.LDAP_REQ_UNBIND)
|
||||
return ber
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* @param {object}
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
_pojo (obj = {}) {
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the standardized `parseToPojo` method.
|
||||
*
|
||||
* @see LdapMessage.parseToPojo
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*/
|
||||
static parseToPojo (ber) {
|
||||
const protocolOp = ber.readSequence()
|
||||
if (protocolOp !== operations.LDAP_REQ_UNBIND) {
|
||||
const op = protocolOp.toString(16).padStart(2, '0')
|
||||
throw Error(`found wrong protocol operation: 0x${op}`)
|
||||
}
|
||||
|
||||
return { protocolOp }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UnbindRequest
|
||||
75
node_modules/@ldapjs/messages/lib/messages/unbind-request.test.js
generated
vendored
Normal file
75
node_modules/@ldapjs/messages/lib/messages/unbind-request.test.js
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const UnbindRequest = require('./unbind-request')
|
||||
|
||||
const { unbindRequestBytes } = require('./_fixtures/message-byte-arrays')
|
||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
||||
|
||||
tap.test('basic', t => {
|
||||
t.test('constructor no args', async t => {
|
||||
const req = new UnbindRequest()
|
||||
t.strictSame(req.pojo, {
|
||||
messageId: 1,
|
||||
protocolOp: operations.LDAP_REQ_UNBIND,
|
||||
type: 'UnbindRequest',
|
||||
controls: []
|
||||
})
|
||||
t.equal(req.type, 'UnbindRequest')
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_toBer', t => {
|
||||
tap.test('converts instance to BER', async t => {
|
||||
const req = new UnbindRequest()
|
||||
const writer = new BerWriter()
|
||||
req._toBer(writer)
|
||||
|
||||
t.equal(
|
||||
Buffer.from(unbindRequestBytes.slice(5)).compare(writer.buffer),
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('_pojo', t => {
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const req = new UnbindRequest()
|
||||
t.strictSame(req._pojo(), {})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('#parseToPojo', t => {
|
||||
t.test('throws if operation incorrect', async t => {
|
||||
const reqBuffer = Buffer.from(unbindRequestBytes)
|
||||
reqBuffer[5] = 0x61
|
||||
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
t.throws(
|
||||
() => UnbindRequest.parseToPojo(reader),
|
||||
Error('found wrong protocol operation: 0x61')
|
||||
)
|
||||
})
|
||||
|
||||
t.test('returns a pojo representation', async t => {
|
||||
const reqBuffer = Buffer.from(unbindRequestBytes)
|
||||
const reader = new BerReader(reqBuffer)
|
||||
reader.readSequence()
|
||||
reader.readInt()
|
||||
|
||||
const pojo = UnbindRequest.parseToPojo(reader)
|
||||
t.equal(pojo.protocolOp, operations.LDAP_REQ_UNBIND)
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
205
node_modules/@ldapjs/messages/lib/parse-to-message.js
generated
vendored
Normal file
205
node_modules/@ldapjs/messages/lib/parse-to-message.js
generated
vendored
Normal file
@ -0,0 +1,205 @@
|
||||
'use strict'
|
||||
|
||||
const { operations } = require('@ldapjs/protocol')
|
||||
const { getControl } = require('@ldapjs/controls')
|
||||
|
||||
const messageClasses = {
|
||||
AbandonRequest: require('./messages/abandon-request'),
|
||||
AddRequest: require('./messages/add-request'),
|
||||
BindRequest: require('./messages/bind-request'),
|
||||
CompareRequest: require('./messages/compare-request'),
|
||||
DeleteRequest: require('./messages/delete-request'),
|
||||
ExtensionRequest: require('./messages/extension-request'),
|
||||
ModifyRequest: require('./messages/modify-request'),
|
||||
ModifyDnRequest: require('./messages/modifydn-request'),
|
||||
SearchRequest: require('./messages/search-request'),
|
||||
UnbindRequest: require('./messages/unbind-request'),
|
||||
|
||||
AbandonResponse: require('./messages/abandon-response'),
|
||||
AddResponse: require('./messages/add-response'),
|
||||
BindResponse: require('./messages/bind-response'),
|
||||
CompareResponse: require('./messages/compare-response'),
|
||||
DeleteResponse: require('./messages/delete-response'),
|
||||
ExtensionResponse: require('./messages/extension-response'),
|
||||
ModifyResponse: require('./messages/modify-response'),
|
||||
ModifyDnResponse: require('./messages/modifydn-response'),
|
||||
|
||||
// Search result messages.
|
||||
SearchResultEntry: require('./messages/search-result-entry'),
|
||||
SearchResultReference: require('./messages/search-result-reference'),
|
||||
SearchResultDone: require('./messages/search-result-done'),
|
||||
|
||||
// Miscellaneous messages.
|
||||
IntermediateResponse: require('./messages/intermediate-response')
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function that inspects a BER object and parses it into an instance
|
||||
* of a specific LDAP message.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber An object that represents a
|
||||
* full LDAP Message sequence as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.1.1.
|
||||
*
|
||||
* @returns {LdapMessage} Some specific instance of the base LDAP Message
|
||||
* type.
|
||||
*
|
||||
* @throws When the input data is malformed.
|
||||
*/
|
||||
module.exports = function parseToMessage (ber) {
|
||||
const inputType = Object.prototype.toString.apply(ber)
|
||||
if (inputType !== '[object BerReader]') {
|
||||
throw TypeError(`Expected BerReader but got ${inputType}.`)
|
||||
}
|
||||
|
||||
ber.readSequence()
|
||||
|
||||
const messageId = ber.readInt()
|
||||
const messageType = identifyType(ber)
|
||||
const MessageClass = messageClasses[messageType]
|
||||
const pojoMessage = MessageClass.parseToPojo(ber)
|
||||
const message = new MessageClass({
|
||||
messageId,
|
||||
...pojoMessage
|
||||
})
|
||||
|
||||
// Look for controls
|
||||
if (ber.peek() === 0xa0) {
|
||||
ber.readSequence()
|
||||
const end = ber.offset + ber.length
|
||||
while (ber.offset < end) {
|
||||
const c = getControl(ber)
|
||||
/* istanbul ignore else */
|
||||
if (c) {
|
||||
message.addControl(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the type of LDAP message the BER represents, e.g. a "Bind Request"
|
||||
* message.
|
||||
*
|
||||
* @param {BerReader} ber
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
function identifyType (ber) {
|
||||
let result
|
||||
switch (ber.peek()) {
|
||||
case operations.LDAP_REQ_ABANDON: {
|
||||
result = 'AbandonRequest'
|
||||
break
|
||||
}
|
||||
|
||||
case 0x00: {
|
||||
result = 'AbandonResponse'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_REQ_ADD: {
|
||||
result = 'AddRequest'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_RES_ADD: {
|
||||
result = 'AddResponse'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_REQ_BIND: {
|
||||
result = 'BindRequest'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_RES_BIND: {
|
||||
result = 'BindResponse'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_REQ_COMPARE: {
|
||||
result = 'CompareRequest'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_RES_COMPARE: {
|
||||
result = 'CompareResponse'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_REQ_DELETE: {
|
||||
result = 'DeleteRequest'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_RES_DELETE: {
|
||||
result = 'DeleteResponse'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_REQ_EXTENSION: {
|
||||
result = 'ExtensionRequest'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_RES_EXTENSION: {
|
||||
result = 'ExtensionResponse'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_REQ_MODIFY: {
|
||||
result = 'ModifyRequest'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_RES_MODIFY: {
|
||||
result = 'ModifyResponse'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_REQ_MODRDN: {
|
||||
result = 'ModifyDnRequest'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_RES_MODRDN: {
|
||||
result = 'ModifyDnResponse'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_REQ_SEARCH: {
|
||||
result = 'SearchRequest'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_RES_SEARCH_ENTRY: {
|
||||
result = 'SearchResultEntry'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_RES_SEARCH_REF: {
|
||||
result = 'SearchResultReference'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_RES_SEARCH_DONE: {
|
||||
result = 'SearchResultDone'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_REQ_UNBIND: {
|
||||
result = 'UnbindRequest'
|
||||
break
|
||||
}
|
||||
|
||||
case operations.LDAP_RES_INTERMEDIATE: {
|
||||
result = 'IntermediateResponse'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
54
node_modules/@ldapjs/messages/lib/parse-to-message.test.js
generated
vendored
Normal file
54
node_modules/@ldapjs/messages/lib/parse-to-message.test.js
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
'use strict'
|
||||
|
||||
const tap = require('tap')
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const parseToMessage = require('./parse-to-message')
|
||||
|
||||
const messageBytesArrays = require('./messages/_fixtures/message-byte-arrays')
|
||||
|
||||
tap.test('throws if input not a BerReader', async t => {
|
||||
const input = Buffer.from([0x30, 0x01, 0x64])
|
||||
t.throws(
|
||||
() => parseToMessage(input),
|
||||
Error('Expected BerReader but got [object Uint8Array]')
|
||||
)
|
||||
})
|
||||
|
||||
tap.test('throws if sequence is invalid', async t => {
|
||||
const input = new BerReader(Buffer.from([0x0a, 0x01, 0x64]))
|
||||
t.throws(
|
||||
() => parseToMessage(input),
|
||||
Error('Expected 0x02: got 0x64')
|
||||
)
|
||||
})
|
||||
|
||||
tap.test('parses messages correctly', async t => {
|
||||
for (const [name, messageBytes] of Object.entries(messageBytesArrays)) {
|
||||
t.comment(`verifying message bytes: ${name}`)
|
||||
const expected = Buffer.from(messageBytes)
|
||||
const reader = new BerReader(expected)
|
||||
const message = parseToMessage(reader)
|
||||
const found = message.toBer().buffer
|
||||
const isEqual = t.equal(expected.compare(found), 0, `${name} comparison`)
|
||||
|
||||
if (isEqual === false) {
|
||||
const diff = {}
|
||||
for (let i = 0; i < expected.length; i += 1) {
|
||||
if (expected[i] !== found[i]) {
|
||||
diff[i] = {
|
||||
expected: Number(expected[i]).toString(16),
|
||||
found: Number(found[i]).toString(16)
|
||||
}
|
||||
}
|
||||
}
|
||||
t.fail(`${name} differs`, diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
tap.test('parses search req with evolution filter', async t => {
|
||||
const messageBytes = require('./_fixtures/evolution-filter-req')
|
||||
const messageBuffer = Buffer.from(messageBytes)
|
||||
const message = parseToMessage(new BerReader(messageBuffer))
|
||||
t.equal(messageBuffer.compare(message.toBer().buffer), 0)
|
||||
})
|
||||
44
node_modules/@ldapjs/messages/package.json
generated
vendored
Normal file
44
node_modules/@ldapjs/messages/package.json
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "@ldapjs/messages",
|
||||
"homepage": "https://github.com/ldapjs/messages",
|
||||
"description": "API for creating and parsing LDAP messages",
|
||||
"version": "1.3.0",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:ldapjs/messages.git"
|
||||
},
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@ldapjs/asn1": "^2.0.0",
|
||||
"@ldapjs/attribute": "^1.0.0",
|
||||
"@ldapjs/change": "^1.0.0",
|
||||
"@ldapjs/controls": "^2.1.0",
|
||||
"@ldapjs/dn": "^1.1.0",
|
||||
"@ldapjs/filter": "^2.1.1",
|
||||
"@ldapjs/protocol": "^1.2.1",
|
||||
"process-warning": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fastify/pre-commit": "^2.0.2",
|
||||
"eslint": "^8.47.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-plugin-import": "^2.28.0",
|
||||
"eslint-plugin-n": "^16.0.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"tap": "^16.3.8"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"lint:ci": "eslint .",
|
||||
"test": "tap --no-coverage-report",
|
||||
"test:cov": "tap",
|
||||
"test:cov:html": "tap --coverage-report=html",
|
||||
"test:watch": "tap -w --no-coverage-report"
|
||||
},
|
||||
"precommit": [
|
||||
"lint",
|
||||
"test"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user