diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 00000000..a1ce7996
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,33 @@
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
+# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
+
+name: Test
+
+on:
+ pull_request:
+ push:
+ branches:
+ - master
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ ruby:
+ - "3.0"
+ - "3.1"
+ - "3.2"
+ - "3.3"
+ - "3.4"
+ - "jruby-9.4"
+ - "truffleruby"
+ steps:
+ - uses: actions/checkout@v4
+ - name: Run tests with Ruby ${{ matrix.ruby }}
+ run: docker compose run ci-${{ matrix.ruby }}
diff --git a/.gitignore b/.gitignore
index 9c2842d9..e7d58b8f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,5 @@ publish/
Gemfile.lock
.bundle
bin/
+.idea
+*.gem
diff --git a/.rubocop.yml b/.rubocop.yml
index 85ffa202..b2f78bb0 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -3,3 +3,18 @@ inherit_from: .rubocop_todo.yml
AllCops:
Exclude:
- 'pkg/**/*'
+
+Layout/ExtraSpacing:
+ Enabled: false
+
+Lint/AssignmentInCondition:
+ Enabled: false
+
+Style/ParallelAssignment:
+ Enabled: false
+
+Style/TrailingCommaInArrayLiteral:
+ EnforcedStyleForMultiline: comma
+
+Style/TrailingCommaInArguments:
+ EnforcedStyleForMultiline: comma
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 5a5dcbc7..50901661 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -1,462 +1,960 @@
-# This configuration was generated by `rubocop --auto-gen-config`
-# on 2014-12-19 15:32:44 +1100 using RuboCop version 0.28.0.
+# This configuration was generated by
+# `rubocop --auto-gen-config`
+# on 2025-05-31 20:03:27 UTC using RuboCop version 1.75.8.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
-# Offense count: 12
-# Configuration parameters: AllowSafeAssignment.
-Lint/AssignmentInCondition:
- Enabled: false
+# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, IndentationWidth.
+# SupportedStyles: with_first_element, with_fixed_indentation
+Layout/ArrayAlignment:
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/connection.rb'
+
+# Offense count: 4
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, IndentOneStep, IndentationWidth.
+# SupportedStyles: case, end
+Layout/CaseIndentation:
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
+
+# Offense count: 24
+# This cop supports safe autocorrection (--autocorrect).
+Layout/EmptyLineAfterGuardClause:
+ Exclude:
+ - 'lib/net/ber.rb'
+ - 'lib/net/ber/core_ext/array.rb'
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/auth_adapter.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/dataset.rb'
+ - 'lib/net/ldap/entry.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'lib/net/snmp.rb'
+ - 'test/integration/test_ber.rb'
# Offense count: 1
-# Configuration parameters: AlignWith, SupportedStyles.
-Lint/EndAlignment:
- Enabled: false
+# This cop supports safe autocorrection (--autocorrect).
+Layout/EmptyLineAfterMagicComment:
+ Exclude:
+ - 'net-ldap.gemspec'
+
+# Offense count: 6
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines.
+Layout/EmptyLineBetweenDefs:
+ Exclude:
+ - 'lib/net/ldap/dataset.rb'
+ - 'lib/net/ldap/error.rb'
+ - 'lib/net/snmp.rb'
# Offense count: 1
-Lint/RescueException:
- Enabled: false
+# This cop supports safe autocorrection (--autocorrect).
+Layout/EmptyLines:
+ Exclude:
+ - 'lib/net/snmp.rb'
# Offense count: 1
-Lint/ShadowingOuterLocalVariable:
- Enabled: false
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowAliasSyntax, AllowedMethods.
+# AllowedMethods: alias_method, public, protected, private
+Layout/EmptyLinesAroundAttributeAccessor:
+ Exclude:
+ - 'lib/net/ber.rb'
-# Offense count: 9
-# Cop supports --auto-correct.
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only
+Layout/EmptyLinesAroundClassBody:
+ Exclude:
+ - 'lib/net/ldap.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+Layout/EmptyLinesAroundExceptionHandlingKeywords:
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyleAlignWith, Severity.
+# SupportedStylesAlignWith: keyword, variable, start_of_line
+Layout/EndAlignment:
+ Exclude:
+ - 'testserver/ldapserver.rb'
+
+# Offense count: 6
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: IndentationWidth.
+# SupportedStyles: special_inside_parentheses, consistent, align_brackets
+Layout/FirstArrayElementIndentation:
+ EnforcedStyle: consistent
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: IndentationWidth.
+# SupportedStyles: special_inside_parentheses, consistent, align_braces
+Layout/FirstHashElementIndentation:
+ EnforcedStyle: consistent
+
+# Offense count: 124
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
+# SupportedHashRocketStyles: key, separator, table
+# SupportedColonStyles: key, separator, table
+# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
+Layout/HashAlignment:
+ Exclude:
+ - 'lib/net/ber.rb'
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/auth_adapter/gss_spnego.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'test/ber/test_ber.rb'
+ - 'test/integration/test_add.rb'
+ - 'test/integration/test_bind.rb'
+ - 'test/integration/test_delete.rb'
+ - 'test/integration/test_open.rb'
+ - 'test/test_helper.rb'
+ - 'test/test_ldap_connection.rb'
+
+# Offense count: 6
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: Width, AllowedPatterns.
+Layout/IndentationWidth:
+ Exclude:
+ - 'lib/net/ber.rb'
+ - 'lib/net/ldap/password.rb'
+ - 'lib/net/snmp.rb'
+
+# Offense count: 14
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment, AllowRBSInlineAnnotation, AllowSteepAnnotation.
+Layout/LeadingCommentSpace:
+ Exclude:
+ - 'lib/net/ber/core_ext/array.rb'
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/entry.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'lib/net/ldap/pdu.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: symmetrical, new_line, same_line
+Layout/MultilineMethodCallBraceLayout:
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
+
+# Offense count: 8
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: space, no_space
+Layout/SpaceAroundEqualsInParameterDefault:
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/snmp.rb'
+
+# Offense count: 4
+# This cop supports safe autocorrection (--autocorrect).
+Layout/SpaceAroundKeyword:
+ Exclude:
+ - 'lib/net/ldap/entry.rb'
+ - 'lib/net/snmp.rb'
+
+# Offense count: 7
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.
+# SupportedStylesForExponentOperator: space, no_space
+# SupportedStylesForRationalLiterals: space, no_space
+Layout/SpaceAroundOperators:
+ Exclude:
+ - 'lib/net/ber/ber_parser.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/entry.rb'
+ - 'lib/net/ldap/filter.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
+# SupportedStyles: space, no_space
+# SupportedStylesForEmptyBraces: space, no_space
+Layout/SpaceInsideBlockBraces:
+ Exclude:
+ - 'lib/net/ldap/dataset.rb'
+
+# Offense count: 8
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: space, compact, no_space
+Layout/SpaceInsideParens:
+ Exclude:
+ - 'lib/net/ldap/entry.rb'
+ - 'lib/net/snmp.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AutoCorrect, AllowComments.
+Lint/EmptyConditionalBody:
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
+
+# Offense count: 1
+# Configuration parameters: AllowComments.
+Lint/EmptyWhen:
+ Exclude:
+ - 'lib/net/ldap/pdu.rb'
+
+# Offense count: 30
+# This cop supports safe autocorrection (--autocorrect).
+Lint/ImplicitStringConcatenation:
+ Exclude:
+ - 'test/test_filter.rb'
+
+# Offense count: 1
+Lint/NonLocalExitFromIterator:
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
+
+# Offense count: 1
+Lint/RescueException:
+ Exclude:
+ - 'lib/net/ldap/pdu.rb'
+
+# Offense count: 10
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AutoCorrect, IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
Lint/UnusedBlockArgument:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/snmp.rb'
-# Offense count: 3
-# Cop supports --auto-correct.
+# Offense count: 7
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
+# NotImplementedExceptions: NotImplementedError
Lint/UnusedMethodArgument:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/entry.rb'
+ - 'lib/net/ldap/pdu.rb'
+ - 'test/test_ldap.rb'
+ - 'test/test_ldap_connection.rb'
+ - 'test/test_search.rb'
-# Offense count: 7
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AutoCorrect, ContextCreatingMethods, MethodCreatingMethods.
+Lint/UselessAccessModifier:
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
+
+# Offense count: 5
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AutoCorrect.
Lint/UselessAssignment:
- Enabled: false
+ Exclude:
+ - 'test/integration/test_add.rb'
+ - 'test/test_ldap_connection.rb'
+ - 'test/test_search.rb'
-# Offense count: 47
+# Offense count: 42
+# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize:
- Max: 114
+ Max: 124
-# Offense count: 11
+# Offense count: 3
+# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
+# AllowedMethods: refine
+Metrics/BlockLength:
+ Max: 119
+
+# Offense count: 6
+# Configuration parameters: CountBlocks, CountModifierForms.
Metrics/BlockNesting:
Max: 4
-# Offense count: 9
-# Configuration parameters: CountComments.
+# Offense count: 12
+# Configuration parameters: CountComments, CountAsOne.
Metrics/ClassLength:
- Max: 470
+ Max: 451
-# Offense count: 20
+# Offense count: 21
+# Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/CyclomaticComplexity:
- Max: 41
+ Max: 45
-# Offense count: 193
-# Configuration parameters: AllowURI, URISchemes.
-Metrics/LineLength:
- Max: 360
-
-# Offense count: 71
-# Configuration parameters: CountComments.
+# Offense count: 79
+# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
Metrics/MethodLength:
Max: 130
-# Offense count: 13
+# Offense count: 1
+# Configuration parameters: CountComments, CountAsOne.
+Metrics/ModuleLength:
+ Max: 103
+
+# Offense count: 12
+# Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/PerceivedComplexity:
- Max: 36
+ Max: 46
# Offense count: 1
-Style/AccessorMethodName:
- Enabled: false
-
-# Offense count: 4
-# Cop supports --auto-correct.
-Style/AlignArray:
- Enabled: false
+Naming/AccessorMethodName:
+ Exclude:
+ - 'lib/net/ldap.rb'
# Offense count: 3
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/AlignParameters:
- Enabled: false
+# This cop supports safe autocorrection (--autocorrect).
+Naming/BinaryOperatorParameterName:
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
+
+# Offense count: 1
+# Configuration parameters: AllowedNames.
+# AllowedNames: module_parent
+Naming/ClassAndModuleCamelCase:
+ Exclude:
+ - 'lib/net/ldap/auth_adapter/gss_spnego.rb'
+
+# Offense count: 88
+Naming/ConstantName:
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'lib/net/ldap/pdu.rb'
+ - 'lib/net/snmp.rb'
+ - 'test/test_ldif.rb'
+ - 'testserver/ldapserver.rb'
+
+# Offense count: 1
+# Configuration parameters: ExpectMatchingDefinition, CheckDefinitionPathHierarchy, CheckDefinitionPathHierarchyRoots, Regex, IgnoreExecutableScripts, AllowedAcronyms.
+# CheckDefinitionPathHierarchyRoots: lib, spec, test, src
+# AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
+Naming/FileName:
+ Exclude:
+ - 'Rakefile.rb'
+ - 'lib/net-ldap.rb'
+
+# Offense count: 11
+# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
+# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
+Naming/MethodParameterName:
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/entry.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'lib/net/snmp.rb'
+ - 'test/test_snmp.rb'
+ - 'testserver/ldapserver.rb'
-# Offense count: 36
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: PreferredName.
+Naming/RescuedExceptionsVariableName:
+ Exclude:
+ - 'lib/net/ldap/pdu.rb'
+
+# Offense count: 9
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: separated, grouped
+Style/AccessorGrouping:
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/pdu.rb'
+
+# Offense count: 11
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: prefer_alias, prefer_alias_method
+Style/Alias:
+ Exclude:
+ - 'lib/net/ber/core_ext/array.rb'
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/entry.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'lib/net/ldap/pdu.rb'
+
+# Offense count: 12
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: always, conditionals
Style/AndOr:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/dataset.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'lib/net/ldap/pdu.rb'
# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: percent_q, bare_percent
Style/BarePercentLiterals:
- Enabled: false
+ Exclude:
+ - 'test/test_entry.rb'
# Offense count: 1
-# Cop supports --auto-correct.
+# This cop supports safe autocorrection (--autocorrect).
Style/BlockComments:
- Enabled: false
-
-# Offense count: 20
-# Cop supports --auto-correct.
-Style/Blocks:
- Enabled: false
-
-# Offense count: 2
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/BracesAroundHashParameters:
- Enabled: false
+ Exclude:
+ - 'test/test_rename.rb'
-# Offense count: 4
-# Configuration parameters: IndentWhenRelativeTo, SupportedStyles, IndentOneStep.
-Style/CaseIndentation:
- Enabled: false
+# Offense count: 1
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: MinBranchesCount.
+Style/CaseLikeIf:
+ Exclude:
+ - 'lib/net/ber/ber_parser.rb'
# Offense count: 4
-# Cop supports --auto-correct.
+# This cop supports safe autocorrection (--autocorrect).
Style/CharacterLiteral:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/dataset.rb'
+ - 'lib/net/ldap/entry.rb'
-# Offense count: 22
-# Configuration parameters: EnforcedStyle, SupportedStyles.
+# Offense count: 23
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle, EnforcedStyleForClasses, EnforcedStyleForModules.
+# SupportedStyles: nested, compact
+# SupportedStylesForClasses: ~, nested, compact
+# SupportedStylesForModules: ~, nested, compact
Style/ClassAndModuleChildren:
Enabled: false
# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: is_a?, kind_of?
Style/ClassCheck:
- Enabled: false
-
-# Offense count: 13
-# Cop supports --auto-correct.
-Style/ColonMethodCall:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber/core_ext/array.rb'
-# Offense count: 2
-# Configuration parameters: Keywords.
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: Keywords, RequireColon.
+# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE
Style/CommentAnnotation:
- Enabled: false
-
-# Offense count: 86
-Style/ConstantName:
- Enabled: false
-
-# Offense count: 18
-# Cop supports --auto-correct.
-Style/DeprecatedHashMethods:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber.rb'
-# Offense count: 46
-Style/Documentation:
- Enabled: false
-
-# Offense count: 23
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/DotPosition:
- Enabled: false
+# Offense count: 8
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/CommentedKeyword:
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/entry.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'lib/net/ldap/pdu.rb'
# Offense count: 1
-# Cop supports --auto-correct.
-Style/ElseAlignment:
- Enabled: false
-
-# Offense count: 4
-# Cop supports --auto-correct.
-# Configuration parameters: AllowAdjacentOneLineDefs.
-Style/EmptyLineBetweenDefs:
- Enabled: false
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions.
+# SupportedStyles: assign_to_condition, assign_inside_condition
+Style/ConditionalAssignment:
+ Exclude:
+ - 'lib/net/ldap/dn.rb'
-# Offense count: 9
-# Cop supports --auto-correct.
-Style/EmptyLines:
- Enabled: false
+# Offense count: 12
+# Configuration parameters: AllowedConstants.
+Style/Documentation:
+ Exclude:
+ - 'spec/**/*'
+ - 'test/**/*'
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/auth_adapter.rb'
+ - 'lib/net/ldap/auth_adapter/sasl.rb'
+ - 'lib/net/ldap/auth_adapter/simple.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/error.rb'
+ - 'lib/net/ldap/instrumentation.rb'
+ - 'lib/net/ldap/password.rb'
+ - 'lib/net/ldap/pdu.rb'
+ - 'lib/net/snmp.rb'
+ - 'testserver/ldapserver.rb'
# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/EmptyLinesAroundClassBody:
- Enabled: false
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AutoCorrect, EnforcedStyle.
+# SupportedStyles: compact, expanded
+Style/EmptyMethod:
+ Exclude:
+ - 'test/test_auth_adapter.rb'
# Offense count: 2
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/EmptyLinesAroundModuleBody:
- Enabled: false
+# This cop supports safe autocorrection (--autocorrect).
+Style/Encoding:
+ Exclude:
+ - 'net-ldap.gemspec'
+ - 'test/test_filter_parser.rb'
# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
Style/EvenOdd:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/dn.rb'
# Offense count: 1
-# Configuration parameters: Exclude.
-Style/FileName:
+# This cop supports safe autocorrection (--autocorrect).
+Style/ExpandPathArguments:
+ Exclude:
+ - 'net-ldap.gemspec'
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+Style/ExplicitBlockArgument:
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/dataset.rb'
+
+# Offense count: 57
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: always, always_true, never
+Style/FrozenStringLiteralComment:
Enabled: false
# Offense count: 9
# Configuration parameters: AllowedVariables.
Style/GlobalVars:
- Enabled: false
+ Exclude:
+ - 'testserver/ldapserver.rb'
-# Offense count: 3
-# Configuration parameters: MinBodyLength.
+# Offense count: 5
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
Style/GuardClause:
- Enabled: false
-
-# Offense count: 150
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
+
+# Offense count: 164
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
+# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
+# SupportedShorthandSyntax: always, never, either, consistent, either_consistent
Style/HashSyntax:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber.rb'
+ - 'lib/net/ber/ber_parser.rb'
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/auth_adapter/gss_spnego.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/pdu.rb'
+ - 'lib/net/snmp.rb'
+ - 'test/test_auth_adapter.rb'
+ - 'test/test_ldap.rb'
+ - 'test/test_ldap_connection.rb'
+ - 'test/test_search.rb'
+ - 'test/test_ssl_ber.rb'
+ - 'testserver/ldapserver.rb'
-# Offense count: 8
-# Configuration parameters: MaxLineLength.
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowIfModifier.
+Style/IfInsideElse:
+ Exclude:
+ - 'lib/net/ldap/instrumentation.rb'
+
+# Offense count: 28
+# This cop supports safe autocorrection (--autocorrect).
Style/IfUnlessModifier:
- Enabled: false
-
-# Offense count: 2
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/IndentHash:
- Enabled: false
-
-# Offense count: 6
-# Cop supports --auto-correct.
-# Configuration parameters: Width.
-Style/IndentationWidth:
- Enabled: false
-
-# Offense count: 2
-# Cop supports --auto-correct.
-Style/LeadingCommentSpace:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber.rb'
+ - 'lib/net/ber/core_ext/integer.rb'
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/auth_adapter.rb'
+ - 'lib/net/ldap/auth_adapter/sasl.rb'
+ - 'lib/net/ldap/auth_adapter/simple.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'lib/net/snmp.rb'
+ - 'test/integration/test_delete.rb'
+ - 'test/integration/test_password_modify.rb'
# Offense count: 21
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline
Style/MethodDefParentheses:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber.rb'
+ - 'lib/net/ldap/pdu.rb'
+ - 'lib/net/snmp.rb'
+ - 'testserver/ldapserver.rb'
-# Offense count: 1
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/MethodName:
- Enabled: false
+# Offense count: 2
+Style/MissingRespondToMissing:
+ Exclude:
+ - 'lib/net/ldap/dn.rb'
+ - 'lib/net/ldap/entry.rb'
-# Offense count: 5
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/MultilineOperationIndentation:
- Enabled: false
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+Style/MultilineIfModifier:
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
+
+# Offense count: 26
+# This cop supports safe autocorrection (--autocorrect).
+Style/MultilineWhenThen:
+ Exclude:
+ - 'lib/net/ldap/dn.rb'
# Offense count: 1
-Style/MultilineTernaryOperator:
- Enabled: false
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowMethodComparison, ComparisonsThreshold.
+Style/MultipleComparison:
+ Exclude:
+ - 'lib/net/ldap/dataset.rb'
+
+# Offense count: 26
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: literals, strict
+Style/MutableConstant:
+ Exclude:
+ - 'lib/net/ber.rb'
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/dn.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'lib/net/ldap/version.rb'
+ - 'lib/net/snmp.rb'
+ - 'test/test_ldif.rb'
+ - 'testserver/ldapserver.rb'
# Offense count: 1
-# Cop supports --auto-correct.
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: both, prefix, postfix
Style/NegatedIf:
- Enabled: false
+ Exclude:
+ - 'test/test_helper.rb'
# Offense count: 1
-# Cop supports --auto-correct.
+# This cop supports safe autocorrection (--autocorrect).
Style/NegatedWhile:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
# Offense count: 3
-# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, MinBodyLength, AllowConsecutiveConditionals.
+# SupportedStyles: skip_modifier_ifs, always
Style/Next:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
+ - 'testserver/ldapserver.rb'
# Offense count: 1
-# Cop supports --auto-correct.
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: predicate, comparison
Style/NilComparison:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
# Offense count: 1
-# Cop supports --auto-correct.
+# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: IncludeSemanticChanges.
Style/NonNilCheck:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber/ber_parser.rb'
# Offense count: 1
-# Cop supports --auto-correct.
+# This cop supports safe autocorrection (--autocorrect).
Style/Not:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
-# Offense count: 10
-# Cop supports --auto-correct.
+# Offense count: 13
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: Strict, AllowedNumbers, AllowedPatterns.
Style/NumericLiterals:
MinDigits: 8
-# Offense count: 3
-Style/OpMethod:
- Enabled: false
+# Offense count: 14
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns.
+# SupportedStyles: predicate, comparison
+Style/NumericPredicate:
+ Exclude:
+ - 'spec/**/*'
+ - 'lib/net/ber/core_ext/integer.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/dn.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'testserver/ldapserver.rb'
-# Offense count: 6
-# Cop supports --auto-correct.
-# Configuration parameters: AllowSafeAssignment.
+# Offense count: 1
+# Configuration parameters: AllowedMethods.
+# AllowedMethods: respond_to_missing?
+Style/OptionalBooleanParameter:
+ Exclude:
+ - 'lib/net/ldap/entry.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowSafeAssignment, AllowInMultilineConditions.
Style/ParenthesesAroundCondition:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/auth_adapter/sasl.rb'
-# Offense count: 3
-# Cop supports --auto-correct.
+# Offense count: 13
+# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: PreferredDelimiters.
Style/PercentLiteralDelimiters:
- Enabled: false
+ Exclude:
+ - 'net-ldap.gemspec'
+ - 'test/integration/test_add.rb'
+ - 'test/integration/test_delete.rb'
+ - 'test/integration/test_open.rb'
+ - 'test/integration/test_password_modify.rb'
+ - 'test/test_entry.rb'
+ - 'test/test_helper.rb'
-# Offense count: 11
-# Cop supports --auto-correct.
+# Offense count: 20
+# This cop supports safe autocorrection (--autocorrect).
Style/PerlBackrefs:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/dataset.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'test/test_ldif.rb'
+ - 'testserver/ldapserver.rb'
-# Offense count: 9
-# Configuration parameters: EnforcedStyle, SupportedStyles.
+# Offense count: 10
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle, AllowedCompactTypes.
+# SupportedStyles: compact, exploded
Style/RaiseArgs:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/pdu.rb'
+ - 'lib/net/snmp.rb'
-# Offense count: 1
-# Cop supports --auto-correct.
+# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
Style/RedundantBegin:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/snmp.rb'
+
+# Offense count: 4
+# This cop supports safe autocorrection (--autocorrect).
+Style/RedundantParentheses:
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
+ - 'test/test_filter.rb'
+
+# Offense count: 5
+# This cop supports safe autocorrection (--autocorrect).
+Style/RedundantPercentQ:
+ Exclude:
+ - 'net-ldap.gemspec'
+ - 'test/test_entry.rb'
+
+# Offense count: 11
+# This cop supports safe autocorrection (--autocorrect).
+Style/RedundantRegexpCharacterClass:
+ Exclude:
+ - 'lib/net/ber/core_ext/integer.rb'
+ - 'lib/net/ldap/dataset.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'testserver/ldapserver.rb'
+
+# Offense count: 5
+# This cop supports safe autocorrection (--autocorrect).
+Style/RedundantRegexpEscape:
+ Exclude:
+ - 'lib/net/ldap/dataset.rb'
+ - 'lib/net/ldap/filter.rb'
# Offense count: 3
-# Cop supports --auto-correct.
+# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowMultipleReturnValues.
Style/RedundantReturn:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber/core_ext/string.rb'
+ - 'lib/net/ldap/auth_adapter.rb'
+ - 'lib/net/ldap/entry.rb'
-# Offense count: 7
-# Cop supports --auto-correct.
+# Offense count: 8
+# This cop supports safe autocorrection (--autocorrect).
Style/RedundantSelf:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber/core_ext/array.rb'
+ - 'lib/net/ber/core_ext/string.rb'
+ - 'lib/net/ldap/dn.rb'
+ - 'lib/net/ldap/filter.rb'
-# Offense count: 1
-# Configuration parameters: MaxSlashes.
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, AllowInnerSlashes.
+# SupportedStyles: slashes, percent_r, mixed
Style/RegexpLiteral:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
+ - 'net-ldap.gemspec'
-# Offense count: 2
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
Style/RescueModifier:
- Enabled: false
-
-# Offense count: 7
-# Cop supports --auto-correct.
-# Configuration parameters: AllowAsExpressionSeparator.
-Style/Semicolon:
- Enabled: false
-
-# Offense count: 61
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/SignalException:
- Enabled: false
+ Exclude:
+ - 'test/ber/core_ext/test_string.rb'
# Offense count: 2
-# Configuration parameters: Methods.
-Style/SingleLineBlockParams:
- Enabled: false
-
-# Offense count: 2
-# Cop supports --auto-correct.
-Style/SingleSpaceBeforeFirstArg:
- Enabled: false
-
-# Offense count: 24
-# Cop supports --auto-correct.
-Style/SpaceAfterComma:
- Enabled: false
-
-# Offense count: 2
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/SpaceAroundEqualsInParameterDefault:
- Enabled: false
-
-# Offense count: 8
-# Cop supports --auto-correct.
-Style/SpaceAroundOperators:
- Enabled: false
-
-# Offense count: 2
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/SpaceBeforeBlockBraces:
- Enabled: false
-
-# Offense count: 18
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
-Style/SpaceInsideBlockBraces:
- Enabled: false
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: implicit, explicit
+Style/RescueStandardError:
+ Exclude:
+ - 'lib/net/snmp.rb'
+ - 'testserver/ldapserver.rb'
-# Offense count: 37
-# Cop supports --auto-correct.
-Style/SpaceInsideBrackets:
- Enabled: false
+# Offense count: 13
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
+# AllowedMethods: present?, blank?, presence, try, try!
+Style/SafeNavigation:
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/dataset.rb'
+ - 'lib/net/ldap/pdu.rb'
-# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SupportedStyles.
-Style/SpaceInsideHashLiteralBraces:
- Enabled: false
+# Offense count: 7
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowAsExpressionSeparator.
+Style/Semicolon:
+ Exclude:
+ - 'lib/net/ldap/dn.rb'
+ - 'testserver/ldapserver.rb'
-# Offense count: 20
-# Cop supports --auto-correct.
-Style/SpaceInsideParens:
- Enabled: false
+# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowModifier.
+Style/SoleNestedConditional:
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/connection.rb'
-# Offense count: 5
-# Cop supports --auto-correct.
+# Offense count: 4
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: RequireEnglish, EnforcedStyle.
+# SupportedStyles: use_perl_names, use_english_names, use_builtin_english_names
Style/SpecialGlobalVars:
- Enabled: false
-
-# Offense count: 645
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
+ Exclude:
+ - 'lib/net/snmp.rb'
+ - 'testserver/ldapserver.rb'
+
+# Offense count: 15
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: Mode.
+Style/StringConcatenation:
+ Exclude:
+ - 'lib/net/ldap/dn.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'lib/net/ldap/password.rb'
+ - 'test/ber/test_ber.rb'
+ - 'test/test_ldif.rb'
+ - 'test/test_snmp.rb'
+
+# Offense count: 728
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
+# SupportedStyles: single_quotes, double_quotes
Style/StringLiterals:
Enabled: false
-# Offense count: 10
-# Cop supports --auto-correct.
-# Configuration parameters: IgnoredMethods.
-Style/SymbolProc:
- Enabled: false
-
# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/TrailingBlankLines:
- Enabled: false
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/StructInheritance:
+ Exclude:
+ - 'test/test_ldap.rb'
-# Offense count: 9
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
-Style/TrailingComma:
+# Offense count: 11
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: MinSize.
+# SupportedStyles: percent, brackets
+Style/SymbolArray:
+ EnforcedStyle: brackets
+
+# Offense count: 4
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, AllowSafeAssignment.
+# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
+Style/TernaryParentheses:
+ Exclude:
+ - 'lib/net/ber/core_ext/integer.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/dataset.rb'
+
+# Offense count: 38
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyleForMultiline.
+# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
+Style/TrailingCommaInHashLiteral:
Enabled: false
# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, Whitelist.
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, AllowedMethods.
+# AllowedMethods: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym
Style/TrivialAccessors:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
-# Offense count: 5
-# Cop supports --auto-correct.
-Style/UnneededPercentQ:
- Enabled: false
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+Style/UnpackFirst:
+ Exclude:
+ - 'lib/net/ber/ber_parser.rb'
# Offense count: 1
-# Configuration parameters: MaxLineLength.
+# This cop supports safe autocorrection (--autocorrect).
Style/WhileUntilModifier:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
# Offense count: 1
-# Cop supports --auto-correct.
+# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: WordRegex.
+# SupportedStyles: percent, brackets
Style/WordArray:
- MinSize: 2
+ EnforcedStyle: percent
+ MinSize: 3
+
+# Offense count: 1
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: forbid_for_all_comparison_operators, forbid_for_equality_operators_only, require_for_all_comparison_operators, require_for_equality_operators_only
+Style/YodaCondition:
+ Exclude:
+ - 'lib/net/ber/ber_parser.rb'
+
+# Offense count: 6
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/ZeroLengthPredicate:
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'testserver/ldapserver.rb'
+
+# Offense count: 27
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
+# URISchemes: http, https
+Layout/LineLength:
+ Max: 360
diff --git a/.travis.yml b/.travis.yml
index 4131d6e4..8956efb8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,20 +1,49 @@
language: ruby
rvm:
- - 1.9.3
- 2.0.0
- 2.1
- 2.2
+ - 2.3
+ - 2.4
+ - 2.5
+ - 2.6
+ - 2.7
+ - jruby-9.2
# optional
- ruby-head
- jruby-19mode
+ - jruby-9.2
- jruby-head
- - rbx-2
+
+addons:
+ hosts:
+ - ldap.example.org # needed for TLS verification
+ - cert.mismatch.example.org
+
+services:
+ - docker
env:
- INTEGRATION=openldap
+cache: bundler
+
+before_install:
+ - gem update bundler
+
install:
- - if [ "$INTEGRATION" = "openldap" ]; then sudo script/install-openldap; fi
+ - >
+ docker run \
+ --hostname ldap.example.org \
+ --env LDAP_TLS_VERIFY_CLIENT=try \
+ -p 389:389 \
+ -p 636:636 \
+ -v "$(pwd)"/test/fixtures/ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom \
+ --name openldap \
+ --detach \
+ osixia/openldap:1.3.0 \
+ --copy-service \
+ --loglevel debug \
- bundle install
script: bundle exec rake ci
@@ -23,8 +52,8 @@ matrix:
allow_failures:
- rvm: ruby-head
- rvm: jruby-19mode
+ - rvm: jruby-9.2
- rvm: jruby-head
- - rvm: rbx-2
fast_finish: true
notifications:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0247a3d4..ee5335b7 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -49,6 +49,6 @@ MyClass.new \
baz: 'garply'
```
-[issues]: https://github.com/ruby-net-ldap/ruby-net-ldap/issues
+[issues]: https://github.com/ruby-ldap/ruby-net-ldap/issues
[pr]: https://help.github.com/articles/using-pull-requests
[travis]: https://travis-ci.org/ruby-ldap/ruby-net-ldap
diff --git a/Contributors.rdoc b/Contributors.rdoc
index e40b20db..137394f8 100644
--- a/Contributors.rdoc
+++ b/Contributors.rdoc
@@ -22,3 +22,4 @@ Contributions since:
* David J. Lee (DavidJLee)
* Cody Cutrer (ccutrer)
* WoodsBagotAndreMarquesLee
+* Rufus Post (mynameisrufus)
diff --git a/Gemfile b/Gemfile
index 851fabc2..10d2031f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,2 +1,8 @@
source 'https://rubygems.org/'
gemspec
+
+gem "debug", platform: :mri
+gem "flexmock", "~> 1.3"
+gem "rake", "~> 12.3.3"
+gem "rubocop", "~> 1.48"
+gem "test-unit"
diff --git a/History.rdoc b/History.rdoc
index 1e0270a8..919eaf67 100644
--- a/History.rdoc
+++ b/History.rdoc
@@ -1,3 +1,126 @@
+=== Net::LDAP 0.20.0
+* Update test.yml by @HarlemSquirrel in #433
+* Add `ostruct` as a dependency to the gemspec by @Ivanov-Anton in #432
+* Require Ruby >= 3.0 by @HarlemSquirrel in #435
+* Link to usage examples by @sebbASF in #428
+* Add controls for modify and add operations by @zeroSteiner in #426
+* Add support for ldapwhoami (RFC4532) (now with tests) by @zeroSteiner in #425
+* Update for ruby 3.4 by @HarlemSquirrel in #439
+* Add ruby 3.4 to CI by @hakeem0114 in #438
+* Add support for UTF-8 encoded passwords by @frankwalentowski in #430
+
+=== Net::LDAP 0.19.0
+* Net::LDAP::DN - Retain trailing spaces in RDN values in DNs #412
+* Add in ability for users to specify LDAP controls when conducting searches #411
+* Document connect_timeout in Constructor Details #415
+* Fix openssl error when using multiple hosts #417
+
+=== Net::LDAP 0.18.0
+* Fix escaping of # and space in attrs #408
+* Add support to use SNI #406
+* Drop Ruby 2.5 and JRuby 9.2 from CI tests
+* Bump rubocop to 1.48.1
+* Update CI for TruffleRuby 22
+
+=== Net::LDAP 0.17.1
+* Fixed shebang of bash #385
+* Omit some tests for now until we update our CA cert #386
+* Add Ruby 3.0 support #388
+* Add TruffleRuby 21.0.0 to CI #389
+* Correct a typo in an error message #391
+* Enable bundler caching for travis #390
+* Fix circular require while loading lib/net/ldap/entry.rb and lib/net/ldap/dataset.rb #392
+* Handle nil value in GetbyteForSSLSocket::getbyte #306
+
+=== Net::LDAP 0.17.0
+* Added private recursive_delete as alternative to DELETE_TREE #268
+* Test suite updates #373 #376 #377
+* Use Base64.strict_encode64 and SSHA256 #303
+* Remove deprecated ConnectionRefusedError #366
+* Added method to get a duplicate of the internal Hash #286
+* remove a circular require #380
+* fix LdapServerAsnSyntax compile #379
+* Implement '==' operator for entries #381
+* fix for undefined method for write exception #383
+
+=== Net::LDAP 0.16.3
+
+* Add Net::LDAP::InvalidDNError #371
+* Use require_relative instead of require #360
+* Address some warnings and fix JRuby test omissions #365
+* Bump rake dev dependency to 12.3 #359
+* Enable rubocop in ci #251
+* Enhance rubocop configuration and test syntax #344
+* CI: Drop rbx-2, uninstallable #364
+* Fix RuboCop warnings #312
+* Fix wrong error class #305
+* CONTRIBUTING.md: Repair link to Issues #309
+* Make the generate() method more idiomatic... #326
+* Make encode_sort_controls() more idiomatic... #327
+* Make the instrument() method more idiomatic... #328
+* Fix uninitialised Net::LDAP::LdapPduError #338
+* README.rdoc: Use SVG build badge #310
+* Update TravisCI config to inclue Ruby 2.7 #346
+* add explicit ** to silence Ruby 2.7 warning #342
+* Support parsing filters with attribute tags #345
+* Bump rubocop development dependency version #336
+* Add link to generated and hosted documentation on rubydoc #319
+* Fix 'uninitialized constant Net::LDAP::PDU::LdapPduError' error #317
+* simplify encoding logic: no more chomping required #362
+
+=== Net::LDAP 0.16.2
+
+* Net::LDAP#open does not cache bind result {#334}[https://github.com/ruby-ldap/ruby-net-ldap/pull/334]
+* Fix CI build {#333}[https://github.com/ruby-ldap/ruby-net-ldap/pull/333]
+* Fix to "undefined method 'result_code'" {#308}[https://github.com/ruby-ldap/ruby-net-ldap/pull/308]
+* Fixed Exception: incompatible character encodings: ASCII-8BIT and UTF-8 in filter.rb {#285}[https://github.com/ruby-ldap/ruby-net-ldap/pull/285]
+
+=== Net::LDAP 0.16.1
+
+* Send DN and newPassword with password_modify request {#271}[https://github.com/ruby-ldap/ruby-net-ldap/pull/271]
+
+=== Net::LDAP 0.16.0
+
+* Sasl fix {#281}[https://github.com/ruby-ldap/ruby-net-ldap/pull/281]
+* enable TLS hostname validation {#279}[https://github.com/ruby-ldap/ruby-net-ldap/pull/279]
+* update rubocop to 0.42.0 {#278}[https://github.com/ruby-ldap/ruby-net-ldap/pull/278]
+
+=== Net::LDAP 0.15.0
+
+* Respect connect_timeout when establishing SSL connections {#273}[https://github.com/ruby-ldap/ruby-net-ldap/pull/273]
+
+=== Net::LDAP 0.14.0
+
+* Normalize the encryption parameter passed to the LDAP constructor {#264}[https://github.com/ruby-ldap/ruby-net-ldap/pull/264]
+* Update Docs: Net::LDAP now requires ruby >= 2 {#261}[https://github.com/ruby-ldap/ruby-net-ldap/pull/261]
+* fix symbol proc {#255}[https://github.com/ruby-ldap/ruby-net-ldap/pull/255]
+* fix trailing commas {#256}[https://github.com/ruby-ldap/ruby-net-ldap/pull/256]
+* fix deprecated hash methods {#254}[https://github.com/ruby-ldap/ruby-net-ldap/pull/254]
+* fix space after comma {#253}[https://github.com/ruby-ldap/ruby-net-ldap/pull/253]
+* fix space inside brackets {#252}[https://github.com/ruby-ldap/ruby-net-ldap/pull/252]
+* Rubocop style fixes {#249}[https://github.com/ruby-ldap/ruby-net-ldap/pull/249]
+* Lazy initialize Net::LDAP::Connection's internal socket {#235}[https://github.com/ruby-ldap/ruby-net-ldap/pull/235]
+* Support for rfc3062 Password Modify, closes #163 {#178}[https://github.com/ruby-ldap/ruby-net-ldap/pull/178]
+
+=== Net::LDAP 0.13.0
+
+Avoid this release for because of an backwards incompatibility in how encryption
+is initialized https://github.com/ruby-ldap/ruby-net-ldap/pull/264. We did not
+yank it because people have already worked around it.
+
+* Set a connect_timeout for the creation of a socket {#243}[https://github.com/ruby-ldap/ruby-net-ldap/pull/243]
+* Update bundler before installing gems with bundler {#245}[https://github.com/ruby-ldap/ruby-net-ldap/pull/245]
+* Net::LDAP#encryption accepts string {#239}[https://github.com/ruby-ldap/ruby-net-ldap/pull/239]
+* Adds correct UTF-8 encoding to Net::BER::BerIdentifiedString {#242}[https://github.com/ruby-ldap/ruby-net-ldap/pull/242]
+* Remove 2.3.0-preview since ruby-head already is included {#241}[https://github.com/ruby-ldap/ruby-net-ldap/pull/241]
+* Drop support for ruby 1.9.3 {#240}[https://github.com/ruby-ldap/ruby-net-ldap/pull/240]
+* Fixed capitalization of StartTLSError {#234}[https://github.com/ruby-ldap/ruby-net-ldap/pull/234]
+
+=== Net::LDAP 0.12.1
+
+* Whitespace formatting cleanup {#236}[https://github.com/ruby-ldap/ruby-net-ldap/pull/236]
+* Set operation result if LDAP server is not accessible {#232}[https://github.com/ruby-ldap/ruby-net-ldap/pull/232]
+
=== Net::LDAP 0.12.0
* DRY up connection handling logic {#224}[https://github.com/ruby-ldap/ruby-net-ldap/pull/224]
diff --git a/README.rdoc b/README.rdoc
index b7f6b311..88bdba61 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -1,4 +1,6 @@
-= Net::LDAP for Ruby {
}[https://travis-ci.org/ruby-ldap/ruby-net-ldap]
+= Net::LDAP for Ruby
+{
}[https://badge.fury.io/rb/net-ldap]
+{
}[https://travis-ci.org/ruby-ldap/ruby-net-ldap]
== Description
@@ -21,11 +23,11 @@ the most recent LDAP RFCs (4510–4519, plus portions of 4520–4532).
== Synopsis
-See Net::LDAP for documentation and usage samples.
+See {Net::LDAP on rubydoc.info}[https://www.rubydoc.info/github/ruby-ldap/ruby-net-ldap/Net/LDAP] for documentation and usage samples.
== Requirements
-Net::LDAP requires a Ruby 1.9.3 compatible interpreter or better.
+Net::LDAP requires a Ruby 2.0.0 compatible interpreter or better.
== Install
@@ -52,19 +54,27 @@ This task will run the test suite and the
rake rubotest
-To run the integration tests against an LDAP server:
+CI takes too long? If your local box supports
+{Docker}[https://www.docker.com/], you can also run integration tests locally.
+Simply run:
- cd test/support/vm/openldap
- vagrant up
- cd ../../../..
- INTEGRATION=openldap bundle exec rake rubotest
+ script/ldap-docker
+ INTEGRATION=openldap rake test
+
+Or, use {Docker Compose}[https://docs.docker.com/compose/]. See docker-compose.yml for available Ruby versions.
+
+ docker-compose run ci-2.7
+
+CAVEAT: you need to add the following line to /etc/hosts
+ 127.0.0.1 ldap.example.org
+ 127.0.0.1 cert.mismatch.example.org
== Release
This section is for gem maintainers to cut a new version of the gem.
* Check out a new branch `release-VERSION`
-* Update lib/net/ldap/version.rb to next version number X.X.X following {semver}(http://semver.org/).
+* Update lib/net/ldap/version.rb to next version number X.X.X following {semver}[http://semver.org/].
* Update `History.rdoc`. Get latest changes with `script/changelog`
* Open a pull request with these changes for review
* After merging, on the master branch, run `script/release`
diff --git a/Rakefile b/Rakefile
index 51ab55dc..da4cf8e7 100644
--- a/Rakefile
+++ b/Rakefile
@@ -15,7 +15,7 @@ Rake::TestTask.new do |t|
end
desc 'Run tests and RuboCop (RuboCop runs on mri only)'
-task ci: [:test]
+task ci: Bundler.current_ruby.mri? ? [:test, :rubocop] : [:test]
desc 'Run tests and RuboCop'
task rubotest: [:test, :rubocop]
diff --git a/ci-run.sh b/ci-run.sh
new file mode 100755
index 00000000..cef309c0
--- /dev/null
+++ b/ci-run.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+set -e
+
+gem install bundler
+ruby -v | grep jruby && apt update && apt install -y gcc
+bundle check || bundle install
+bundle exec rake ci
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..4fbfbec8
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,136 @@
+networks:
+ integration_test_network:
+
+services:
+ openldap:
+ image: osixia/openldap:1.4.0
+ networks:
+ integration_test_network:
+ aliases:
+ - ldap.example.org
+ - cert.mismatch.example.org
+ environment:
+ LDAP_TLS_VERIFY_CLIENT: "try"
+ LDAP_SEED_INTERNAL_LDIF_PATH: "/ldif"
+ healthcheck:
+ test: ["CMD", "ldapsearch", "-x", "-s", "base"]
+ interval: 60s
+ start_period: 30s
+ timeout: 5s
+ retries: 1
+ hostname: "ldap.example.org"
+ volumes:
+ - ./test/fixtures/ldif:/ldif:ro
+
+ ci-3.0:
+ image: ruby:3.0
+ command: /code/ci-run.sh
+ environment:
+ INTEGRATION: openldap
+ INTEGRATION_HOST: ldap.example.org
+ depends_on:
+ - openldap
+ networks:
+ integration_test_network:
+ volumes:
+ - .:/code
+ working_dir: /code
+
+ ci-3.1:
+ image: ruby:3.1
+ command: /code/ci-run.sh
+ environment:
+ INTEGRATION: openldap
+ INTEGRATION_HOST: ldap.example.org
+ depends_on:
+ - openldap
+ networks:
+ integration_test_network:
+ volumes:
+ - .:/code
+ working_dir: /code
+
+ ci-3.2:
+ image: ruby:3.2
+ command: /code/ci-run.sh
+ environment:
+ INTEGRATION: openldap
+ INTEGRATION_HOST: ldap.example.org
+ depends_on:
+ - openldap
+ networks:
+ integration_test_network:
+ volumes:
+ - .:/code
+ working_dir: /code
+
+ ci-3.3:
+ image: ruby:3.3
+ command: /code/ci-run.sh
+ environment:
+ INTEGRATION: openldap
+ INTEGRATION_HOST: ldap.example.org
+ depends_on:
+ - openldap
+ networks:
+ integration_test_network:
+ volumes:
+ - .:/code
+ working_dir: /code
+
+ ci-3.4:
+ image: ruby:3.4
+ entrypoint: /code/ci-run.sh
+ environment:
+ INTEGRATION: openldap
+ INTEGRATION_HOST: ldap.example.org
+ depends_on:
+ - openldap
+ networks:
+ integration_test_network:
+ volumes:
+ - .:/code
+ working_dir: /code
+
+ # https://github.com/flavorjones/truffleruby/pkgs/container/truffleruby
+ ci-truffleruby:
+ image: ghcr.io/flavorjones/truffleruby:stable
+ command: /code/ci-run.sh
+ environment:
+ INTEGRATION: openldap
+ INTEGRATION_HOST: ldap.example.org
+ depends_on:
+ - openldap
+ networks:
+ integration_test_network:
+ volumes:
+ - .:/code
+ working_dir: /code
+
+ ci-jruby-9.3:
+ image: jruby:9.3
+ command: /code/ci-run.sh
+ environment:
+ INTEGRATION: openldap
+ INTEGRATION_HOST: ldap.example.org
+ depends_on:
+ - openldap
+ networks:
+ integration_test_network:
+ volumes:
+ - .:/code
+ working_dir: /code
+
+ ci-jruby-9.4:
+ image: jruby:9.4
+ command: /code/ci-run.sh
+ environment:
+ INTEGRATION: openldap
+ INTEGRATION_HOST: ldap.example.org
+ depends_on:
+ - openldap
+ networks:
+ integration_test_network:
+ volumes:
+ - .:/code
+ working_dir: /code
diff --git a/lib/net-ldap.rb b/lib/net-ldap.rb
index 879851eb..717878ca 100644
--- a/lib/net-ldap.rb
+++ b/lib/net-ldap.rb
@@ -1,2 +1,2 @@
# -*- ruby encoding: utf-8 -*-
-require 'net/ldap'
+require_relative 'net/ldap'
diff --git a/lib/net/ber.rb b/lib/net/ber.rb
index b4b9e9da..34696cc3 100644
--- a/lib/net/ber.rb
+++ b/lib/net/ber.rb
@@ -1,5 +1,5 @@
# -*- ruby encoding: utf-8 -*-
-require 'net/ldap/version'
+require_relative 'ldap/version'
module Net # :nodoc:
##
@@ -106,6 +106,7 @@ module Net # :nodoc:
#
| CHARACTER STRING | C | 29: 61 (0x3d, 0b00111101) |
# | BMPString | P | 30: 30 (0x1e, 0b00011110) |
# | BMPString | C | 30: 62 (0x3e, 0b00111110) |
+ # | ExtendedResponse | C | 107: 139 (0x8b, 0b010001011) |
#
module BER
VERSION = Net::LDAP::VERSION
@@ -234,7 +235,7 @@ def self.compile_syntax(syntax)
# TODO 20100327 AZ: Should we be allocating an array of 256 values
# that will either be +nil+ or an object type symbol, or should we
# allocate an empty Hash since unknown values return +nil+ anyway?
- out = [ nil ] * 256
+ out = [nil] * 256
syntax.each do |tag_class_id, encodings|
tag_class = TAG_CLASS[tag_class_id]
encodings.each do |encoding_id, classes|
@@ -269,7 +270,7 @@ class Net::BER::BerIdentifiedOid
def initialize(oid)
if oid.is_a?(String)
- oid = oid.split(/\./).map {|s| s.to_i }
+ oid = oid.split(/\./).map(&:to_i)
end
@value = oid
end
@@ -293,14 +294,43 @@ def to_arr
##
# A String object with a BER identifier attached.
+#
class Net::BER::BerIdentifiedString < String
attr_accessor :ber_identifier
+
+ # The binary data provided when parsing the result of the LDAP search
+ # has the encoding 'ASCII-8BIT' (which is basically 'BINARY', or 'unknown').
+ #
+ # This is the kind of a backtrace showing how the binary `data` comes to
+ # BerIdentifiedString.new(data):
+ #
+ # @conn.read_ber(syntax)
+ # -> StringIO.new(self).read_ber(syntax), i.e. included from module
+ # -> Net::BER::BERParser.read_ber(syntax)
+ # -> (private)Net::BER::BERParser.parse_ber_object(syntax, id, data)
+ #
+ # In the `#parse_ber_object` method `data`, according to its OID, is being
+ # 'casted' to one of the Net::BER:BerIdentifiedXXX classes.
+ #
+ # As we are using LDAP v3 we can safely assume that the data is encoded
+ # in UTF-8 and therefore the only thing to be done when instantiating is to
+ # switch the encoding from 'ASCII-8BIT' to 'UTF-8'.
+ #
+ # Unfortunately, there are some ActiveDirectory specific attributes
+ # (like `objectguid`) that should remain binary (do they really?).
+ # Using the `#valid_encoding?` we can trap this cases. Special cases like
+ # Japanese, Korean, etc. encodings might also profit from this. However
+ # I have no clue how this encodings function.
def initialize args
- super begin
- args.respond_to?(:encode) ? args.encode('UTF-8') : args
- rescue
- args
- end
+ super
+ #
+ # Check the encoding of the newly created String and set the encoding
+ # to 'UTF-8' (NOTE: we do NOT change the bytes, but only set the
+ # encoding to 'UTF-8').
+ return unless encoding == Encoding::BINARY
+ current_encoding = encoding
+ force_encoding('UTF-8')
+ force_encoding(current_encoding) unless valid_encoding?
end
end
@@ -319,4 +349,4 @@ def to_ber
Null = Net::BER::BerIdentifiedNull.new
end
-require 'net/ber/core_ext'
+require_relative 'ber/core_ext'
diff --git a/lib/net/ber/ber_parser.rb b/lib/net/ber/ber_parser.rb
index 09de8c82..39d3737e 100644
--- a/lib/net/ber/ber_parser.rb
+++ b/lib/net/ber/ber_parser.rb
@@ -14,7 +14,7 @@ module Net::BER::BERParser
}
constructed = {
16 => :array,
- 17 => :array
+ 17 => :array,
}
universal = { :primitive => primitive, :constructed => constructed }
@@ -172,10 +172,10 @@ def read_ber(syntax = nil)
yield id, content_length if block_given?
if -1 == content_length
- raise Net::BER::BerError, "Indeterminite BER content length not implemented."
- else
- data = read(content_length)
+ raise Net::BER::BerError,
+ "Indeterminite BER content length not implemented."
end
+ data = read(content_length)
parse_ber_object(syntax, id, data)
end
diff --git a/lib/net/ber/core_ext.rb b/lib/net/ber/core_ext.rb
index b1939844..37e0993b 100644
--- a/lib/net/ber/core_ext.rb
+++ b/lib/net/ber/core_ext.rb
@@ -1,5 +1,5 @@
# -*- ruby encoding: utf-8 -*-
-require 'net/ber/ber_parser'
+require_relative 'ber_parser'
# :stopdoc:
class IO
include Net::BER::BERParser
@@ -19,35 +19,35 @@ class OpenSSL::SSL::SSLSocket
module Net::BER::Extensions # :nodoc:
end
-require 'net/ber/core_ext/string'
+require_relative 'core_ext/string'
# :stopdoc:
class String
include Net::BER::BERParser
include Net::BER::Extensions::String
end
-require 'net/ber/core_ext/array'
+require_relative 'core_ext/array'
# :stopdoc:
class Array
include Net::BER::Extensions::Array
end
# :startdoc:
-require 'net/ber/core_ext/integer'
+require_relative 'core_ext/integer'
# :stopdoc:
class Integer
include Net::BER::Extensions::Integer
end
# :startdoc:
-require 'net/ber/core_ext/true_class'
+require_relative 'core_ext/true_class'
# :stopdoc:
class TrueClass
include Net::BER::Extensions::TrueClass
end
# :startdoc:
-require 'net/ber/core_ext/false_class'
+require_relative 'core_ext/false_class'
# :stopdoc:
class FalseClass
include Net::BER::Extensions::FalseClass
diff --git a/lib/net/ber/core_ext/array.rb b/lib/net/ber/core_ext/array.rb
index 250fa243..9deb4a1e 100644
--- a/lib/net/ber/core_ext/array.rb
+++ b/lib/net/ber/core_ext/array.rb
@@ -89,7 +89,7 @@ def to_ber_control
#if our array does not contain at least one array then wrap it in an array before going forward
ary = self[0].kind_of?(Array) ? self : [self]
ary = ary.collect do |control_sequence|
- control_sequence.collect{|element| element.to_ber}.to_ber_sequence.reject_empty_ber_arrays
+ control_sequence.collect(&:to_ber).to_ber_sequence.reject_empty_ber_arrays
end
ary.to_ber_sequence.reject_empty_ber_arrays
end
diff --git a/lib/net/ber/core_ext/integer.rb b/lib/net/ber/core_ext/integer.rb
index b2149f9b..78313045 100644
--- a/lib/net/ber/core_ext/integer.rb
+++ b/lib/net/ber/core_ext/integer.rb
@@ -20,7 +20,7 @@ def to_ber_length_encoding
if self <= 127
[self].pack('C')
else
- i = [self].pack('N').sub(/^[\0]+/,"")
+ i = [self].pack('N').sub(/^[\0]+/, "")
[0x80 + i.length].pack('C') + i
end
end
diff --git a/lib/net/ber/core_ext/string.rb b/lib/net/ber/core_ext/string.rb
index e8a43e2c..995d26d4 100644
--- a/lib/net/ber/core_ext/string.rb
+++ b/lib/net/ber/core_ext/string.rb
@@ -75,6 +75,6 @@ def read_ber!(syntax = nil)
end
def reject_empty_ber_arrays
- self.gsub(/0\000/n,'')
+ self.gsub(/0\000/n, '')
end
end
diff --git a/lib/net/ldap.rb b/lib/net/ldap.rb
index 7c151895..8dca73c0 100644
--- a/lib/net/ldap.rb
+++ b/lib/net/ldap.rb
@@ -17,19 +17,19 @@ class LDAP
end
require 'socket'
-require 'net/ber'
-require 'net/ldap/pdu'
-require 'net/ldap/filter'
-require 'net/ldap/dataset'
-require 'net/ldap/password'
-require 'net/ldap/entry'
-require 'net/ldap/instrumentation'
-require 'net/ldap/connection'
-require 'net/ldap/version'
-require 'net/ldap/error'
-require 'net/ldap/auth_adapter'
-require 'net/ldap/auth_adapter/simple'
-require 'net/ldap/auth_adapter/sasl'
+require_relative 'ber'
+require_relative 'ldap/pdu'
+require_relative 'ldap/filter'
+require_relative 'ldap/dataset'
+require_relative 'ldap/password'
+require_relative 'ldap/entry'
+require_relative 'ldap/instrumentation'
+require_relative 'ldap/connection'
+require_relative 'ldap/version'
+require_relative 'ldap/error'
+require_relative 'ldap/auth_adapter'
+require_relative 'ldap/auth_adapter/simple'
+require_relative 'ldap/auth_adapter/sasl'
Net::LDAP::AuthAdapter.register([:simple, :anon, :anonymous], Net::LDAP::AuthAdapter::Simple)
Net::LDAP::AuthAdapter.register(:sasl, Net::LDAP::AuthAdapter::Sasl)
@@ -79,6 +79,14 @@ class LDAP
#
# p ldap.get_operation_result
#
+# === Setting connect timeout
+#
+# By default, Net::LDAP uses TCP sockets with a connection timeout of 5 seconds.
+#
+# This value can be tweaked passing the :connect_timeout parameter.
+# i.e.
+# ldap = Net::LDAP.new ...,
+# :connect_timeout => 3
#
# == A Brief Introduction to LDAP
#
@@ -256,14 +264,14 @@ class Net::LDAP
SearchScope_BaseObject = 0
SearchScope_SingleLevel = 1
SearchScope_WholeSubtree = 2
- SearchScopes = [ SearchScope_BaseObject, SearchScope_SingleLevel,
- SearchScope_WholeSubtree ]
+ SearchScopes = [SearchScope_BaseObject, SearchScope_SingleLevel,
+ SearchScope_WholeSubtree]
DerefAliases_Never = 0
DerefAliases_Search = 1
DerefAliases_Find = 2
DerefAliases_Always = 3
- DerefAliasesArray = [ DerefAliases_Never, DerefAliases_Search, DerefAliases_Find, DerefAliases_Always ]
+ DerefAliasesArray = [DerefAliases_Never, DerefAliases_Search, DerefAliases_Find, DerefAliases_Always]
primitive = { 2 => :null } # UnbindRequest body
constructed = {
@@ -303,7 +311,7 @@ class Net::LDAP
0 => :array, # RFC-2251 Control and Filter-AND
1 => :array, # SearchFilter-OR
2 => :array, # SearchFilter-NOT
- 3 => :array, # Seach referral
+ 3 => :array, # Search referral
4 => :array, # unknown use in Microsoft Outlook
5 => :array, # SearchFilter-GE
6 => :array, # SearchFilter-LE
@@ -315,7 +323,14 @@ class Net::LDAP
:constructed => constructed,
}
+ universal = {
+ constructed: {
+ 107 => :string, # ExtendedResponse
+ },
+ }
+
AsnSyntax = Net::BER.compile_syntax(:application => application,
+ :universal => universal,
:context_specific => context_specific)
DefaultHost = "127.0.0.1"
@@ -324,7 +339,9 @@ class Net::LDAP
DefaultTreebase = "dc=com"
DefaultForceNoPage = false
- StartTlsOid = "1.3.6.1.4.1.1466.20037"
+ StartTlsOid = '1.3.6.1.4.1.1466.20037'
+ PasswdModifyOid = '1.3.6.1.4.1.4203.1.11.1'
+ WhoamiOid = '1.3.6.1.4.1.4203.1.11.3'
# https://tools.ietf.org/html/rfc4511#section-4.1.9
# https://tools.ietf.org/html/rfc4511#appendix-A
@@ -373,14 +390,14 @@ class Net::LDAP
ResultCodeCompareFalse,
ResultCodeCompareTrue,
ResultCodeReferral,
- ResultCodeSaslBindInProgress
+ ResultCodeSaslBindInProgress,
]
# nonstandard list of "successful" result codes for searches
ResultCodesSearchSuccess = [
ResultCodeSuccess,
ResultCodeTimeLimitExceeded,
- ResultCodeSizeLimitExceeded
+ ResultCodeSizeLimitExceeded,
]
# map of result code to human message
@@ -396,7 +413,7 @@ class Net::LDAP
ResultCodeStrongerAuthRequired => "Stronger Auth Needed",
ResultCodeReferral => "Referral",
ResultCodeAdminLimitExceeded => "Admin Limit Exceeded",
- ResultCodeUnavailableCriticalExtension => "Unavailable crtical extension",
+ ResultCodeUnavailableCriticalExtension => "Unavailable critical extension",
ResultCodeConfidentialityRequired => "Confidentiality Required",
ResultCodeSaslBindInProgress => "saslBindInProgress",
ResultCodeNoSuchAttribute => "No Such Attribute",
@@ -422,7 +439,7 @@ class Net::LDAP
ResultCodeEntryAlreadyExists => "Entry Already Exists",
ResultCodeObjectClassModsProhibited => "ObjectClass Modifications Prohibited",
ResultCodeAffectsMultipleDSAs => "Affects Multiple DSAs",
- ResultCodeOther => "Other"
+ ResultCodeOther => "Other",
}
module LDAPControls
@@ -460,20 +477,75 @@ def self.result2string(code) #:nodoc:
# specify a treebase. If you give a treebase value in any particular
# call to #search, that value will override any treebase value you give
# here.
- # * :encryption => specifies the encryption to be used in communicating
- # with the LDAP server. The value is either a Hash containing additional
- # parameters, or the Symbol :simple_tls, which is equivalent to
- # specifying the Hash {:method => :simple_tls}. There is a fairly large
- # range of potential values that may be given for this parameter. See
- # #encryption for details.
# * :force_no_page => Set to true to prevent paged results even if your
# server says it supports them. This is a fix for MS Active Directory
# * :instrumentation_service => An object responsible for instrumenting
# operations, compatible with ActiveSupport::Notifications' public API.
+ # * :connect_timeout => The TCP socket timeout (in seconds) to use when
+ # connecting to the LDAP server (default 5 seconds).
+ # * :encryption => specifies the encryption to be used in communicating
+ # with the LDAP server. The value must be a Hash containing additional
+ # parameters, which consists of two keys:
+ # method: - :simple_tls or :start_tls
+ # tls_options: - Hash of options for that method
+ # The :simple_tls encryption method encrypts all communications
+ # with the LDAP server. It completely establishes SSL/TLS encryption with
+ # the LDAP server before any LDAP-protocol data is exchanged. There is no
+ # plaintext negotiation and no special encryption-request controls are
+ # sent to the server. The :simple_tls option is the simplest, easiest
+ # way to encrypt communications between Net::LDAP and LDAP servers.
+ # If you get communications or protocol errors when using this option,
+ # check with your LDAP server administrator. Pay particular attention
+ # to the TCP port you are connecting to. It's impossible for an LDAP
+ # server to support plaintext LDAP communications and simple TLS
+ # connections on the same port. The standard TCP port for unencrypted
+ # LDAP connections is 389, but the standard port for simple-TLS
+ # encrypted connections is 636. Be sure you are using the correct port.
+ # The :start_tls like the :simple_tls encryption method also encrypts all
+ # communcations with the LDAP server. With the exception that it operates
+ # over the standard TCP port.
+ #
+ # To validate the LDAP server's certificate (a security must if you're
+ # talking over the public internet), you need to set :tls_options
+ # something like this...
+ #
+ # Net::LDAP.new(
+ # # ... set host, bind dn, etc ...
+ # encryption: {
+ # method: :simple_tls,
+ # tls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS,
+ # }
+ # )
+ #
+ # The above will use the operating system-provided store of CA
+ # certificates to validate your LDAP server's cert.
+ # If cert validation fails, it'll happen during the #bind
+ # whenever you first try to open a connection to the server.
+ # Those methods will throw Net::LDAP::ConnectionError with
+ # a message about certificate verify failing. If your
+ # LDAP server's certificate is signed by DigiCert, Comodo, etc.,
+ # you're probably good. If you've got a self-signed cert but it's
+ # been added to the host's OS-maintained CA store (e.g. on Debian
+ # add foobar.crt to /usr/local/share/ca-certificates/ and run
+ # `update-ca-certificates`), then the cert should pass validation.
+ # To ignore the OS's CA store, put your CA in a PEM-encoded file and...
+ #
+ # encryption: {
+ # method: :simple_tls,
+ # tls_options: { ca_file: '/path/to/my-little-ca.pem',
+ # ssl_version: 'TLSv1_1' },
+ # }
+ #
+ # As you might guess, the above example also fails the connection
+ # if the client can't negotiate TLS v1.1.
+ # tls_options is ultimately passed to OpenSSL::SSL::SSLContext#set_params
+ # For more details, see
+ # http://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html
#
# Instantiating a Net::LDAP object does not result in network
# traffic to the LDAP server. It simply stores the connection and binding
- # parameters in the object.
+ # parameters in the object. That's why Net::LDAP.new doesn't throw
+ # cert validation errors itself; #bind does instead.
def initialize(args = {})
@host = args[:host] || DefaultHost
@port = args[:port] || DefaultPort
@@ -482,7 +554,8 @@ def initialize(args = {})
@auth = args[:auth] || DefaultAuth
@base = args[:base] || DefaultTreebase
@force_no_page = args[:force_no_page] || DefaultForceNoPage
- encryption args[:encryption] # may be nil
+ @encryption = normalize_encryption(args[:encryption]) # may be nil
+ @connect_timeout = args[:connect_timeout]
if pr = @auth[:password] and pr.respond_to?(:call)
@auth[:password] = pr.call
@@ -533,7 +606,7 @@ def authenticate(username, password)
@auth = {
:method => :simple,
:username => username,
- :password => password
+ :password => password,
}
end
alias_method :auth, :authenticate
@@ -546,54 +619,12 @@ def authenticate(username, password)
# additional capabilities are added, more configuration values will be
# added here.
#
- # The :simple_tls encryption method encrypts all communications
- # with the LDAP server. It completely establishes SSL/TLS encryption with
- # the LDAP server before any LDAP-protocol data is exchanged. There is no
- # plaintext negotiation and no special encryption-request controls are
- # sent to the server. The :simple_tls option is the simplest, easiest
- # way to encrypt communications between Net::LDAP and LDAP servers.
- # It's intended for cases where you have an implicit level of trust in the
- # authenticity of the LDAP server. No validation of the LDAP server's SSL
- # certificate is performed. This means that :simple_tls will not produce
- # errors if the LDAP server's encryption certificate is not signed by a
- # well-known Certification Authority. If you get communications or
- # protocol errors when using this option, check with your LDAP server
- # administrator. Pay particular attention to the TCP port you are
- # connecting to. It's impossible for an LDAP server to support plaintext
- # LDAP communications and simple TLS connections on the same port.
- # The standard TCP port for unencrypted LDAP connections is 389, but the
- # standard port for simple-TLS encrypted connections is 636. Be sure you
- # are using the correct port.
- #
- # The :start_tls like the :simple_tls encryption method also encrypts all
- # communcations with the LDAP server. With the exception that it operates
- # over the standard TCP port.
- #
- # In order to verify certificates and enable other TLS options, the
- # :tls_options hash can be passed alongside :simple_tls or :start_tls.
- # This hash contains any options that can be passed to
- # OpenSSL::SSL::SSLContext#set_params(). The most common options passed
- # should be OpenSSL::SSL::SSLContext::DEFAULT_PARAMS, or the :ca_file option,
- # which contains a path to a Certificate Authority file (PEM-encoded).
- #
- # Example for a default setup without custom settings:
- # {
- # :method => :simple_tls,
- # :tls_options => OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
- # }
- #
- # Example for specifying a CA-File and only allowing TLSv1.1 connections:
+ # This method is deprecated.
#
- # {
- # :method => :start_tls,
- # :tls_options => { :ca_file => "/etc/cafile.pem", :ssl_version => "TLSv1_1" }
- # }
def encryption(args)
- case args
- when :simple_tls, :start_tls
- args = { :method => args, :tls_options => {} }
- end
- @encryption = args
+ warn "Deprecation warning: please give :encryption option as a Hash to Net::LDAP.new"
+ return if args.nil?
+ @encryption = normalize_encryption(args)
end
# #open takes the same parameters as #new. #open makes a network
@@ -637,8 +668,11 @@ def self.open(args)
#++
def get_operation_result
result = @result
- result = result.result if result.is_a?(Net::LDAP::PDU)
os = OpenStruct.new
+ if result.is_a?(Net::LDAP::PDU)
+ os.extended_response = result.extended_response
+ result = result.result
+ end
if result.is_a?(Hash)
# We might get a hash of LDAP response codes instead of a simple
# numeric code.
@@ -681,7 +715,7 @@ def open
begin
@open_connection = new_connection
payload[:connection] = @open_connection
- payload[:bind] = @open_connection.bind(@auth)
+ payload[:bind] = @result = @open_connection.bind(@auth)
yield self
ensure
@open_connection.close if @open_connection
@@ -750,10 +784,10 @@ def search(args = {})
instrument "search.net_ldap", args do |payload|
@result = use_connection(args) do |conn|
- conn.search(args) { |entry|
+ conn.search(args) do |entry|
result_set << entry if result_set
yield entry if block_given?
- }
+ end
end
if return_result_set
@@ -892,7 +926,7 @@ def bind(auth = @auth)
# end
def bind_as(args = {})
result = false
- open { |me|
+ open do |me|
rs = search args
if rs and rs.first and dn = rs.first.dn
password = args[:password]
@@ -900,7 +934,7 @@ def bind_as(args = {})
result = rs if bind(:method => :simple, :username => dn,
:password => password)
end
- }
+ end
result
end
@@ -1027,6 +1061,44 @@ def modify(args)
end
end
+ # Password Modify
+ #
+ # Change existing password:
+ #
+ # dn = 'uid=modify-password-user1,ou=People,dc=rubyldap,dc=com'
+ # auth = {
+ # method: :simple,
+ # username: dn,
+ # password: 'passworD1'
+ # }
+ # ldap.password_modify(dn: dn,
+ # auth: auth,
+ # old_password: 'passworD1',
+ # new_password: 'passworD2')
+ #
+ # Or get the LDAP server to generate a password for you:
+ #
+ # dn = 'uid=modify-password-user1,ou=People,dc=rubyldap,dc=com'
+ # auth = {
+ # method: :simple,
+ # username: dn,
+ # password: 'passworD1'
+ # }
+ # ldap.password_modify(dn: dn,
+ # auth: auth,
+ # old_password: 'passworD1')
+ #
+ # ldap.get_operation_result.extended_response[0][0] #=> 'VtcgGf/G'
+ #
+ def password_modify(args)
+ instrument "modify_password.net_ldap", args do |payload|
+ @result = use_connection(args) do |conn|
+ conn.password_modify(args)
+ end
+ @result.success?
+ end
+ end
+
# Add a value to an attribute. Takes the full DN of the entry to modify,
# the name (Symbol or String) of the attribute, and the value (String or
# Array). If the attribute does not exist (and there are no schema
@@ -1113,14 +1185,39 @@ def delete(args)
# entries. This method sends an extra control code to tell the LDAP server
# to do a tree delete. ('1.2.840.113556.1.4.805')
#
+ # If the LDAP server does not support the DELETE_TREE control code, subordinate
+ # entries are deleted recursively instead.
+ #
# Returns True or False to indicate whether the delete succeeded. Extended
# status information is available by calling #get_operation_result.
#
# dn = "mail=deleteme@example.com, ou=people, dc=example, dc=com"
# ldap.delete_tree :dn => dn
def delete_tree(args)
- delete(args.merge(:control_codes => [[Net::LDAP::LDAPControls::DELETE_TREE, true]]))
+ if search_root_dse[:supportedcontrol].include? Net::LDAP::LDAPControls::DELETE_TREE
+ delete(args.merge(:control_codes => [[Net::LDAP::LDAPControls::DELETE_TREE, true]]))
+ else
+ recursive_delete(args)
+ end
+ end
+
+ # Return the authorization identity of the client that issues the
+ # ldapwhoami request. The method does not support any arguments.
+ #
+ # Returns True or False to indicate whether the request was successfull.
+ # The result is available in the extended status information when calling
+ # #get_operation_result.
+ #
+ # ldap.ldapwhoami
+ # puts ldap.get_operation_result.extended_response
+ def ldapwhoami(args = {})
+ instrument "ldapwhoami.net_ldap", args do |payload|
+ @result = use_connection(args, &:ldapwhoami)
+ @result.success? ? @result.extended_response : nil
+ end
end
+ alias_method :whoami, :ldapwhoami
+
# This method is experimental and subject to change. Return the rootDSE
# record from the LDAP server as a Net::LDAP::Entry, or an empty Entry if
# the server doesn't return the record.
@@ -1145,7 +1242,7 @@ def search_root_dse
:supportedExtension,
:supportedFeatures,
:supportedLdapVersion,
- :supportedSASLMechanisms
+ :supportedSASLMechanisms,
])
(rs and rs.first) or Net::LDAP::Entry.new
end
@@ -1178,10 +1275,10 @@ def search_subschema_entry
rs = search(:ignore_server_caps => true, :base => "",
:scope => SearchScope_BaseObject,
:attributes => [:subschemaSubentry])
- return Net::LDAP::Entry.new unless (rs and rs.first)
+ return Net::LDAP::Entry.new unless rs and rs.first
subschema_name = rs.first.subschemasubentry
- return Net::LDAP::Entry.new unless (subschema_name and subschema_name.first)
+ return Net::LDAP::Entry.new unless subschema_name and subschema_name.first
rs = search(:ignore_server_caps => true, :base => subschema_name.first,
:scope => SearchScope_BaseObject,
@@ -1212,6 +1309,11 @@ def inspect
inspected
end
+ # Internal: Set @open_connection for testing
+ def connection=(connection)
+ @open_connection = connection
+ end
+
private
# Yields an open connection if there is one, otherwise establishes a new
@@ -1224,11 +1326,9 @@ def use_connection(args)
else
begin
conn = new_connection
- if (result = conn.bind(args[:auth] || @auth)).result_code == Net::LDAP::ResultCodeSuccess
- yield conn
- else
- return result
- end
+ result = conn.bind(args[:auth] || @auth)
+ return result unless result.result_code == Net::LDAP::ResultCodeSuccess
+ yield conn
ensure
conn.close if conn
end
@@ -1237,11 +1337,50 @@ def use_connection(args)
# Establish a new connection to the LDAP server
def new_connection
- Net::LDAP::Connection.new \
+ connection = Net::LDAP::Connection.new \
:host => @host,
:port => @port,
:hosts => @hosts,
:encryption => @encryption,
- :instrumentation_service => @instrumentation_service
+ :instrumentation_service => @instrumentation_service,
+ :connect_timeout => @connect_timeout
+
+ # Force connect to see if there's a connection error
+ connection.socket
+ connection
+ rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT => e
+ @result = {
+ :resultCode => 52,
+ :errorMessage => ResultStrings[ResultCodeUnavailable],
+ }
+ raise e
end
+
+ # Normalize encryption parameter the constructor accepts, expands a few
+ # convenience symbols into recognizable hashes
+ def normalize_encryption(args)
+ return if args.nil?
+ return args if args.is_a? Hash
+
+ case method = args.to_sym
+ when :simple_tls, :start_tls
+ { :method => method, :tls_options => {} }
+ end
+ end
+
+ # Recursively delete a dn and it's subordinate children.
+ # This is useful when a server does not support the DELETE_TREE control code.
+ def recursive_delete(args)
+ raise EmptyDNError unless args.is_a?(Hash) && args.key?(:dn)
+ # Delete Children
+ search(base: args[:dn], scope: Net::LDAP::SearchScope_SingleLevel) do |entry|
+ recursive_delete(dn: entry.dn)
+ end
+ # Delete Self
+ unless delete(dn: args[:dn])
+ raise Net::LDAP::Error, get_operation_result[:error_message].to_s
+ end
+ true
+ end
+
end # class LDAP
diff --git a/lib/net/ldap/auth_adapter/gss_spnego.rb b/lib/net/ldap/auth_adapter/gss_spnego.rb
index e251f038..b4c3e519 100644
--- a/lib/net/ldap/auth_adapter/gss_spnego.rb
+++ b/lib/net/ldap/auth_adapter/gss_spnego.rb
@@ -1,5 +1,5 @@
-require 'net/ldap/auth_adapter'
-require 'net/ldap/auth_adapter/sasl'
+require_relative '../auth_adapter'
+require_relative 'sasl'
module Net
class LDAP
@@ -20,19 +20,20 @@ def bind(auth)
require 'ntlm'
user, psw = [auth[:username] || auth[:dn], auth[:password]]
- raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
+ raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless user && psw
- nego = proc { |challenge|
+ nego = proc do |challenge|
t2_msg = NTLM::Message.parse(challenge)
t3_msg = t2_msg.response({ :user => user, :password => psw },
{ :ntlmv2 => true })
t3_msg.serialize
- }
+ end
- Net::LDAP::AuthAdapter::Sasl.new(@connection).
- bind(:method => :sasl, :mechanism => "GSS-SPNEGO",
- :initial_credential => NTLM::Message::Type1.new.serialize,
- :challenge_response => nego)
+ Net::LDAP::AuthAdapter::Sasl.new(@connection).bind \
+ :method => :sasl,
+ :mechanism => "GSS-SPNEGO",
+ :initial_credential => NTLM::Message::Type1.new.serialize,
+ :challenge_response => nego
end
end
end
diff --git a/lib/net/ldap/auth_adapter/sasl.rb b/lib/net/ldap/auth_adapter/sasl.rb
index fa7315b5..bfebfc94 100644
--- a/lib/net/ldap/auth_adapter/sasl.rb
+++ b/lib/net/ldap/auth_adapter/sasl.rb
@@ -1,9 +1,11 @@
-require 'net/ldap/auth_adapter'
+require_relative '../auth_adapter'
module Net
class LDAP
class AuthAdapter
class Sasl < Net::LDAP::AuthAdapter
+ MAX_SASL_CHALLENGES = 10
+
#--
# Required parameters: :mechanism, :initial_credential and
# :challenge_response
@@ -28,12 +30,12 @@ class Sasl < Net::LDAP::AuthAdapter
def bind(auth)
mech, cred, chall = auth[:mechanism], auth[:initial_credential],
auth[:challenge_response]
- raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (mech && cred && chall)
+ raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless mech && cred && chall
message_id = @connection.next_msgid
n = 0
- loop {
+ loop do
sasl = [mech.to_ber, cred.to_ber].to_ber_contextspecific(3)
request = [
Net::LDAP::Connection::LdapVersion.to_ber, "".to_ber, sasl
@@ -47,10 +49,10 @@ def bind(auth)
end
return pdu unless pdu.result_code == Net::LDAP::ResultCodeSaslBindInProgress
- raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges)
+ raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MAX_SASL_CHALLENGES)
cred = chall.call(pdu.result_server_sasl_creds)
- }
+ end
raise Net::LDAP::SASLChallengeOverflowError, "why are we here?"
end
diff --git a/lib/net/ldap/auth_adapter/simple.rb b/lib/net/ldap/auth_adapter/simple.rb
index d01b57ae..8a753ea6 100644
--- a/lib/net/ldap/auth_adapter/simple.rb
+++ b/lib/net/ldap/auth_adapter/simple.rb
@@ -1,4 +1,4 @@
-require 'net/ldap/auth_adapter'
+require_relative '../auth_adapter'
module Net
class LDAP
@@ -11,7 +11,7 @@ def bind(auth)
["", ""]
end
- raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
+ raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless user && psw
message_id = @connection.next_msgid
request = [
diff --git a/lib/net/ldap/connection.rb b/lib/net/ldap/connection.rb
index 4e3f6dd0..f1a70b18 100644
--- a/lib/net/ldap/connection.rb
+++ b/lib/net/ldap/connection.rb
@@ -3,38 +3,63 @@
class Net::LDAP::Connection #:nodoc:
include Net::LDAP::Instrumentation
+ # Seconds before failing for socket connect timeout
+ DefaultConnectTimeout = 5
+
LdapVersion = 3
- MaxSaslChallenges = 10
- def initialize(server)
+ # Initialize a connection to an LDAP server
+ #
+ # :server
+ # :hosts Array of tuples specifying host, port
+ # :host host
+ # :port port
+ # :socket prepared socket
+ #
+ def initialize(server = {})
+ @server = server
@instrumentation_service = server[:instrumentation_service]
- if server[:socket]
- prepare_socket(server)
- else
- server[:hosts] = [[server[:host], server[:port]]] if server[:hosts].nil?
- open_connection(server)
- end
+ # Allows tests to parameterize what socket class to use
+ @socket_class = server.fetch(:socket_class, DefaultSocket)
yield self if block_given?
end
- def prepare_socket(server)
+ def socket_class=(socket_class)
+ @socket_class = socket_class
+ end
+
+ def prepare_socket(server, timeout=nil, hostname='127.0.0.1')
socket = server[:socket]
encryption = server[:encryption]
@conn = socket
- setup_encryption encryption if encryption
+ setup_encryption(encryption, timeout, hostname) if encryption
end
def open_connection(server)
hosts = server[:hosts]
encryption = server[:encryption]
+ timeout = server[:connect_timeout] || DefaultConnectTimeout
+ socket_opts = {
+ connect_timeout: timeout,
+ }
+
errors = []
hosts.each do |host, port|
begin
- prepare_socket(server.merge(socket: TCPSocket.new(host, port)))
+ prepare_socket(server.merge(socket: @socket_class.new(host, port, socket_opts)), timeout, host)
+ if encryption
+ if encryption[:tls_options] &&
+ encryption[:tls_options][:verify_mode] &&
+ encryption[:tls_options][:verify_mode] == OpenSSL::SSL::VERIFY_NONE
+ warn "not verifying SSL hostname of LDAPS server '#{host}:#{port}'"
+ else
+ @conn.post_connection_check(host)
+ end
+ end
return
rescue Net::LDAP::Error, SocketError, SystemCallError,
OpenSSL::SSL::SSLError => e
@@ -49,7 +74,8 @@ def open_connection(server)
module GetbyteForSSLSocket
def getbyte
- getc.ord
+ c = getc
+ c && c.ord
end
end
@@ -60,7 +86,7 @@ def close
end
end
- def self.wrap_with_ssl(io, tls_options = {})
+ def self.wrap_with_ssl(io, tls_options = {}, timeout=nil, hostname=nil)
raise Net::LDAP::NoOpenSSLError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL
ctx = OpenSSL::SSL::SSLContext.new
@@ -70,7 +96,23 @@ def self.wrap_with_ssl(io, tls_options = {})
ctx.set_params(tls_options) unless tls_options.empty?
conn = OpenSSL::SSL::SSLSocket.new(io, ctx)
- conn.connect
+ conn.hostname = hostname
+
+ begin
+ if timeout
+ conn.connect_nonblock
+ else
+ conn.connect
+ end
+ rescue IO::WaitReadable
+ raise Errno::ETIMEDOUT, "OpenSSL connection read timeout" unless
+ IO.select([conn], nil, nil, timeout)
+ retry
+ rescue IO::WaitWritable
+ raise Errno::ETIMEDOUT, "OpenSSL connection write timeout" unless
+ IO.select(nil, [conn], nil, timeout)
+ retry
+ end
# Doesn't work:
# conn.sync_close = true
@@ -107,17 +149,17 @@ def self.wrap_with_ssl(io, tls_options = {})
# communications, as with simple_tls. Thanks for Kouhei Sutou for
# generously contributing the :start_tls path.
#++
- def setup_encryption(args)
+ def setup_encryption(args, timeout=nil, hostname=nil)
args[:tls_options] ||= {}
case args[:method]
when :simple_tls
- @conn = self.class.wrap_with_ssl(@conn, args[:tls_options])
+ @conn = self.class.wrap_with_ssl(@conn, args[:tls_options], timeout, hostname)
# additional branches requiring server validation and peer certs, etc.
# go here.
when :start_tls
message_id = next_msgid
request = [
- Net::LDAP::StartTlsOid.to_ber_contextspecific(0)
+ Net::LDAP::StartTlsOid.to_ber_contextspecific(0),
].to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest)
write(request, nil, message_id)
@@ -127,11 +169,9 @@ def setup_encryption(args)
raise Net::LDAP::NoStartTLSResultError, "no start_tls result"
end
- if pdu.result_code.zero?
- @conn = self.class.wrap_with_ssl(@conn, args[:tls_options])
- else
- raise Net::LDAP::StartTlSError, "start_tls failed: #{pdu.result_code}"
- end
+ raise Net::LDAP::StartTLSError,
+ "start_tls failed: #{pdu.result_code}" unless pdu.result_code.zero?
+ @conn = self.class.wrap_with_ssl(@conn, args[:tls_options], timeout, hostname)
else
raise Net::LDAP::EncMethodUnsupportedError, "unsupported encryption method #{args[:method]}"
end
@@ -143,7 +183,7 @@ def setup_encryption(args)
# have to call it, but perhaps it will come in handy someday.
#++
def close
- return if @conn.nil?
+ return if !defined?(@conn) || @conn.nil?
@conn.close
@conn = nil
end
@@ -161,12 +201,10 @@ def queued_read(message_id)
# read messages until we have a match for the given message_id
while pdu = read
- if pdu.message_id == message_id
- return pdu
- else
- message_queue[pdu.message_id].push pdu
- next
- end
+ return pdu if pdu.message_id == message_id
+
+ message_queue[pdu.message_id].push pdu
+ next
end
pdu
@@ -195,7 +233,7 @@ def message_queue
def read(syntax = Net::LDAP::AsnSyntax)
ber_object =
instrument "read.net_ldap_connection", :syntax => syntax do |payload|
- @conn.read_ber(syntax) do |id, content_length|
+ socket.read_ber(syntax) do |id, content_length|
payload[:object_type_id] = id
payload[:content_length] = content_length
end
@@ -225,7 +263,7 @@ def read(syntax = Net::LDAP::AsnSyntax)
def write(request, controls = nil, message_id = next_msgid)
instrument "write.net_ldap_connection" do |payload|
packet = [message_id.to_ber, request, controls].compact.to_ber_sequence
- payload[:content_length] = @conn.write(packet)
+ payload[:content_length] = socket.write(packet)
end
end
private :write
@@ -264,10 +302,10 @@ def encode_sort_controls(sort_definitions)
control[2] = (control[2] == true).to_ber
control.to_ber_sequence
end
- sort_control = [
+ [
Net::LDAP::LDAPControls::SORT_REQUEST.to_ber,
false.to_ber,
- sort_control_values.to_ber_sequence.to_s.to_ber
+ sort_control_values.to_ber_sequence.to_s.to_ber,
].to_ber_sequence
end
@@ -364,12 +402,11 @@ def search(args = nil)
# should collect this into a private helper to clarify the structure
query_limit = 0
if size > 0
- if paged
- query_limit = (((size - n_results) < 126) ? (size -
- n_results) : 0)
- else
- query_limit = size
- end
+ query_limit = if paged
+ (((size - n_results) < 126) ? (size - n_results) : 0)
+ else
+ size
+ end
end
request = [
@@ -380,23 +417,29 @@ def search(args = nil)
time.to_ber,
attrs_only.to_ber,
filter.to_ber,
- ber_attrs.to_ber_sequence
+ ber_attrs.to_ber_sequence,
].to_ber_appsequence(Net::LDAP::PDU::SearchRequest)
# rfc2696_cookie sometimes contains binary data from Microsoft Active Directory
# this breaks when calling to_ber. (Can't force binary data to UTF-8)
# we have to disable paging (even though server supports it) to get around this...
+ user_controls = args.fetch(:controls, [])
controls = []
controls <<
[
Net::LDAP::LDAPControls::PAGED_RESULTS.to_ber,
# Criticality MUST be false to interoperate with normal LDAPs.
false.to_ber,
- rfc2696_cookie.map{ |v| v.to_ber}.to_ber_sequence.to_s.to_ber
+ rfc2696_cookie.map(&:to_ber).to_ber_sequence.to_s.to_ber,
].to_ber_sequence if paged
controls << ber_sort if ber_sort
- controls = controls.empty? ? nil : controls.to_ber_contextspecific(0)
+ if controls.empty? && user_controls.empty?
+ controls = nil
+ else
+ controls += user_controls
+ controls = controls.to_ber_contextspecific(0)
+ end
write(request, controls, message_id)
@@ -432,6 +475,10 @@ def search(args = nil)
end
end
+ if result_pdu.nil?
+ raise Net::LDAP::ResponseMissingOrInvalidError, "response missing"
+ end
+
# count number of pages of results
payload[:page_count] ||= 0
payload[:page_count] += 1
@@ -487,20 +534,20 @@ def search(args = nil)
MODIFY_OPERATIONS = { #:nodoc:
:add => 0,
:delete => 1,
- :replace => 2
+ :replace => 2,
}
def self.modify_ops(operations)
ops = []
if operations
- operations.each { |op, attrib, values|
+ operations.each do |op, attrib, values|
# TODO, fix the following line, which gives a bogus error if the
# opcode is invalid.
op_ber = MODIFY_OPERATIONS[op.to_sym].to_ber_enumerated
- values = [ values ].flatten.map { |v| v.to_ber if v }.to_ber_set
- values = [ attrib.to_s.to_ber, values ].to_ber_sequence
- ops << [ op_ber, values ].to_ber
- }
+ values = [values].flatten.map { |v| v.to_ber if v }.to_ber_set
+ values = [attrib.to_s.to_ber, values].to_ber_sequence
+ ops << [op_ber, values].to_ber
+ end
end
ops
end
@@ -519,10 +566,15 @@ def modify(args)
message_id = next_msgid
request = [
modify_dn.to_ber,
- ops.to_ber_sequence
+ ops.to_ber_sequence,
].to_ber_appsequence(Net::LDAP::PDU::ModifyRequest)
- write(request, nil, message_id)
+ controls = args.fetch(:controls, nil)
+ unless controls.nil?
+ controls = controls.to_ber_contextspecific(0)
+ end
+
+ write(request, controls, message_id)
pdu = queued_read(message_id)
if !pdu || pdu.app_tag != Net::LDAP::PDU::ModifyResponse
@@ -532,6 +584,51 @@ def modify(args)
pdu
end
+ ##
+ # Password Modify
+ #
+ # http://tools.ietf.org/html/rfc3062
+ #
+ # passwdModifyOID OBJECT IDENTIFIER ::= 1.3.6.1.4.1.4203.1.11.1
+ #
+ # PasswdModifyRequestValue ::= SEQUENCE {
+ # userIdentity [0] OCTET STRING OPTIONAL
+ # oldPasswd [1] OCTET STRING OPTIONAL
+ # newPasswd [2] OCTET STRING OPTIONAL }
+ #
+ # PasswdModifyResponseValue ::= SEQUENCE {
+ # genPasswd [0] OCTET STRING OPTIONAL }
+ #
+ # Encoded request:
+ #
+ # 00\x02\x01\x02w+\x80\x171.3.6.1.4.1.4203.1.11.1\x81\x100\x0E\x81\x05old\x82\x05new
+ #
+ def password_modify(args)
+ dn = args[:dn]
+ raise ArgumentError, 'DN is required' if !dn || dn.empty?
+
+ ext_seq = [Net::LDAP::PasswdModifyOid.to_ber_contextspecific(0)]
+
+ pwd_seq = []
+ pwd_seq << dn.to_ber(0x80)
+ pwd_seq << args[:old_password].to_ber(0x81) unless args[:old_password].nil?
+ pwd_seq << args[:new_password].to_ber(0x82) unless args[:new_password].nil?
+ ext_seq << pwd_seq.to_ber_sequence.to_ber(0x81)
+
+ request = ext_seq.to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest)
+
+ message_id = next_msgid
+
+ write(request, nil, message_id)
+ pdu = queued_read(message_id)
+
+ if !pdu || pdu.app_tag != Net::LDAP::PDU::ExtendedResponse
+ raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid"
+ end
+
+ pdu
+ end
+
#--
# TODO: need to support a time limit, in case the server fails to respond.
# Unlike other operation-methods in this class, we return a result hash
@@ -542,14 +639,19 @@ def modify(args)
def add(args)
add_dn = args[:dn] or raise Net::LDAP::EmptyDNError, "Unable to add empty DN"
add_attrs = []
- a = args[:attributes] and a.each { |k, v|
- add_attrs << [ k.to_s.to_ber, Array(v).map { |m| m.to_ber}.to_ber_set ].to_ber_sequence
- }
+ a = args[:attributes] and a.each do |k, v|
+ add_attrs << [k.to_s.to_ber, Array(v).map(&:to_ber).to_ber_set].to_ber_sequence
+ end
message_id = next_msgid
request = [add_dn.to_ber, add_attrs.to_ber_sequence].to_ber_appsequence(Net::LDAP::PDU::AddRequest)
- write(request, nil, message_id)
+ controls = args.fetch(:controls, nil)
+ unless controls.nil?
+ controls = controls.to_ber_contextspecific(0)
+ end
+
+ write(request, controls, message_id)
pdu = queued_read(message_id)
if !pdu || pdu.app_tag != Net::LDAP::PDU::AddResponse
@@ -600,4 +702,49 @@ def delete(args)
pdu
end
+
+ def ldapwhoami
+ ext_seq = [Net::LDAP::WhoamiOid.to_ber_contextspecific(0)]
+ request = ext_seq.to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest)
+
+ message_id = next_msgid
+
+ write(request, nil, message_id)
+ pdu = queued_read(message_id)
+
+ if !pdu || pdu.app_tag != Net::LDAP::PDU::ExtendedResponse
+ raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid"
+ end
+
+ pdu
+ end
+
+ # Internal: Returns a Socket like object used internally to communicate with
+ # LDAP server.
+ #
+ # Typically a TCPSocket, but can be a OpenSSL::SSL::SSLSocket
+ def socket
+ return @conn if defined?(@conn) && !@conn.nil?
+
+ # First refactoring uses the existing methods open_connection and
+ # prepare_socket to set @conn. Next cleanup would centralize connection
+ # handling here.
+ if @server[:socket]
+ prepare_socket(@server)
+ else
+ @server[:hosts] = [[@server[:host], @server[:port]]] if @server[:hosts].nil?
+ open_connection(@server)
+ end
+
+ @conn
+ end
+
+ private
+
+ # Wrap around Socket.tcp to normalize with other Socket initializers
+ class DefaultSocket
+ def self.new(host, port, socket_opts = {})
+ Socket.tcp(host, port, **socket_opts)
+ end
+ end
end # class Connection
diff --git a/lib/net/ldap/dataset.rb b/lib/net/ldap/dataset.rb
index 54fc1a07..bc225e89 100644
--- a/lib/net/ldap/dataset.rb
+++ b/lib/net/ldap/dataset.rb
@@ -29,7 +29,7 @@ def to_ldif
keys.sort.each do |dn|
ary << "dn: #{dn}"
- attributes = self[dn].keys.map { |attr| attr.to_s }.sort
+ attributes = self[dn].keys.map(&:to_s).sort
attributes.each do |attr|
self[dn][attr.to_sym].each do |value|
if attr == "userpassword" or value_is_binary?(value)
@@ -103,7 +103,7 @@ def gets
# with the conversion of
def from_entry(entry)
dataset = Net::LDAP::Dataset.new
- hash = { }
+ hash = {}
entry.each_attribute do |attribute, value|
next if attribute == :dn
hash[attribute] = value
@@ -141,7 +141,7 @@ def read_ldif(io)
# $' is the dn-value
# Avoid the Base64 class because not all Ruby versions have it.
dn = ($1 == ":") ? $'.unpack('m').shift : $'
- ds[dn] = Hash.new { |k,v| k[v] = [] }
+ ds[dn] = Hash.new { |k, v| k[v] = [] }
yield :dn, dn if block_given?
elsif line.empty?
dn = nil
@@ -164,5 +164,3 @@ def read_ldif(io)
end
end
end
-
-require 'net/ldap/entry' unless defined? Net::LDAP::Entry
diff --git a/lib/net/ldap/dn.rb b/lib/net/ldap/dn.rb
index 3037eefd..9098cdb9 100644
--- a/lib/net/ldap/dn.rb
+++ b/lib/net/ldap/dn.rb
@@ -57,19 +57,19 @@ def each_pair
state = :key_oid
key << char
when ' ' then state = :key
- else raise "DN badly formed"
+ else raise Net::LDAP::InvalidDNError, "DN badly formed"
end
when :key_normal then
case char
when '=' then state = :value
when 'a'..'z', 'A'..'Z', '0'..'9', '-', ' ' then key << char
- else raise "DN badly formed"
+ else raise Net::LDAP::InvalidDNError, "DN badly formed"
end
when :key_oid then
case char
when '=' then state = :value
when '0'..'9', '.', ' ' then key << char
- else raise "DN badly formed"
+ else raise Net::LDAP::InvalidDNError, "DN badly formed"
end
when :value then
case char
@@ -81,7 +81,7 @@ def each_pair
value << char
when ',' then
state = :key
- yield key.string.strip, value.string.rstrip
+ yield key.string.strip, value.string
key = StringIO.new
value = StringIO.new;
else
@@ -93,7 +93,7 @@ def each_pair
when '\\' then state = :value_normal_escape
when ',' then
state = :key
- yield key.string.strip, value.string.rstrip
+ yield key.string.strip, value.string
key = StringIO.new
value = StringIO.new;
else value << char
@@ -110,7 +110,7 @@ def each_pair
when '0'..'9', 'a'..'f', 'A'..'F' then
state = :value_normal
value << "#{hex_buffer}#{char}".to_i(16).chr
- else raise "DN badly formed"
+ else raise Net::LDAP::InvalidDNError, "DN badly formed"
end
when :value_quoted then
case char
@@ -132,7 +132,7 @@ def each_pair
when '0'..'9', 'a'..'f', 'A'..'F' then
state = :value_quoted
value << "#{hex_buffer}#{char}".to_i(16).chr
- else raise "DN badly formed"
+ else raise Net::LDAP::InvalidDNError, "DN badly formed"
end
when :value_hexstring then
case char
@@ -142,38 +142,37 @@ def each_pair
when ' ' then state = :value_end
when ',' then
state = :key
- yield key.string.strip, value.string.rstrip
+ yield key.string.strip, value.string
key = StringIO.new
value = StringIO.new;
- else raise "DN badly formed"
+ else raise Net::LDAP::InvalidDNError, "DN badly formed"
end
when :value_hexstring_hex then
case char
when '0'..'9', 'a'..'f', 'A'..'F' then
state = :value_hexstring
value << char
- else raise "DN badly formed"
+ else raise Net::LDAP::InvalidDNError, "DN badly formed"
end
when :value_end then
case char
when ' ' then state = :value_end
when ',' then
state = :key
- yield key.string.strip, value.string.rstrip
+ yield key.string.strip, value.string
key = StringIO.new
value = StringIO.new;
- else raise "DN badly formed"
+ else raise Net::LDAP::InvalidDNError, "DN badly formed"
end
- else raise "Fell out of state machine"
+ else raise Net::LDAP::InvalidDNError, "Fell out of state machine"
end
end
# Last pair
- if [:value, :value_normal, :value_hexstring, :value_end].include? state
- yield key.string.strip, value.string.rstrip
- else
- raise "DN badly formed"
- end
+ raise Net::LDAP::InvalidDNError, "DN badly formed" unless
+ [:value, :value_normal, :value_hexstring, :value_end].include? state
+
+ yield key.string.strip, value.string
end
##
@@ -193,27 +192,19 @@ def to_s
# http://tools.ietf.org/html/rfc2253 section 2.4 lists these exceptions
# for dn values. All of the following must be escaped in any normal string
# using a single backslash ('\') as escape.
- ESCAPES = {
- ',' => ',',
- '+' => '+',
- '"' => '"',
- '\\' => '\\',
- '<' => '<',
- '>' => '>',
- ';' => ';',
- }
+ ESCAPES = %w[, + " \\ < > ;]
- # Compiled character class regexp using the keys from the above hash, and
+ # Compiled character class regexp using the values from the above list, and
# checking for a space or # at the start, or space at the end, of the
# string.
ESCAPE_RE = Regexp.new("(^ |^#| $|[" +
- ESCAPES.keys.map { |e| Regexp.escape(e) }.join +
+ ESCAPES.map { |e| Regexp.escape(e) }.join +
"])")
##
# Escape a string for use in a DN value
def self.escape(string)
- string.gsub(ESCAPE_RE) { |char| "\\" + ESCAPES[char] }
+ string.gsub(ESCAPE_RE) { |char| "\\" + char }
end
##
diff --git a/lib/net/ldap/entry.rb b/lib/net/ldap/entry.rb
index c2615268..18668892 100644
--- a/lib/net/ldap/entry.rb
+++ b/lib/net/ldap/entry.rb
@@ -133,6 +133,13 @@ def attribute_names
@myhash.keys
end
+ ##
+ # Creates a duplicate of the internal Hash containing the attributes
+ # of the entry.
+ def to_h
+ @myhash.dup
+ end
+
##
# Accesses each of the attributes present in the Entry.
#
@@ -140,11 +147,10 @@ def attribute_names
# arguments to the block: a Symbol giving the name of the attribute, and a
# (possibly empty) \Array of data values.
def each # :yields: attribute-name, data-values-array
- if block_given?
- attribute_names.each {|a|
- attr_name,values = a,self[a]
- yield attr_name, values
- }
+ return unless block_given?
+ attribute_names.each do|a|
+ attr_name, values = a, self[a]
+ yield attr_name, values
end
end
alias_method :each_attribute, :each
@@ -188,6 +194,8 @@ def setter?(sym)
sym.to_s[-1] == ?=
end
private :setter?
-end # class Entry
-require 'net/ldap/dataset' unless defined? Net::LDAP::Dataset
+ def ==(other)
+ other.instance_of?(self.class) && @myhash == other.to_h
+ end
+end # class Entry
diff --git a/lib/net/ldap/error.rb b/lib/net/ldap/error.rb
index 9f157195..49a338d6 100644
--- a/lib/net/ldap/error.rb
+++ b/lib/net/ldap/error.rb
@@ -1,37 +1,13 @@
class Net::LDAP
- class LdapError < StandardError
- def message
- "Deprecation warning: Net::LDAP::LdapError is no longer used. Use Net::LDAP::Error or rescue one of it's subclasses. \n" + super
- end
- end
-
class Error < StandardError; end
class AlreadyOpenedError < Error; end
class SocketError < Error; end
- class ConnectionRefusedError < Error;
- def initialize(*args)
- warn_deprecation_message
- super
- end
-
- def message
- warn_deprecation_message
- super
- end
-
- private
- def warn_deprecation_message
- warn "Deprecation warning: Net::LDAP::ConnectionRefused will be deprecated. Use Errno::ECONNREFUSED instead."
- end
- end
class ConnectionError < Error
def self.new(errors)
error = errors.first.first
if errors.size == 1
- if error.kind_of? Errno::ECONNREFUSED
- return Net::LDAP::ConnectionRefusedError.new(error.message)
- end
+ return error if error.is_a? Errno::ECONNREFUSED
return Net::LDAP::Error.new(error.message)
end
@@ -59,6 +35,7 @@ class SearchScopeInvalidError < Error; end
class ResponseTypeInvalidError < Error; end
class ResponseMissingOrInvalidError < Error; end
class EmptyDNError < Error; end
+ class InvalidDNError < Error; end
class HashTypeUnsupportedError < Error; end
class OperatorError < Error; end
class SubstringFilterError < Error; end
diff --git a/lib/net/ldap/filter.rb b/lib/net/ldap/filter.rb
index aad84f83..dc0d0ab3 100644
--- a/lib/net/ldap/filter.rb
+++ b/lib/net/ldap/filter.rb
@@ -23,7 +23,7 @@
class Net::LDAP::Filter
##
# Known filter types.
- FilterTypes = [ :ne, :eq, :ge, :le, :and, :or, :not, :ex, :bineq ]
+ FilterTypes = [:ne, :eq, :ge, :le, :and, :or, :not, :ex, :bineq]
def initialize(op, left, right) #:nodoc:
unless FilterTypes.include?(op)
@@ -287,7 +287,7 @@ def parse_ber(ber)
when 0xa4 # context-specific constructed 4, "substring"
str = ""
final = false
- ber.last.each { |b|
+ ber.last.each do |b|
case b.ber_identifier
when 0x80 # context-specific primitive 0, SubstringFilter "initial"
raise Net::LDAP::SubstringFilterError, "Unrecognized substring filter; bad initial value." if str.length > 0
@@ -298,7 +298,7 @@ def parse_ber(ber)
str += "*#{escape(b)}"
final = true
end
- }
+ end
str += "*" unless final
eq(ber.first.to_s, str)
when 0xa5 # context-specific constructed 5, "greaterOrEqual"
@@ -490,7 +490,7 @@ def to_ber
when :eq
if @right == "*" # presence test
@left.to_s.to_ber_contextspecific(7)
- elsif @right =~ /[*]/ # substring
+ elsif @right.to_s =~ /[*]/ # substring
# Parsing substrings is a little tricky. We use String#split to
# break a string into substrings delimited by the * (star)
# character. But we also need to know whether there is a star at the
@@ -550,10 +550,10 @@ def to_ber
[self.class.eq(@left, @right).to_ber].to_ber_contextspecific(2)
when :and
ary = [@left.coalesce(:and), @right.coalesce(:and)].flatten
- ary.map {|a| a.to_ber}.to_ber_contextspecific(0)
+ ary.map(&:to_ber).to_ber_contextspecific(0)
when :or
ary = [@left.coalesce(:or), @right.coalesce(:or)].flatten
- ary.map {|a| a.to_ber}.to_ber_contextspecific(1)
+ ary.map(&:to_ber).to_ber_contextspecific(1)
when :not
[@left.to_ber].to_ber_contextspecific(2)
end
@@ -645,8 +645,15 @@ def match(entry)
##
# Converts escaped characters (e.g., "\\28") to unescaped characters
+ # @note slawson20170317: Don't attempt to unescape 16 byte binary data which we assume are objectGUIDs
+ # The binary form of 5936AE79-664F-44EA-BCCB-5C39399514C6 triggers a BINARY -> UTF-8 conversion error
def unescape(right)
- right.to_s.gsub(/\\([a-fA-F\d]{2})/) { [$1.hex].pack("U") }
+ right = right.to_s
+ if right.length == 16 && right.encoding == Encoding::BINARY
+ right
+ else
+ right.to_s.gsub(/\\([a-fA-F\d]{2})/) { [$1.hex].pack("U") }
+ end
end
private :unescape
@@ -748,7 +755,7 @@ def parse_paren_expression(scanner)
# This parses a given expression inside of parentheses.
def parse_filter_branch(scanner)
scanner.scan(/\s*/)
- if token = scanner.scan(/[-\w:.]*[\w]/)
+ if token = scanner.scan(/[-\w:.;]*[\w]/)
scanner.scan(/\s*/)
if op = scanner.scan(/<=|>=|!=|:=|=/)
scanner.scan(/\s*/)
diff --git a/lib/net/ldap/instrumentation.rb b/lib/net/ldap/instrumentation.rb
index 143e03b3..d5cc6bf7 100644
--- a/lib/net/ldap/instrumentation.rb
+++ b/lib/net/ldap/instrumentation.rb
@@ -12,8 +12,8 @@ module Net::LDAP::Instrumentation
def instrument(event, payload = {})
payload = (payload || {}).dup
if instrumentation_service
- instrumentation_service.instrument(event, payload) do |payload|
- payload[:result] = yield(payload) if block_given?
+ instrumentation_service.instrument(event, payload) do |instr_payload|
+ instr_payload[:result] = yield(instr_payload) if block_given?
end
else
yield(payload) if block_given?
diff --git a/lib/net/ldap/password.rb b/lib/net/ldap/password.rb
index 28406f03..4a6a1ae7 100644
--- a/lib/net/ldap/password.rb
+++ b/lib/net/ldap/password.rb
@@ -1,5 +1,6 @@
# -*- ruby encoding: utf-8 -*-
require 'digest/sha1'
+require 'digest/sha2'
require 'digest/md5'
require 'base64'
require 'securerandom'
@@ -19,20 +20,25 @@ class << self
# * Should we provide sha1 as a synonym for sha1? I vote no because then
# should you also provide ssha1 for symmetry?
#
- attribute_value = ""
def generate(type, str)
case type
when :md5
- attribute_value = '{MD5}' + Base64.encode64(Digest::MD5.digest(str)).chomp!
+ '{MD5}' + Base64.strict_encode64(Digest::MD5.digest(str))
when :sha
- attribute_value = '{SHA}' + Base64.encode64(Digest::SHA1.digest(str)).chomp!
+ '{SHA}' + Base64.strict_encode64(Digest::SHA1.digest(str))
when :ssha
salt = SecureRandom.random_bytes(16)
- attribute_value = '{SSHA}' + Base64.encode64(Digest::SHA1.digest(str + salt) + salt).chomp!
+ digest = Digest::SHA1.new
+ digest << str << salt
+ '{SSHA}' + Base64.strict_encode64(digest.digest + salt)
+ when :ssha256
+ salt = SecureRandom.random_bytes(16)
+ digest = Digest::SHA256.new
+ digest << str << salt
+ '{SSHA256}' + Base64.strict_encode64(digest.digest + salt)
else
raise Net::LDAP::HashTypeUnsupportedError, "Unsupported password-hash type (#{type})"
end
- return attribute_value
end
end
end
diff --git a/lib/net/ldap/pdu.rb b/lib/net/ldap/pdu.rb
index f749f669..83a609b7 100644
--- a/lib/net/ldap/pdu.rb
+++ b/lib/net/ldap/pdu.rb
@@ -74,6 +74,7 @@ class Error < RuntimeError; end
attr_reader :search_referrals
attr_reader :search_parameters
attr_reader :bind_parameters
+ attr_reader :extended_response
##
# Returns RFC-2251 Controls if any.
@@ -120,9 +121,9 @@ def initialize(ber_object)
when UnbindRequest
parse_unbind_request(ber_object[1])
when ExtendedResponse
- parse_ldap_result(ber_object[1])
+ parse_extended_response(ber_object[1])
else
- raise LdapPduError.new("unknown pdu-type: #{@app_tag}")
+ raise Error.new("unknown pdu-type: #{@app_tag}")
end
parse_controls(ber_object[2]) if ber_object[2]
@@ -174,12 +175,35 @@ def parse_ldap_result(sequence)
@ldap_result = {
:resultCode => sequence[0],
:matchedDN => sequence[1],
- :errorMessage => sequence[2]
+ :errorMessage => sequence[2],
}
parse_search_referral(sequence[3]) if @ldap_result[:resultCode] == Net::LDAP::ResultCodeReferral
end
private :parse_ldap_result
+ ##
+ # Parse an extended response
+ #
+ # http://www.ietf.org/rfc/rfc2251.txt
+ #
+ # Each Extended operation consists of an Extended request and an
+ # Extended response.
+ #
+ # ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
+ # requestName [0] LDAPOID,
+ # requestValue [1] OCTET STRING OPTIONAL }
+
+ def parse_extended_response(sequence)
+ sequence.length.between?(3, 5) or raise Net::LDAP::PDU::Error, "Invalid LDAP result length."
+ @ldap_result = {
+ :resultCode => sequence[0],
+ :matchedDN => sequence[1],
+ :errorMessage => sequence[2],
+ }
+ @extended_response = sequence.length == 3 ? nil : sequence.last
+ end
+ private :parse_extended_response
+
##
# A Bind Response may have an additional field, ID [7], serverSaslCreds,
# per RFC 2251 pgh 4.2.3.
diff --git a/lib/net/ldap/version.rb b/lib/net/ldap/version.rb
index 219b4156..2caeaa5f 100644
--- a/lib/net/ldap/version.rb
+++ b/lib/net/ldap/version.rb
@@ -1,5 +1,5 @@
module Net
class LDAP
- VERSION = "0.12.0"
+ VERSION = "0.20.0"
end
end
diff --git a/lib/net/snmp.rb b/lib/net/snmp.rb
index 501df851..f89fe267 100644
--- a/lib/net/snmp.rb
+++ b/lib/net/snmp.rb
@@ -1,5 +1,5 @@
# -*- ruby encoding: utf-8 -*-
-require 'net/ldap/version'
+require_relative 'ldap/version'
# :stopdoc:
module Net
@@ -12,7 +12,7 @@ class SNMP
2 => :integer, # Gauge32 or Unsigned32, (RFC2578 sec 2)
3 => :integer # TimeTicks32, (RFC2578 sec 2)
},
- :constructed => {}
+ :constructed => {},
},
:context_specific => {
:primitive => {},
@@ -20,8 +20,8 @@ class SNMP
0 => :array, # GetRequest PDU (RFC1157 pgh 4.1.2)
1 => :array, # GetNextRequest PDU (RFC1157 pgh 4.1.3)
2 => :array # GetResponse PDU (RFC1157 pgh 4.1.4)
- }
- }
+ },
+ },
})
# SNMP 32-bit counter.
@@ -70,7 +70,7 @@ class Error < StandardError; end
:get_next_request,
:get_response,
:set_request,
- :trap
+ :trap,
]
ErrorStatusCodes = { # Per RFC1157, pgh 4.1.1
0 => "noError",
@@ -78,7 +78,7 @@ class Error < StandardError; end
2 => "noSuchName",
3 => "badValue",
4 => "readOnly",
- 5 => "genErr"
+ 5 => "genErr",
}
class << self
@@ -148,7 +148,7 @@ def parse_get_request data
# data[2] is error_index, always zero.
send :error_status=, 0
send :error_index=, 0
- data[3].each do |n,v|
+ data[3].each do |n, v|
# A variable-binding, of which there may be several,
# consists of an OID and a BER null.
# We're ignoring the null, we might want to verify it instead.
@@ -166,7 +166,7 @@ def parse_get_response data
send :request_id=, data[0].to_i
send :error_status=, data[1].to_i
send :error_index=, data[2].to_i
- data[3].each do |n,v|
+ data[3].each do |n, v|
# A variable-binding, of which there may be several,
# consists of an OID and a BER null.
# We're ignoring the null, we might want to verify it instead.
@@ -177,7 +177,7 @@ def parse_get_response data
def version= ver
- unless [0,2].include?(ver)
+ unless [0, 2].include?(ver)
raise Error.new("unknown snmp-version: #{ver}")
end
@version = ver
@@ -191,7 +191,7 @@ def pdu_type= t
end
def error_status= es
- unless ErrorStatusCodes.has_key?(es)
+ unless ErrorStatusCodes.key?(es)
raise Error.new("unknown error-status: #{es}")
end
@error_status = es
@@ -227,10 +227,10 @@ def pdu_to_ber_string
error_status.to_ber,
error_index.to_ber,
[
- @variables.map {|n,v|
+ @variables.map do|n, v|
[n.to_ber_oid, Net::BER::BerIdentifiedNull.new.to_ber].to_ber_sequence
- }
- ].to_ber_sequence
+ end,
+ ].to_ber_sequence,
].to_ber_contextspecific(0)
when :get_next_request
[
@@ -238,10 +238,10 @@ def pdu_to_ber_string
error_status.to_ber,
error_index.to_ber,
[
- @variables.map {|n,v|
+ @variables.map do|n, v|
[n.to_ber_oid, Net::BER::BerIdentifiedNull.new.to_ber].to_ber_sequence
- }
- ].to_ber_sequence
+ end,
+ ].to_ber_sequence,
].to_ber_contextspecific(1)
when :get_response
[
@@ -249,10 +249,10 @@ def pdu_to_ber_string
error_status.to_ber,
error_index.to_ber,
[
- @variables.map {|n,v|
+ @variables.map do|n, v|
[n.to_ber_oid, v.to_ber].to_ber_sequence
- }
- ].to_ber_sequence
+ end,
+ ].to_ber_sequence,
].to_ber_contextspecific(2)
else
raise Error.new( "unknown pdu-type: #{pdu_type}" )
diff --git a/net-ldap.gemspec b/net-ldap.gemspec
index 97c12906..077077f2 100644
--- a/net-ldap.gemspec
+++ b/net-ldap.gemspec
@@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
-require 'net/ldap/version'
+require_relative 'lib/net/ldap/version'
Gem::Specification.new do |s|
s.name = %q{net-ldap}
@@ -21,16 +21,14 @@ Our roadmap for Net::LDAP 1.0 is to gain full client compliance with
the most recent LDAP RFCs (4510-4519, plutions of 4520-4532).}
s.email = ["blackhedd@rubyforge.org", "gemiel@gmail.com", "rory.ocon@gmail.com", "kaspar.schiess@absurd.li", "austin@rubyforge.org"]
s.extra_rdoc_files = ["Contributors.rdoc", "Hacking.rdoc", "History.rdoc", "License.rdoc", "README.rdoc"]
- s.files = `git ls-files`.split $/
+ s.files = Dir["*.rdoc", "lib/**/*"]
s.test_files = s.files.grep(%r{^test})
s.homepage = %q{http://github.com/ruby-ldap/ruby-net-ldap}
s.rdoc_options = ["--main", "README.rdoc"]
s.require_paths = ["lib"]
- s.required_ruby_version = ">= 1.9.3"
+ s.required_ruby_version = ">= 3.0.0"
s.summary = %q{Net::LDAP for Ruby (also called net-ldap) implements client access for the Lightweight Directory Access Protocol (LDAP), an IETF standard protocol for accessing distributed directory services}
- s.add_development_dependency("flexmock", "~> 1.3")
- s.add_development_dependency("rake", "~> 10.0")
- s.add_development_dependency("rubocop", "~> 0.28.0")
- s.add_development_dependency("test-unit")
+ s.add_dependency("base64")
+ s.add_dependency("ostruct")
end
diff --git a/script/changelog b/script/changelog
index cda2ad83..f42a0bd4 100755
--- a/script/changelog
+++ b/script/changelog
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
# Usage: script/changelog [-r ] [-b ] [-h ]
#
# repo: BASE string of GitHub REPOsitory url. e.g. "user_or_org/REPOsitory". Defaults to git remote url.
diff --git a/script/install-openldap b/script/install-openldap
deleted file mode 100755
index b9efac98..00000000
--- a/script/install-openldap
+++ /dev/null
@@ -1,112 +0,0 @@
-#!/usr/bin/env sh
-set -e
-set -x
-
-BASE_PATH="$( cd `dirname $0`/../test/fixtures/openldap && pwd )"
-SEED_PATH="$( cd `dirname $0`/../test/fixtures && pwd )"
-
-dpkg -s slapd time ldap-utils gnutls-bin ssl-cert > /dev/null ||\
- DEBIAN_FRONTEND=noninteractive apt-get update -y --force-yes && \
- DEBIAN_FRONTEND=noninteractive apt-get install -y --force-yes slapd time ldap-utils gnutls-bin ssl-cert
-
-/etc/init.d/slapd stop
-
-TMPDIR=$(mktemp -d)
-cd $TMPDIR
-
-# Delete data and reconfigure.
-cp -v /var/lib/ldap/DB_CONFIG ./DB_CONFIG
-rm -rf /etc/ldap/slapd.d/*
-rm -rf /var/lib/ldap/*
-cp -v ./DB_CONFIG /var/lib/ldap/DB_CONFIG
-slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/slapd.conf.ldif
-# Load memberof and ref-int overlays and configure them.
-slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/memberof.ldif
-# Load retcode overlay and configure
-slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/retcode.ldif
-
-# Add base domain.
-slapadd -F /etc/ldap/slapd.d < /etc/ssl/private/cakey.pem"
-
-sh -c "cat > /etc/ssl/ca.info < /etc/ssl/ldap01.info < CA_FILE)
- @ldap.encryption(method: :start_tls, tls_options: tls_options)
- assert @ldap.bind(method: :simple, username: "uid=user1,ou=People,dc=rubyldap,dc=com", password: "passworD1"), @ldap.get_operation_result.inspect
+ omit "We need to update our CA cert"
+ @ldap.host = INTEGRATION_HOSTNAME
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: TLS_OPTS.merge(ca_file: CA_FILE),
+ )
+ assert @ldap.bind(BIND_CREDS),
+ @ldap.get_operation_result.inspect
+ end
+
+ def test_bind_tls_with_bad_hostname_verify_none_no_ca_passes
+ @ldap.host = INTEGRATION_HOSTNAME
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE },
+ )
+ assert @ldap.bind(BIND_CREDS),
+ @ldap.get_operation_result.inspect
+ end
+
+ def test_bind_tls_with_bad_hostname_verify_none_no_ca_opt_merge_passes
+ @ldap.host = 'cert.mismatch.example.org'
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: TLS_OPTS.merge(verify_mode: OpenSSL::SSL::VERIFY_NONE),
+ )
+ assert @ldap.bind(BIND_CREDS),
+ @ldap.get_operation_result.inspect
end
- def test_bind_tls_with_verify_none
- tls_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.merge(:verify_mode => OpenSSL::SSL::VERIFY_NONE)
- @ldap.encryption(method: :start_tls, tls_options: tls_options)
- assert @ldap.bind(method: :simple, username: "uid=user1,ou=People,dc=rubyldap,dc=com", password: "passworD1"), @ldap.get_operation_result.inspect
+ def test_bind_tls_with_bad_hostname_verify_peer_ca_fails
+ omit "We need to update our CA cert"
+ @ldap.host = 'cert.mismatch.example.org'
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: { verify_mode: OpenSSL::SSL::VERIFY_PEER,
+ ca_file: CA_FILE },
+ )
+ error = assert_raise Net::LDAP::Error,
+ Errno::ECONNREFUSED do
+ @ldap.bind BIND_CREDS
+ end
+ assert_equal(
+ "hostname \"#{@ldap.host}\" does not match the server certificate",
+ error.message,
+ )
+ end
+
+ def test_bind_tls_with_bad_hostname_ca_default_opt_merge_fails
+ omit "We need to update our CA cert"
+ @ldap.host = 'cert.mismatch.example.org'
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: TLS_OPTS.merge(ca_file: CA_FILE),
+ )
+ error = assert_raise Net::LDAP::Error,
+ Errno::ECONNREFUSED do
+ @ldap.bind BIND_CREDS
+ end
+ assert_equal(
+ "hostname \"#{@ldap.host}\" does not match the server certificate",
+ error.message,
+ )
+ end
+
+ def test_bind_tls_with_bad_hostname_ca_no_opt_merge_fails
+ omit "We need to update our CA cert"
+ @ldap.host = 'cert.mismatch.example.org'
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: { ca_file: CA_FILE },
+ )
+ error = assert_raise Net::LDAP::Error,
+ Errno::ECONNREFUSED do
+ @ldap.bind BIND_CREDS
+ end
+ assert_equal(
+ "hostname \"#{@ldap.host}\" does not match the server certificate",
+ error.message,
+ )
+ end
+
+ def test_bind_tls_with_valid_hostname_default_opts_passes
+ omit "We need to update our CA cert"
+ @ldap.host = INTEGRATION_HOSTNAME
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: TLS_OPTS.merge(verify_mode: OpenSSL::SSL::VERIFY_PEER,
+ ca_file: CA_FILE),
+ )
+ assert @ldap.bind(BIND_CREDS),
+ @ldap.get_operation_result.inspect
+ end
+
+ def test_bind_tls_with_valid_hostname_just_verify_peer_ca_passes
+ omit "We need to update our CA cert"
+ @ldap.host = INTEGRATION_HOSTNAME
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: { verify_mode: OpenSSL::SSL::VERIFY_PEER,
+ ca_file: CA_FILE },
+ )
+ assert @ldap.bind(BIND_CREDS),
+ @ldap.get_operation_result.inspect
+ end
+
+ def test_bind_tls_with_bogus_hostname_system_ca_fails
+ @ldap.host = 'cert.mismatch.example.org'
+ @ldap.encryption(method: :start_tls, tls_options: {})
+ error = assert_raise Net::LDAP::Error,
+ Errno::ECONNREFUSED do
+ @ldap.bind BIND_CREDS
+ end
+ assert_equal(
+ "hostname \"#{@ldap.host}\" does not match the server certificate",
+ error.message,
+ )
+ end
+
+ def test_bind_tls_with_multiple_hosts
+ omit "We need to update our CA cert"
+ @ldap.host = nil
+ @ldap.hosts = [[INTEGRATION_HOSTNAME, 389], [INTEGRATION_HOSTNAME, 389]]
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: TLS_OPTS.merge(verify_mode: OpenSSL::SSL::VERIFY_PEER,
+ ca_file: CA_FILE),
+ )
+ assert @ldap.bind(BIND_CREDS),
+ @ldap.get_operation_result.inspect
+ end
+
+ def test_bind_tls_with_multiple_bogus_hosts
+ # omit "We need to update our CA cert"
+ @ldap.host = nil
+ @ldap.hosts = [['cert.mismatch.example.org', 389], ['bogus.example.com', 389]]
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: TLS_OPTS.merge(verify_mode: OpenSSL::SSL::VERIFY_PEER,
+ ca_file: CA_FILE),
+ )
+ error = assert_raise Net::LDAP::Error,
+ Net::LDAP::ConnectionError do
+ @ldap.bind BIND_CREDS
+ end
+ assert_equal("Unable to connect to any given server: ",
+ error.message.split("\n").shift)
+ end
+
+ def test_bind_tls_with_multiple_bogus_hosts_no_verification
+ omit "We need to update our CA cert"
+ @ldap.host = nil
+ @ldap.hosts = [['cert.mismatch.example.org', 389], ['bogus.example.com', 389]]
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: TLS_OPTS.merge(verify_mode: OpenSSL::SSL::VERIFY_NONE),
+ )
+ assert @ldap.bind(BIND_CREDS),
+ @ldap.get_operation_result.inspect
+ end
+
+ def test_bind_tls_with_multiple_bogus_hosts_ca_check_only_fails
+ @ldap.host = nil
+ @ldap.hosts = [['cert.mismatch.example.org', 389], ['bogus.example.com', 389]]
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: { ca_file: CA_FILE },
+ )
+ error = assert_raise Net::LDAP::Error,
+ Net::LDAP::ConnectionError do
+ @ldap.bind BIND_CREDS
+ end
+ assert_equal("Unable to connect to any given server: ",
+ error.message.split("\n").shift)
+ end
+
+ # This test is CI-only because we can't add the fixture CA
+ # to the system CA store on people's dev boxes.
+ def test_bind_tls_valid_hostname_system_ca_on_travis_passes
+ omit "not sure how to install custom CA cert in travis"
+ omit_unless ENV['TRAVIS'] == 'true'
+
+ @ldap.host = INTEGRATION_HOSTNAME
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: { verify_mode: OpenSSL::SSL::VERIFY_PEER },
+ )
+ assert @ldap.bind(BIND_CREDS),
+ @ldap.get_operation_result.inspect
end
end
diff --git a/test/integration/test_delete.rb b/test/integration/test_delete.rb
index 355df7b9..20e3414c 100644
--- a/test/integration/test_delete.rb
+++ b/test/integration/test_delete.rb
@@ -3,21 +3,42 @@
class TestDeleteIntegration < LDAPIntegrationTestCase
def setup
super
- @ldap.authenticate "cn=admin,dc=rubyldap,dc=com", "passworD1"
-
- @dn = "uid=delete-user1,ou=People,dc=rubyldap,dc=com"
+ @dn = "uid=delete-user1,ou=People,dc=example,dc=org"
attrs = {
objectclass: %w(top inetOrgPerson organizationalPerson person),
uid: "delete-user1",
cn: "delete-user1",
sn: "delete-user1",
- mail: "delete-user1@rubyldap.com"
+ mail: "delete-user1@rubyldap.com",
}
unless @ldap.search(base: @dn, scope: Net::LDAP::SearchScope_BaseObject)
assert @ldap.add(dn: @dn, attributes: attrs), @ldap.get_operation_result.inspect
end
assert @ldap.search(base: @dn, scope: Net::LDAP::SearchScope_BaseObject)
+
+ @parent_dn = "uid=parent,ou=People,dc=example,dc=org"
+ parent_attrs = {
+ objectclass: %w(top inetOrgPerson organizationalPerson person),
+ uid: "parent",
+ cn: "parent",
+ sn: "parent",
+ mail: "parent@rubyldap.com",
+ }
+ @child_dn = "uid=child,uid=parent,ou=People,dc=example,dc=org"
+ child_attrs = {
+ objectclass: %w(top inetOrgPerson organizationalPerson person),
+ uid: "child",
+ cn: "child",
+ sn: "child",
+ mail: "child@rubyldap.com",
+ }
+ unless @ldap.search(base: @parent_dn, scope: Net::LDAP::SearchScope_BaseObject)
+ assert @ldap.add(dn: @parent_dn, attributes: parent_attrs), @ldap.get_operation_result.inspect
+ assert @ldap.add(dn: @child_dn, attributes: child_attrs), @ldap.get_operation_result.inspect
+ end
+ assert @ldap.search(base: @parent_dn, scope: Net::LDAP::SearchScope_BaseObject)
+ assert @ldap.search(base: @child_dn, scope: Net::LDAP::SearchScope_BaseObject)
end
def test_delete
@@ -28,4 +49,14 @@ def test_delete
assert_equal Net::LDAP::ResultCodeNoSuchObject, result.code
assert_equal Net::LDAP::ResultStrings[Net::LDAP::ResultCodeNoSuchObject], result.message
end
+
+ def test_delete_tree
+ assert @ldap.delete_tree(dn: @parent_dn), @ldap.get_operation_result.inspect
+ refute @ldap.search(base: @parent_dn, scope: Net::LDAP::SearchScope_BaseObject)
+ refute @ldap.search(base: @child_dn, scope: Net::LDAP::SearchScope_BaseObject)
+
+ result = @ldap.get_operation_result
+ assert_equal Net::LDAP::ResultCodeNoSuchObject, result.code
+ assert_equal Net::LDAP::ResultStrings[Net::LDAP::ResultCodeNoSuchObject], result.message
+ end
end
diff --git a/test/integration/test_open.rb b/test/integration/test_open.rb
index 36724f5d..9ce36d72 100644
--- a/test/integration/test_open.rb
+++ b/test/integration/test_open.rb
@@ -4,8 +4,8 @@ class TestBindIntegration < LDAPIntegrationTestCase
def test_binds_without_open
events = @service.subscribe "bind.net_ldap_connection"
- @ldap.search(filter: "uid=user1", base: "ou=People,dc=rubyldap,dc=com", ignore_server_caps: true)
- @ldap.search(filter: "uid=user1", base: "ou=People,dc=rubyldap,dc=com", ignore_server_caps: true)
+ @ldap.search(filter: "uid=user1", base: "ou=People,dc=example,dc=org", ignore_server_caps: true)
+ @ldap.search(filter: "uid=user1", base: "ou=People,dc=example,dc=org", ignore_server_caps: true)
assert_equal 2, events.size
end
@@ -14,8 +14,8 @@ def test_binds_with_open
events = @service.subscribe "bind.net_ldap_connection"
@ldap.open do
- @ldap.search(filter: "uid=user1", base: "ou=People,dc=rubyldap,dc=com", ignore_server_caps: true)
- @ldap.search(filter: "uid=user1", base: "ou=People,dc=rubyldap,dc=com", ignore_server_caps: true)
+ @ldap.search(filter: "uid=user1", base: "ou=People,dc=example,dc=org", ignore_server_caps: true)
+ @ldap.search(filter: "uid=user1", base: "ou=People,dc=example,dc=org", ignore_server_caps: true)
end
assert_equal 1, events.size
@@ -29,9 +29,9 @@ def test_nested_search_without_open
entries = []
nested_entry = nil
- @ldap.search(filter: "(|(uid=user1)(uid=user2))", base: "ou=People,dc=rubyldap,dc=com") do |entry|
+ @ldap.search(filter: "(|(uid=user1)(uid=user2))", base: "ou=People,dc=example,dc=org") do |entry|
entries << entry.uid.first
- nested_entry ||= @ldap.search(filter: "uid=user3", base: "ou=People,dc=rubyldap,dc=com").first
+ nested_entry ||= @ldap.search(filter: "uid=user3", base: "ou=People,dc=example,dc=org").first
end
assert_equal "user3", nested_entry.uid.first
@@ -43,9 +43,9 @@ def test_nested_search_with_open
nested_entry = nil
@ldap.open do
- @ldap.search(filter: "(|(uid=user1)(uid=user2))", base: "ou=People,dc=rubyldap,dc=com") do |entry|
+ @ldap.search(filter: "(|(uid=user1)(uid=user2))", base: "ou=People,dc=example,dc=org") do |entry|
entries << entry.uid.first
- nested_entry ||= @ldap.search(filter: "uid=user3", base: "ou=People,dc=rubyldap,dc=com").first
+ nested_entry ||= @ldap.search(filter: "uid=user3", base: "ou=People,dc=example,dc=org").first
end
end
@@ -57,20 +57,19 @@ def test_nested_add_with_open
entries = []
nested_entry = nil
- dn = "uid=nested-open-added-user1,ou=People,dc=rubyldap,dc=com"
+ dn = "uid=nested-open-added-user1,ou=People,dc=example,dc=org"
attrs = {
objectclass: %w(top inetOrgPerson organizationalPerson person),
uid: "nested-open-added-user1",
cn: "nested-open-added-user1",
sn: "nested-open-added-user1",
- mail: "nested-open-added-user1@rubyldap.com"
+ mail: "nested-open-added-user1@rubyldap.com",
}
- @ldap.authenticate "cn=admin,dc=rubyldap,dc=com", "passworD1"
@ldap.delete dn: dn
@ldap.open do
- @ldap.search(filter: "(|(uid=user1)(uid=user2))", base: "ou=People,dc=rubyldap,dc=com") do |entry|
+ @ldap.search(filter: "(|(uid=user1)(uid=user2))", base: "ou=People,dc=example,dc=org") do |entry|
entries << entry.uid.first
nested_entry ||= begin
diff --git a/test/integration/test_password_modify.rb b/test/integration/test_password_modify.rb
new file mode 100644
index 00000000..e7d8d670
--- /dev/null
+++ b/test/integration/test_password_modify.rb
@@ -0,0 +1,111 @@
+require_relative '../test_helper'
+
+class TestPasswordModifyIntegration < LDAPIntegrationTestCase
+ # see: https://www.rfc-editor.org/rfc/rfc3062#section-2
+ PASSWORD_MODIFY_SYNTAX = Net::BER.compile_syntax(
+ application: {},
+ universal: {},
+ context_specific: { primitive: { 0 => :string } },
+ )
+
+ def setup
+ super
+ @admin_account = { dn: 'cn=admin,dc=example,dc=org', password: 'admin', method: :simple }
+ @ldap.authenticate @admin_account[:dn], @admin_account[:password]
+
+ @dn = 'uid=modify-password-user1,ou=People,dc=example,dc=org'
+
+ attrs = {
+ objectclass: %w(top inetOrgPerson organizationalPerson person),
+ uid: 'modify-password-user1',
+ cn: 'modify-password-user1',
+ sn: 'modify-password-user1',
+ mail: 'modify-password-user1@rubyldap.com',
+ userPassword: 'admin',
+ }
+ unless @ldap.search(base: @dn, scope: Net::LDAP::SearchScope_BaseObject)
+ assert @ldap.add(dn: @dn, attributes: attrs), @ldap.get_operation_result.inspect
+ end
+ assert @ldap.search(base: @dn, scope: Net::LDAP::SearchScope_BaseObject)
+
+ @auth = {
+ method: :simple,
+ username: @dn,
+ password: 'admin',
+ }
+ end
+
+ def test_password_modify
+ assert @ldap.password_modify(dn: @dn,
+ auth: @auth,
+ old_password: 'admin',
+ new_password: 'passworD2')
+
+ assert @ldap.get_operation_result.extended_response.nil?,
+ 'Should not have generated a new password'
+
+ refute @ldap.bind(username: @dn, password: 'admin', method: :simple),
+ 'Old password should no longer be valid'
+
+ assert @ldap.bind(username: @dn, password: 'passworD2', method: :simple),
+ 'New password should be valid'
+ end
+
+ def test_password_modify_generate
+ assert @ldap.password_modify(dn: @dn,
+ auth: @auth,
+ old_password: 'admin')
+
+ passwd_modify_response_value = @ldap.get_operation_result.extended_response
+ seq = Net::BER::BerIdentifiedArray.new
+ sio = StringIO.new(passwd_modify_response_value)
+ until (e = sio.read_ber(PASSWORD_MODIFY_SYNTAX)).nil?
+ seq << e
+ end
+ generated_password = seq[0][0]
+
+ assert generated_password, 'Should have generated a password'
+
+ refute @ldap.bind(username: @dn, password: 'admin', method: :simple),
+ 'Old password should no longer be valid'
+
+ assert @ldap.bind(username: @dn, password: generated_password, method: :simple),
+ 'New password should be valid'
+ end
+
+ def test_password_modify_generate_no_old_password
+ assert @ldap.password_modify(dn: @dn,
+ auth: @auth)
+
+ passwd_modify_response_value = @ldap.get_operation_result.extended_response
+ seq = Net::BER::BerIdentifiedArray.new
+ sio = StringIO.new(passwd_modify_response_value)
+ until (e = sio.read_ber(PASSWORD_MODIFY_SYNTAX)).nil?
+ seq << e
+ end
+ generated_password = seq[0][0]
+ assert generated_password, 'Should have generated a password'
+
+ refute @ldap.bind(username: @dn, password: 'admin', method: :simple),
+ 'Old password should no longer be valid'
+
+ assert @ldap.bind(username: @dn, password: generated_password, method: :simple),
+ 'New password should be valid'
+ end
+
+ def test_password_modify_overwrite_old_password
+ assert @ldap.password_modify(dn: @dn,
+ auth: @admin_account,
+ new_password: 'passworD3')
+
+ refute @ldap.bind(username: @dn, password: 'admin', method: :simple),
+ 'Old password should no longer be valid'
+
+ assert @ldap.bind(username: @dn, password: 'passworD3', method: :simple),
+ 'New password should be valid'
+ end
+
+ def teardown
+ @ldap.delete dn: @dn
+ end
+end
diff --git a/test/integration/test_return_codes.rb b/test/integration/test_return_codes.rb
index 0e381a0a..30057a2a 100644
--- a/test/integration/test_return_codes.rb
+++ b/test/integration/test_return_codes.rb
@@ -4,8 +4,16 @@
# See: section 12.12 http://www.openldap.org/doc/admin24/overlays.html
class TestReturnCodeIntegration < LDAPIntegrationTestCase
+ def test_open_error
+ @ldap.authenticate "cn=fake", "creds"
+ @ldap.open do
+ result = @ldap.get_operation_result
+ assert_equal Net::LDAP::ResultCodeInvalidCredentials, result.code
+ end
+ end
+
def test_operations_error
- refute @ldap.search(filter: "cn=operationsError", base: "ou=Retcodes,dc=rubyldap,dc=com")
+ refute @ldap.search(filter: "cn=operationsError", base: "ou=Retcodes,dc=example,dc=org")
assert result = @ldap.get_operation_result
assert_equal Net::LDAP::ResultCodeOperationsError, result.code
@@ -13,7 +21,7 @@ def test_operations_error
end
def test_protocol_error
- refute @ldap.search(filter: "cn=protocolError", base: "ou=Retcodes,dc=rubyldap,dc=com")
+ refute @ldap.search(filter: "cn=protocolError", base: "ou=Retcodes,dc=example,dc=org")
assert result = @ldap.get_operation_result
assert_equal Net::LDAP::ResultCodeProtocolError, result.code
@@ -21,7 +29,7 @@ def test_protocol_error
end
def test_time_limit_exceeded
- assert @ldap.search(filter: "cn=timeLimitExceeded", base: "ou=Retcodes,dc=rubyldap,dc=com")
+ assert @ldap.search(filter: "cn=timeLimitExceeded", base: "ou=Retcodes,dc=example,dc=org")
assert result = @ldap.get_operation_result
assert_equal Net::LDAP::ResultCodeTimeLimitExceeded, result.code
@@ -29,7 +37,7 @@ def test_time_limit_exceeded
end
def test_size_limit_exceeded
- assert @ldap.search(filter: "cn=sizeLimitExceeded", base: "ou=Retcodes,dc=rubyldap,dc=com")
+ assert @ldap.search(filter: "cn=sizeLimitExceeded", base: "ou=Retcodes,dc=example,dc=org")
assert result = @ldap.get_operation_result
assert_equal Net::LDAP::ResultCodeSizeLimitExceeded, result.code
diff --git a/test/integration/test_search.rb b/test/integration/test_search.rb
index b56052ce..1f562c22 100644
--- a/test/integration/test_search.rb
+++ b/test/integration/test_search.rb
@@ -4,7 +4,7 @@ class TestSearchIntegration < LDAPIntegrationTestCase
def test_search
entries = []
- result = @ldap.search(base: "dc=rubyldap,dc=com") do |entry|
+ result = @ldap.search(base: "dc=example,dc=org") do |entry|
assert_kind_of Net::LDAP::Entry, entry
entries << entry
end
@@ -16,7 +16,7 @@ def test_search
def test_search_without_result
entries = []
- result = @ldap.search(base: "dc=rubyldap,dc=com", return_result: false) do |entry|
+ result = @ldap.search(base: "dc=example,dc=org", return_result: false) do |entry|
assert_kind_of Net::LDAP::Entry, entry
entries << entry
end
@@ -26,24 +26,24 @@ def test_search_without_result
end
def test_search_filter_string
- entries = @ldap.search(base: "dc=rubyldap,dc=com", filter: "(uid=user1)")
+ entries = @ldap.search(base: "dc=example,dc=org", filter: "(uid=user1)")
assert_equal 1, entries.size
end
def test_search_filter_object
filter = Net::LDAP::Filter.eq("uid", "user1") | Net::LDAP::Filter.eq("uid", "user2")
- entries = @ldap.search(base: "dc=rubyldap,dc=com", filter: filter)
+ entries = @ldap.search(base: "dc=example,dc=org", filter: filter)
assert_equal 2, entries.size
end
def test_search_constrained_attributes
- entry = @ldap.search(base: "uid=user1,ou=People,dc=rubyldap,dc=com", attributes: ["cn", "sn"]).first
+ entry = @ldap.search(base: "uid=user1,ou=People,dc=example,dc=org", attributes: ["cn", "sn"]).first
assert_equal [:cn, :dn, :sn], entry.attribute_names.sort # :dn is always included
assert_empty entry[:mail]
end
def test_search_attributes_only
- entry = @ldap.search(base: "uid=user1,ou=People,dc=rubyldap,dc=com", attributes_only: true).first
+ entry = @ldap.search(base: "uid=user1,ou=People,dc=example,dc=org", attributes_only: true).first
assert_empty entry[:cn], "unexpected attribute value: #{entry[:cn]}"
end
@@ -52,12 +52,12 @@ def test_search_timeout
entries = []
events = @service.subscribe "search.net_ldap_connection"
- result = @ldap.search(base: "dc=rubyldap,dc=com", time: 5) do |entry|
+ result = @ldap.search(base: "dc=example,dc=org", time: 5) do |entry|
assert_kind_of Net::LDAP::Entry, entry
entries << entry
end
- payload, _ = events.pop
+ payload, = events.pop
assert_equal 5, payload[:time]
assert_equal entries, result
end
@@ -66,7 +66,7 @@ def test_search_timeout
def test_search_with_size
entries = []
- result = @ldap.search(base: "dc=rubyldap,dc=com", size: 1) do |entry|
+ result = @ldap.search(base: "dc=example,dc=org", size: 1) do |entry|
assert_kind_of Net::LDAP::Entry, entry
entries << entry
end
diff --git a/test/support/vm/openldap/README.md b/test/support/vm/openldap/README.md
deleted file mode 100644
index a2769567..00000000
--- a/test/support/vm/openldap/README.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Local OpenLDAP Integration Testing
-
-Set up a [Vagrant](http://www.vagrantup.com/) VM to run integration tests against OpenLDAP locally.
-
-To run integration tests locally:
-
-``` bash
-# start VM (from the correct directory)
-$ cd test/support/vm/openldap/
-$ vagrant up
-
-# get the IP address of the VM
-$ ip=$(vagrant ssh -- "ifconfig eth1 | grep -o -E '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1")
-
-# change back to root project directory
-$ cd ../../../..
-
-# run all tests, including integration tests
-$ time INTEGRATION=openldap INTEGRATION_HOST=$ip bundle exec rake
-
-# run a specific integration test file
-$ time INTEGRATION=openldap INTEGRATION_HOST=$ip bundle exec ruby test/integration/test_search.rb
-
-# run integration tests by default
-$ export INTEGRATION=openldap
-$ export INTEGRATION_HOST=$ip
-
-# now run tests without having to set ENV variables
-$ time bundle exec rake
-```
-
-You may need to `gem install vagrant` first in order to provision the VM.
diff --git a/test/support/vm/openldap/Vagrantfile b/test/support/vm/openldap/Vagrantfile
deleted file mode 100644
index 96233e92..00000000
--- a/test/support/vm/openldap/Vagrantfile
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- mode: ruby -*-
-# vi: set ft=ruby :
-
-# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
-VAGRANTFILE_API_VERSION = "2"
-
-Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
- config.vm.hostname = "rubyldap.com"
-
- config.vm.box = "hashicorp/precise64"
-
- config.vm.network "private_network", type: :dhcp
-
- config.ssh.forward_agent = true
-
- config.vm.provision "shell", inline: "apt-get update; exec env /vagrant_data/script/install-openldap"
-
- config.vm.synced_folder "../../../..", "/vagrant_data"
-
- config.vm.provider "vmware_fusion" do |vb, override|
- override.vm.box = "hashicorp/precise64"
- vb.memory = 4596
- vb.vmx["displayname"] = "integration tests vm"
- vb.vmx["numvcpus"] = "2"
- end
-
- config.vm.provider "virtualbox" do |vb, override|
- vb.memory = 4096
- vb.customize ["modifyvm", :id, "--nicpromisc2", "allow-all"]
- vb.customize ["modifyvm", :id, "--chipset", "ich9"]
- vb.customize ["modifyvm", :id, "--vram", "16"]
- end
-end
diff --git a/test/test_auth_adapter.rb b/test/test_auth_adapter.rb
index 7cec57bc..9e4c6002 100644
--- a/test/test_auth_adapter.rb
+++ b/test/test_auth_adapter.rb
@@ -1,9 +1,13 @@
require 'test_helper'
class TestAuthAdapter < Test::Unit::TestCase
+ class FakeSocket
+ def initialize(*args)
+ end
+ end
+
def test_undefined_auth_adapter
- flexmock(TCPSocket).should_receive(:new).ordered.with('ldap.example.com', 379).once.and_return(nil)
- conn = Net::LDAP::Connection.new(host: 'ldap.example.com', port: 379)
+ conn = Net::LDAP::Connection.new(host: 'ldap.example.com', port: 379, :socket_class => FakeSocket)
assert_raise Net::LDAP::AuthMethodUnsupportedError, "Unsupported auth method (foo)" do
conn.bind(method: :foo)
end
diff --git a/test/test_dn.rb b/test/test_dn.rb
index 0cb2ec5a..52e87bd7 100644
--- a/test/test_dn.rb
+++ b/test/test_dn.rb
@@ -1,11 +1,25 @@
require_relative 'test_helper'
-require 'net/ldap/dn'
+require_relative '../lib/net/ldap/dn'
class TestDN < Test::Unit::TestCase
def test_escape
assert_equal '\\,\\+\\"\\\\\\<\\>\\;', Net::LDAP::DN.escape(',+"\\<>;')
end
+ def test_escape_pound_sign
+ assert_equal '\\#test', Net::LDAP::DN.escape('#test')
+ end
+
+ def test_escape_space
+ assert_equal '\\ before_after\\ ', Net::LDAP::DN.escape(' before_after ')
+ end
+
+ def test_retain_spaces
+ dn = Net::LDAP::DN.new('CN=Foo.bar.baz, OU=Foo \ ,OU=\ Bar, O=Baz')
+ assert_equal "CN=Foo.bar.baz, OU=Foo \\ ,OU=\\ Bar, O=Baz", dn.to_s
+ assert_equal ["CN", "Foo.bar.baz", "OU", "Foo ", "OU", " Bar", "O", "Baz"], dn.to_a
+ end
+
def test_escape_on_initialize
dn = Net::LDAP::DN.new('cn', ',+"\\<>;', 'ou=company')
assert_equal 'cn=\\,\\+\\"\\\\\\<\\>\\;,ou=company', dn.to_s
@@ -13,20 +27,19 @@ def test_escape_on_initialize
def test_to_a
dn = Net::LDAP::DN.new('cn=James, ou=Company\\,\\20LLC')
- assert_equal ['cn','James','ou','Company, LLC'], dn.to_a
+ assert_equal ['cn', 'James', 'ou', 'Company, LLC'], dn.to_a
end
def test_to_a_parenthesis
dn = Net::LDAP::DN.new('cn = \ James , ou = "Comp\28ny" ')
- assert_equal ['cn',' James','ou','Comp(ny'], dn.to_a
+ assert_equal ['cn', ' James ', 'ou', 'Comp(ny'], dn.to_a
end
def test_to_a_hash_symbol
dn = Net::LDAP::DN.new('1.23.4= #A3B4D5 ,ou=Company')
- assert_equal ['1.23.4','#A3B4D5','ou','Company'], dn.to_a
+ assert_equal ['1.23.4', '#A3B4D5', 'ou', 'Company'], dn.to_a
end
- # TODO: raise a more specific exception than RuntimeError
def test_bad_input_raises_error
[
'cn=James,',
@@ -38,7 +51,7 @@ def test_bad_input_raises_error
'd1.2=Value',
].each do |input|
dn = Net::LDAP::DN.new(input)
- assert_raises(RuntimeError) { dn.to_a }
+ assert_raises(Net::LDAP::InvalidDNError) { dn.to_a }
end
end
end
diff --git a/test/test_entry.rb b/test/test_entry.rb
index e2184747..60c89ba6 100644
--- a/test/test_entry.rb
+++ b/test/test_entry.rb
@@ -39,6 +39,32 @@ def test_case_insensitive_attribute_names
assert_equal ['Jensen'], @entry['Sn']
assert_equal ['Jensen'], @entry['SN']
end
+
+ def test_to_h
+ @entry['sn'] = 'Jensen'
+ expected = {
+ dn: ['cn=Barbara,o=corp'],
+ sn: ['Jensen'],
+ }
+ duplicate = @entry.to_h
+ assert_equal expected, duplicate
+
+ # check that changing the duplicate
+ # does not affect the internal state
+ duplicate.delete(:sn)
+ assert_not_equal duplicate, @entry.to_h
+ end
+
+ def test_equal_operator
+ entry_two = Net::LDAP::Entry.new 'cn=Barbara,o=corp'
+ assert_equal @entry, entry_two
+
+ @entry['sn'] = 'Jensen'
+ assert_not_equal @entry, entry_two
+
+ entry_two['sn'] = 'Jensen'
+ assert_equal @entry, entry_two
+ end
end
class TestEntryLDIF < Test::Unit::TestCase
@@ -47,7 +73,8 @@ def setup
%Q{dn: something
foo: foo
barAttribute: bar
- })
+ },
+ )
end
def test_attribute
@@ -59,7 +86,7 @@ def test_modify_attribute
@entry.foo = 'bar'
assert_equal ['bar'], @entry.foo
- @entry.fOo= 'baz'
+ @entry.fOo = 'baz'
assert_equal ['baz'], @entry.foo
end
end
diff --git a/test/test_filter.rb b/test/test_filter.rb
index 2bcccd92..807c86dd 100644
--- a/test/test_filter.rb
+++ b/test/test_filter.rb
@@ -13,11 +13,11 @@ def test_invalid_filter_string
end
def test_invalid_filter
- assert_raises(Net::LDAP::OperatorError) {
+ assert_raises(Net::LDAP::OperatorError) do
# This test exists to prove that our constructor blocks unknown filter
# types. All filters must be constructed using helpers.
Filter.__send__(:new, :xx, nil, nil)
- }
+ end
end
def test_to_s
@@ -144,7 +144,7 @@ def test_ber_conversion
'(:dn:2.4.8.10:=Dino)',
'(cn:dn:1.2.3.4.5:=John Smith)',
'(sn:dn:2.4.6.8.10:=Barbara Jones)',
- '(&(sn:dn:2.4.6.8.10:=Barbara Jones))'
+ '(&(sn:dn:2.4.6.8.10:=Barbara Jones))',
].each_with_index do |filter_str, index|
define_method "test_decode_filter_#{index}" do
filter = Net::LDAP::Filter.from_rfc2254(filter_str)
@@ -195,7 +195,7 @@ def test_well_known_ber_string
"foo" "\\2A\\5C" "bar",
"foo" "\\2a\\5c" "bar",
"foo" "\\2A\\5c" "bar",
- "foo" "\\2a\\5C" "bar"
+ "foo" "\\2a\\5C" "bar",
].each do |escaped|
# unescapes escaped characters
filter = Net::LDAP::Filter.eq("objectclass", "#{escaped}*#{escaped}*#{escaped}")
diff --git a/test/test_filter_parser.rb b/test/test_filter_parser.rb
index 6f1ca48b..960ff1ad 100644
--- a/test/test_filter_parser.rb
+++ b/test/test_filter_parser.rb
@@ -1,4 +1,5 @@
# encoding: utf-8
+
require_relative 'test_helper'
class TestFilterParser < Test::Unit::TestCase
@@ -21,4 +22,8 @@ def test_slash
def test_colons
assert_kind_of Net::LDAP::Filter, Net::LDAP::Filter::FilterParser.parse("(ismemberof=cn=edu:berkeley:app:calmessages:deans,ou=campus groups,dc=berkeley,dc=edu)")
end
+
+ def test_attr_tag
+ assert_kind_of Net::LDAP::Filter, Net::LDAP::Filter::FilterParser.parse("(mail;primary=jane@example.org)")
+ end
end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index cd34017c..4a7600bd 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -1,6 +1,6 @@
# Add 'lib' to load path.
require 'test/unit'
-require 'net/ldap'
+require_relative '../lib/net/ldap'
require 'flexmock/test_unit'
# Whether integration tests should be run.
@@ -14,10 +14,18 @@
if File.exist?("/etc/ssl/certs/cacert.pem")
"/etc/ssl/certs/cacert.pem"
else
- File.expand_path("fixtures/cacert.pem", File.dirname(__FILE__))
+ File.expand_path("fixtures/ca/docker-ca.pem", File.dirname(__FILE__))
end
end
+BIND_CREDS = {
+ method: :simple,
+ username: "cn=admin,dc=example,dc=org",
+ password: "admin",
+}.freeze
+
+TLS_OPTS = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.merge({}).freeze
+
if RUBY_VERSION < "2.0"
class String
def b
@@ -57,10 +65,9 @@ def setup
@ldap = Net::LDAP.new \
host: ENV.fetch('http://github.com/INTEGRATION_HOST', 'localhost'),
port: ENV.fetch('http://github.com/INTEGRATION_PORT', 389),
- admin_user: 'uid=admin,dc=rubyldap,dc=com',
- admin_password: 'passworD1',
- search_domains: %w(dc=rubyldap,dc=com),
+ search_domains: %w(dc=example,dc=org),
uid: 'uid',
instrumentation_service: @service
+ @ldap.authenticate "cn=admin,dc=example,dc=org", "admin"
end
end
diff --git a/test/test_ldap.rb b/test/test_ldap.rb
index f30416b2..6c061475 100644
--- a/test/test_ldap.rb
+++ b/test/test_ldap.rb
@@ -1,6 +1,28 @@
-require 'test_helper'
+require_relative 'test_helper'
class TestLDAPInstrumentation < Test::Unit::TestCase
+ # Fake Net::LDAP::Connection for testing
+ class FakeConnection
+ # It's difficult to instantiate Net::LDAP::PDU objects. Faking out what we
+ # need here until that object is brought under test and has it's constructor
+ # cleaned up.
+ class Result < Struct.new(:success?, :result_code); end
+
+ def initialize
+ @bind_success = Result.new(true, Net::LDAP::ResultCodeSuccess)
+ @search_success = Result.new(true, Net::LDAP::ResultCodeSizeLimitExceeded)
+ end
+
+ def bind(args = {})
+ @bind_success
+ end
+
+ def search(*args)
+ yield @search_success if block_given?
+ @search_success
+ end
+ end
+
def setup
@connection = flexmock(:connection, :close => true)
flexmock(Net::LDAP::Connection).should_receive(:new).and_return(@connection)
@@ -15,8 +37,9 @@ def setup
def test_instrument_bind
events = @service.subscribe "bind.net_ldap"
- bind_result = flexmock(:bind_result, :success? => true)
- flexmock(@connection).should_receive(:bind).with(Hash).and_return(bind_result)
+ fake_connection = FakeConnection.new
+ @subject.connection = fake_connection
+ bind_result = fake_connection.bind
assert @subject.bind
@@ -28,10 +51,9 @@ def test_instrument_bind
def test_instrument_search
events = @service.subscribe "search.net_ldap"
- flexmock(@connection).should_receive(:bind).and_return(flexmock(:bind_result, :result_code => Net::LDAP::ResultCodeSuccess))
- flexmock(@connection).should_receive(:search).with(Hash, Proc).
- yields(entry = Net::LDAP::Entry.new("uid=user1,ou=users,dc=example,dc=com")).
- and_return(flexmock(:search_result, :success? => true, :result_code => Net::LDAP::ResultCodeSuccess))
+ fake_connection = FakeConnection.new
+ @subject.connection = fake_connection
+ entry = fake_connection.search
refute_nil @subject.search(:filter => "(uid=user1)")
@@ -44,10 +66,9 @@ def test_instrument_search
def test_instrument_search_with_size
events = @service.subscribe "search.net_ldap"
- flexmock(@connection).should_receive(:bind).and_return(flexmock(:bind_result, :result_code => Net::LDAP::ResultCodeSuccess))
- flexmock(@connection).should_receive(:search).with(Hash, Proc).
- yields(entry = Net::LDAP::Entry.new("uid=user1,ou=users,dc=example,dc=com")).
- and_return(flexmock(:search_result, :success? => true, :result_code => Net::LDAP::ResultCodeSizeLimitExceeded))
+ fake_connection = FakeConnection.new
+ @subject.connection = fake_connection
+ entry = fake_connection.search
refute_nil @subject.search(:filter => "(uid=user1)", :size => 1)
@@ -64,4 +85,30 @@ def test_obscure_auth
@subject.auth "joe_user", password
assert_not_include(@subject.inspect, password)
end
+
+ def test_encryption
+ enc = @subject.encryption('start_tls')
+
+ assert_equal enc[:method], :start_tls
+ end
+
+ def test_normalize_encryption_symbol
+ enc = @subject.send(:normalize_encryption, :start_tls)
+ assert_equal enc, :method => :start_tls, :tls_options => {}
+ end
+
+ def test_normalize_encryption_nil
+ enc = @subject.send(:normalize_encryption, nil)
+ assert_equal enc, nil
+ end
+
+ def test_normalize_encryption_string
+ enc = @subject.send(:normalize_encryption, 'start_tls')
+ assert_equal enc, :method => :start_tls, :tls_options => {}
+ end
+
+ def test_normalize_encryption_hash
+ enc = @subject.send(:normalize_encryption, :method => :start_tls, :tls_options => { :foo => :bar })
+ assert_equal enc, :method => :start_tls, :tls_options => { :foo => :bar }
+ end
end
diff --git a/test/test_ldap_connection.rb b/test/test_ldap_connection.rb
index 73752631..fdfa418c 100644
--- a/test/test_ldap_connection.rb
+++ b/test/test_ldap_connection.rb
@@ -9,93 +9,123 @@ def capture_stderr
$stderr = stderr
end
+ # Fake socket for testing
+ #
+ # FakeTCPSocket.new("success", 636)
+ # FakeTCPSocket.new("fail.SocketError", 636) # raises SocketError
+ class FakeTCPSocket
+ def initialize(host, port, socket_opts = {})
+ status, error = host.split(".")
+ raise Object.const_get(error) if status == "fail"
+ end
+ end
+
def test_list_of_hosts_with_first_host_successful
hosts = [
- ['test.mocked.com', 636],
- ['test2.mocked.com', 636],
- ['test3.mocked.com', 636],
+ ["success.host", 636],
+ ["fail.SocketError", 636],
+ ["fail.SocketError", 636],
]
- flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[0]).once.and_return(nil)
- flexmock(TCPSocket).should_receive(:new).ordered.never
- Net::LDAP::Connection.new(:hosts => hosts)
+
+ connection = Net::LDAP::Connection.new(:hosts => hosts, :socket_class => FakeTCPSocket)
+ connection.socket
end
def test_list_of_hosts_with_first_host_failure
hosts = [
- ['test.mocked.com', 636],
- ['test2.mocked.com', 636],
- ['test3.mocked.com', 636],
+ ["fail.SocketError", 636],
+ ["success.host", 636],
+ ["fail.SocketError", 636],
]
- flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[0]).once.and_raise(SocketError)
- flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[1]).once.and_return(nil)
- flexmock(TCPSocket).should_receive(:new).ordered.never
- Net::LDAP::Connection.new(:hosts => hosts)
+
+ connection = Net::LDAP::Connection.new(:hosts => hosts, :socket_class => FakeTCPSocket)
+ connection.socket
end
def test_list_of_hosts_with_all_hosts_failure
hosts = [
- ['test.mocked.com', 636],
- ['test2.mocked.com', 636],
- ['test3.mocked.com', 636],
+ ["fail.SocketError", 636],
+ ["fail.SocketError", 636],
+ ["fail.SocketError", 636],
]
- flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[0]).once.and_raise(SocketError)
- flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[1]).once.and_raise(SocketError)
- flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[2]).once.and_raise(SocketError)
- flexmock(TCPSocket).should_receive(:new).ordered.never
+
+ connection = Net::LDAP::Connection.new(:hosts => hosts, :socket_class => FakeTCPSocket)
assert_raise Net::LDAP::ConnectionError do
- Net::LDAP::Connection.new(:hosts => hosts)
+ connection.socket
+ end
+ end
+
+ # This belongs in test_ldap, not test_ldap_connection
+ def test_result_for_connection_failed_is_set
+ flexmock(Socket).should_receive(:tcp).and_raise(Errno::ECONNREFUSED)
+
+ ldap_client = Net::LDAP.new(host: '127.0.0.1', port: 12345)
+
+ assert_raise Errno::ECONNREFUSED do
+ ldap_client.bind(method: :simple, username: 'asdf', password: 'asdf')
end
+
+ assert_equal(ldap_client.get_operation_result.code, 52)
+ assert_equal(ldap_client.get_operation_result.message, 'Unavailable')
end
def test_unresponsive_host
+ connection = Net::LDAP::Connection.new(:host => "fail.Errno::ETIMEDOUT", :port => 636, :socket_class => FakeTCPSocket)
assert_raise Net::LDAP::Error do
- Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636)
+ connection.socket
end
end
def test_blocked_port
- flexmock(TCPSocket).should_receive(:new).and_raise(SocketError)
+ connection = Net::LDAP::Connection.new(:host => "fail.SocketError", :port => 636, :socket_class => FakeTCPSocket)
assert_raise Net::LDAP::Error do
- Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636)
+ connection.socket
end
end
def test_connection_refused
- flexmock(TCPSocket).should_receive(:new).and_raise(Errno::ECONNREFUSED)
+ connection = Net::LDAP::Connection.new(:host => "fail.Errno::ECONNREFUSED", :port => 636, :socket_class => FakeTCPSocket)
stderr = capture_stderr do
- assert_raise Net::LDAP::ConnectionRefusedError do
- Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636)
+ assert_raise Errno::ECONNREFUSED do
+ connection.socket
+ end
+ end
+ end
+
+ def test_connection_timeout
+ connection = Net::LDAP::Connection.new(:host => "fail.Errno::ETIMEDOUT", :port => 636, :socket_class => FakeTCPSocket)
+ capture_stderr do
+ assert_raise Net::LDAP::Error do
+ connection.socket
end
end
- assert_equal("Deprecation warning: Net::LDAP::ConnectionRefused will be deprecated. Use Errno::ECONNREFUSED instead.\n", stderr)
end
def test_raises_unknown_exceptions
- error = Class.new(StandardError)
- flexmock(TCPSocket).should_receive(:new).and_raise(error)
- assert_raise error do
- Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636)
+ connection = Net::LDAP::Connection.new(:host => "fail.StandardError", :port => 636, :socket_class => FakeTCPSocket)
+ assert_raise StandardError do
+ connection.socket
end
end
def test_modify_ops_delete
- args = { :operations => [ [ :delete, "mail" ] ] }
+ args = { :operations => [[:delete, "mail"]] }
result = Net::LDAP::Connection.modify_ops(args[:operations])
- expected = [ "0\r\n\x01\x010\b\x04\x04mail1\x00" ]
+ expected = ["0\r\n\x01\x010\b\x04\x04mail1\x00"]
assert_equal(expected, result)
end
def test_modify_ops_add
- args = { :operations => [ [ :add, "mail", "testuser@example.com" ] ] }
+ args = { :operations => [[:add, "mail", "testuser@example.com"]] }
result = Net::LDAP::Connection.modify_ops(args[:operations])
- expected = [ "0#\n\x01\x000\x1E\x04\x04mail1\x16\x04\x14testuser@example.com" ]
+ expected = ["0#\n\x01\x000\x1E\x04\x04mail1\x16\x04\x14testuser@example.com"]
assert_equal(expected, result)
end
def test_modify_ops_replace
- args = { :operations =>[ [ :replace, "mail", "testuser@example.com" ] ] }
+ args = { :operations => [[:replace, "mail", "testuser@example.com"]] }
result = Net::LDAP::Connection.modify_ops(args[:operations])
- expected = [ "0#\n\x01\x020\x1E\x04\x04mail1\x16\x04\x14testuser@example.com" ]
+ expected = ["0#\n\x01\x020\x1E\x04\x04mail1\x16\x04\x14testuser@example.com"]
assert_equal(expected, result)
end
@@ -129,7 +159,7 @@ def make_message(message_id, options = {})
app_tag: Net::LDAP::PDU::SearchResult,
code: Net::LDAP::ResultCodeSuccess,
matched_dn: "",
- error_message: ""
+ error_message: "",
}.merge(options)
result = Net::BER::BerIdentifiedArray.new([options[:code], options[:matched_dn], options[:error_message]])
result.ber_identifier = options[:app_tag]
@@ -160,9 +190,9 @@ def test_queued_read_reads_until_message_id_match
result2 = make_message(2)
mock = flexmock("socket")
- mock.should_receive(:read_ber).
- and_return(result1).
- and_return(result2)
+ mock.should_receive(:read_ber)
+ .and_return(result1)
+ .and_return(result2)
conn = Net::LDAP::Connection.new(:socket => mock)
assert result = conn.queued_read(2)
@@ -175,9 +205,9 @@ def test_queued_read_modify
result2 = make_message(2, app_tag: Net::LDAP::PDU::ModifyResponse)
mock = flexmock("socket")
- mock.should_receive(:read_ber).
- and_return(result1).
- and_return(result2)
+ mock.should_receive(:read_ber)
+ .and_return(result1)
+ .and_return(result2)
mock.should_receive(:write)
conn = Net::LDAP::Connection.new(:socket => mock)
@@ -196,9 +226,9 @@ def test_queued_read_add
result2 = make_message(2, app_tag: Net::LDAP::PDU::AddResponse)
mock = flexmock("socket")
- mock.should_receive(:read_ber).
- and_return(result1).
- and_return(result2)
+ mock.should_receive(:read_ber)
+ .and_return(result1)
+ .and_return(result2)
mock.should_receive(:write)
conn = Net::LDAP::Connection.new(:socket => mock)
@@ -214,9 +244,9 @@ def test_queued_read_rename
result2 = make_message(2, app_tag: Net::LDAP::PDU::ModifyRDNResponse)
mock = flexmock("socket")
- mock.should_receive(:read_ber).
- and_return(result1).
- and_return(result2)
+ mock.should_receive(:read_ber)
+ .and_return(result1)
+ .and_return(result2)
mock.should_receive(:write)
conn = Net::LDAP::Connection.new(:socket => mock)
@@ -224,7 +254,7 @@ def test_queued_read_rename
assert result = conn.rename(
olddn: "uid=renamable-user1,ou=People,dc=rubyldap,dc=com",
- newrdn: "uid=renamed-user1"
+ newrdn: "uid=renamed-user1",
)
assert result.success?
assert_equal 2, result.message_id
@@ -235,9 +265,9 @@ def test_queued_read_delete
result2 = make_message(2, app_tag: Net::LDAP::PDU::DeleteResponse)
mock = flexmock("socket")
- mock.should_receive(:read_ber).
- and_return(result1).
- and_return(result2)
+ mock.should_receive(:read_ber)
+ .and_return(result1)
+ .and_return(result2)
mock.should_receive(:write)
conn = Net::LDAP::Connection.new(:socket => mock)
@@ -253,13 +283,13 @@ def test_queued_read_setup_encryption_with_start_tls
result2 = make_message(2, app_tag: Net::LDAP::PDU::ExtendedResponse)
mock = flexmock("socket")
- mock.should_receive(:read_ber).
- and_return(result1).
- and_return(result2)
+ mock.should_receive(:read_ber)
+ .and_return(result1)
+ .and_return(result2)
mock.should_receive(:write)
conn = Net::LDAP::Connection.new(:socket => mock)
- flexmock(Net::LDAP::Connection).should_receive(:wrap_with_ssl).with(mock, {}).
- and_return(mock)
+ flexmock(Net::LDAP::Connection).should_receive(:wrap_with_ssl).with(mock, {}, nil, nil)
+ .and_return(mock)
conn.next_msgid # simulates ongoing query
@@ -272,9 +302,9 @@ def test_queued_read_bind_simple
result2 = make_message(2, app_tag: Net::LDAP::PDU::BindResult)
mock = flexmock("socket")
- mock.should_receive(:read_ber).
- and_return(result1).
- and_return(result2)
+ mock.should_receive(:read_ber)
+ .and_return(result1)
+ .and_return(result2)
mock.should_receive(:write)
conn = Net::LDAP::Connection.new(:socket => mock)
@@ -283,7 +313,8 @@ def test_queued_read_bind_simple
assert result = conn.bind(
method: :simple,
username: "uid=user1,ou=People,dc=rubyldap,dc=com",
- password: "passworD1")
+ password: "passworD1",
+ )
assert result.success?
assert_equal 2, result.message_id
end
@@ -293,9 +324,9 @@ def test_queued_read_bind_sasl
result2 = make_message(2, app_tag: Net::LDAP::PDU::BindResult)
mock = flexmock("socket")
- mock.should_receive(:read_ber).
- and_return(result1).
- and_return(result2)
+ mock.should_receive(:read_ber)
+ .and_return(result1)
+ .and_return(result2)
mock.should_receive(:write)
conn = Net::LDAP::Connection.new(:socket => mock)
@@ -305,17 +336,30 @@ def test_queued_read_bind_sasl
method: :sasl,
mechanism: "fake",
initial_credential: "passworD1",
- challenge_response: flexmock("challenge proc"))
+ challenge_response: flexmock("challenge proc"),
+ )
assert result.success?
assert_equal 2, result.message_id
end
+
+ def test_invalid_pdu_type
+ options = {
+ code: Net::LDAP::ResultCodeSuccess,
+ matched_dn: "",
+ error_message: "",
+ }
+ ber = Net::BER::BerIdentifiedArray.new([options[:code], options[:matched_dn], options[:error_message]])
+ assert_raise Net::LDAP::PDU::Error do
+ Net::LDAP::PDU.new([0, ber])
+ end
+ end
end
class TestLDAPConnectionErrors < Test::Unit::TestCase
def setup
@tcp_socket = flexmock(:connection)
@tcp_socket.should_receive(:write)
- flexmock(TCPSocket).should_receive(:new).and_return(@tcp_socket)
+ flexmock(Socket).should_receive(:tcp).and_return(@tcp_socket)
@connection = Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636)
end
@@ -344,7 +388,7 @@ class TestLDAPConnectionInstrumentation < Test::Unit::TestCase
def setup
@tcp_socket = flexmock(:connection)
@tcp_socket.should_receive(:write)
- flexmock(TCPSocket).should_receive(:new).and_return(@tcp_socket)
+ flexmock(Socket).should_receive(:tcp).and_return(@tcp_socket)
@service = MockInstrumentationService.new
@connection = Net::LDAP::Connection.new \
@@ -366,8 +410,8 @@ def test_write_net_ldap_connection_event
# a write event
payload, result = events.pop
- assert payload.has_key?(:result)
- assert payload.has_key?(:content_length)
+ assert payload.key?(:result)
+ assert payload.key?(:content_length)
end
def test_read_net_ldap_connection_event
@@ -383,7 +427,7 @@ def test_read_net_ldap_connection_event
# a read event
payload, result = events.pop
- assert payload.has_key?(:result)
+ assert payload.key?(:result)
assert_equal read_result, result
end
@@ -400,9 +444,9 @@ def test_parse_pdu_net_ldap_connection_event
# a parse_pdu event
payload, result = events.pop
- assert payload.has_key?(:pdu)
- assert payload.has_key?(:app_tag)
- assert payload.has_key?(:message_id)
+ assert payload.key?(:pdu)
+ assert payload.key?(:app_tag)
+ assert payload.key?(:message_id)
assert_equal Net::LDAP::PDU::BindResult, payload[:app_tag]
assert_equal 1, payload[:message_id]
pdu = payload[:pdu]
@@ -422,7 +466,7 @@ def test_bind_net_ldap_connection_event
# a read event
payload, result = events.pop
- assert payload.has_key?(:result)
+ assert payload.key?(:result)
assert result.success?, "should be success"
end
@@ -430,7 +474,7 @@ def test_search_net_ldap_connection_event
# search data
search_data_ber = Net::BER::BerIdentifiedArray.new([1, [
"uid=user1,ou=People,dc=rubyldap,dc=com",
- [ ["uid", ["user1"]] ]
+ [["uid", ["user1"]]],
]])
search_data_ber.ber_identifier = Net::LDAP::PDU::SearchReturnedData
search_data = [1, search_data_ber]
@@ -438,8 +482,8 @@ def test_search_net_ldap_connection_event
search_result_ber = Net::BER::BerIdentifiedArray.new([Net::LDAP::ResultCodeSuccess, "", ""])
search_result_ber.ber_identifier = Net::LDAP::PDU::SearchResult
search_result = [1, search_result_ber]
- @tcp_socket.should_receive(:read_ber).and_return(search_data).
- and_return(search_result)
+ @tcp_socket.should_receive(:read_ber).and_return(search_data)
+ .and_return(search_result)
events = @service.subscribe "search.net_ldap_connection"
unread = @service.subscribe "search_messages_unread.net_ldap_connection"
@@ -449,12 +493,96 @@ def test_search_net_ldap_connection_event
# a search event
payload, result = events.pop
- assert payload.has_key?(:result)
- assert payload.has_key?(:filter)
+ assert payload.key?(:result)
+ assert payload.key?(:filter)
assert_equal "(uid=user1)", payload[:filter].to_s
assert result
# ensure no unread
assert unread.empty?, "should not have any leftover unread messages"
end
+
+ def test_add_with_controls
+ dacl_flag = 0x4 # DACL_SECURITY_INFORMATION
+ control_values = [dacl_flag].map(&:to_ber).to_ber_sequence.to_s.to_ber
+ controls = []
+ # LDAP_SERVER_SD_FLAGS constant definition, taken from https://ldapwiki.com/wiki/LDAP_SERVER_SD_FLAGS_OID
+ ldap_server_sd_flags = '1.2.840.113556.1.4.801'.freeze
+ controls << [ldap_server_sd_flags.to_ber, true.to_ber, control_values].to_ber_sequence
+
+ ber = Net::BER::BerIdentifiedArray.new([Net::LDAP::ResultCodeSuccess, "", ""])
+ ber.ber_identifier = Net::LDAP::PDU::AddResponse
+ @tcp_socket.should_receive(:read_ber).and_return([1, ber])
+
+ result = @connection.add(:dn => "uid=added-user1,ou=People,dc=rubyldap,dc=com", :controls => controls)
+ assert result.success?, "should be success"
+ assert_equal "", result.error_message
+ end
+
+ def test_modify_with_controls
+ dacl_flag = 0x4 # DACL_SECURITY_INFORMATION
+ control_values = [dacl_flag].map(&:to_ber).to_ber_sequence.to_s.to_ber
+ controls = []
+ # LDAP_SERVER_SD_FLAGS constant definition, taken from https://ldapwiki.com/wiki/LDAP_SERVER_SD_FLAGS_OID
+ ldap_server_sd_flags = '1.2.840.113556.1.4.801'.freeze
+ controls << [ldap_server_sd_flags.to_ber, true.to_ber, control_values].to_ber_sequence
+
+ ber = Net::BER::BerIdentifiedArray.new([Net::LDAP::ResultCodeSuccess, "", ""])
+ ber.ber_identifier = Net::LDAP::PDU::ModifyResponse
+ @tcp_socket.should_receive(:read_ber).and_return([1, ber])
+
+ result = @connection.modify(:dn => "1", :operations => [[:replace, "mail", "something@sothsdkf.com"]], :controls => controls)
+ assert result.success?, "should be success"
+ assert_equal "", result.error_message
+ end
+
+ def test_search_with_controls
+ # search data
+ search_data_ber = Net::BER::BerIdentifiedArray.new([1, [
+ "uid=user1,ou=People,dc=rubyldap,dc=com",
+ [["uid", ["user1"]]],
+ ]])
+ search_data_ber.ber_identifier = Net::LDAP::PDU::SearchReturnedData
+ search_data = [1, search_data_ber]
+ # search result (end of results)
+ search_result_ber = Net::BER::BerIdentifiedArray.new([Net::LDAP::ResultCodeSuccess, "", ""])
+ search_result_ber.ber_identifier = Net::LDAP::PDU::SearchResult
+ search_result = [1, search_result_ber]
+ @tcp_socket.should_receive(:read_ber).and_return(search_data)
+ .and_return(search_result)
+
+ events = @service.subscribe "search.net_ldap_connection"
+ unread = @service.subscribe "search_messages_unread.net_ldap_connection"
+
+ all_but_sacl_flag = 0x1 | 0x2 | 0x4 # OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION
+ control_values = [all_but_sacl_flag].map(&:to_ber).to_ber_sequence.to_s.to_ber
+ controls = []
+ # LDAP_SERVER_SD_FLAGS constant definition, taken from https://ldapwiki.com/wiki/LDAP_SERVER_SD_FLAGS_OID
+ ldap_server_sd_flags = '1.2.840.113556.1.4.801'.freeze
+ controls << [ldap_server_sd_flags.to_ber, true.to_ber, control_values].to_ber_sequence
+
+ result = @connection.search(filter: "(uid=user1)", base: "ou=People,dc=rubyldap,dc=com", controls: controls)
+ assert result.success?, "should be success"
+
+ # a search event
+ payload, result = events.pop
+ assert payload.key?(:result)
+ assert payload.key?(:filter)
+ assert_equal "(uid=user1)", payload[:filter].to_s
+ assert result
+
+ # ensure no unread
+ assert unread.empty?, "should not have any leftover unread messages"
+ end
+
+ def test_ldapwhoami
+ ber = Net::BER::BerIdentifiedArray.new([Net::LDAP::ResultCodeSuccess, '', '', 0, 'dn:uid=zerosteiner,ou=users,dc=example,dc=org'])
+ ber.ber_identifier = Net::LDAP::PDU::ExtendedResponse
+ response = [1, ber]
+
+ @tcp_socket.should_receive(:read_ber).and_return(response)
+
+ result = @connection.ldapwhoami
+ assert result.extended_response == 'dn:uid=zerosteiner,ou=users,dc=example,dc=org'
+ end
end
diff --git a/test/test_ldif.rb b/test/test_ldif.rb
index 988c3155..c74ea6e7 100644
--- a/test/test_ldif.rb
+++ b/test/test_ldif.rb
@@ -22,61 +22,61 @@ def test_ldif_with_version
def test_ldif_with_comments
str = ["# Hello from LDIF-land", "# This is an unterminated comment"]
io = StringIO.new(str[0] + "\r\n" + str[1])
- ds = Net::LDAP::Dataset::read_ldif(io)
+ ds = Net::LDAP::Dataset.read_ldif(io)
assert_equal(str, ds.comments)
end
def test_ldif_with_password
psw = "goldbricks"
- hashed_psw = "{SHA}" + Base64::encode64(Digest::SHA1.digest(psw)).chomp
+ hashed_psw = "{SHA}" + Base64.encode64(Digest::SHA1.digest(psw)).chomp
- ldif_encoded = Base64::encode64(hashed_psw).chomp
- ds = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: Goldbrick\r\nuserPassword:: #{ldif_encoded}\r\n\r\n"))
+ ldif_encoded = Base64.encode64(hashed_psw).chomp
+ ds = Net::LDAP::Dataset.read_ldif(StringIO.new("dn: Goldbrick\r\nuserPassword:: #{ldif_encoded}\r\n\r\n"))
recovered_psw = ds["Goldbrick"][:userpassword].shift
assert_equal(hashed_psw, recovered_psw)
end
def test_ldif_with_continuation_lines
- ds = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: abcdefg\r\n hijklmn\r\n\r\n"))
- assert_equal(true, ds.has_key?("abcdefghijklmn"))
+ ds = Net::LDAP::Dataset.read_ldif(StringIO.new("dn: abcdefg\r\n hijklmn\r\n\r\n"))
+ assert_equal(true, ds.key?("abcdefghijklmn"))
end
def test_ldif_with_continuation_lines_and_extra_whitespace
- ds1 = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: abcdefg\r\n hijklmn\r\n\r\n"))
- assert_equal(true, ds1.has_key?("abcdefg hijklmn"))
- ds2 = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: abcdefg\r\n hij klmn\r\n\r\n"))
- assert_equal(true, ds2.has_key?("abcdefghij klmn"))
+ ds1 = Net::LDAP::Dataset.read_ldif(StringIO.new("dn: abcdefg\r\n hijklmn\r\n\r\n"))
+ assert_equal(true, ds1.key?("abcdefg hijklmn"))
+ ds2 = Net::LDAP::Dataset.read_ldif(StringIO.new("dn: abcdefg\r\n hij klmn\r\n\r\n"))
+ assert_equal(true, ds2.key?("abcdefghij klmn"))
end
def test_ldif_tab_is_not_continuation
- ds = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: key\r\n\tnotcontinued\r\n\r\n"))
- assert_equal(true, ds.has_key?("key"))
+ ds = Net::LDAP::Dataset.read_ldif(StringIO.new("dn: key\r\n\tnotcontinued\r\n\r\n"))
+ assert_equal(true, ds.key?("key"))
end
def test_ldif_with_base64_dn
str = "dn:: Q049QmFzZTY0IGRuIHRlc3QsT1U9VGVzdCxPVT1Vbml0cyxEQz1leGFtcGxlLERDPWNvbQ==\r\n\r\n"
- ds = Net::LDAP::Dataset::read_ldif(StringIO.new(str))
- assert_equal(true, ds.has_key?("CN=Base64 dn test,OU=Test,OU=Units,DC=example,DC=com"))
+ ds = Net::LDAP::Dataset.read_ldif(StringIO.new(str))
+ assert_equal(true, ds.key?("CN=Base64 dn test,OU=Test,OU=Units,DC=example,DC=com"))
end
def test_ldif_with_base64_dn_and_continuation_lines
str = "dn:: Q049QmFzZTY0IGRuIHRlc3Qgd2l0aCBjb250aW51YXRpb24gbGluZSxPVT1UZXN0LE9VPVVua\r\n XRzLERDPWV4YW1wbGUsREM9Y29t\r\n\r\n"
- ds = Net::LDAP::Dataset::read_ldif(StringIO.new(str))
- assert_equal(true, ds.has_key?("CN=Base64 dn test with continuation line,OU=Test,OU=Units,DC=example,DC=com"))
+ ds = Net::LDAP::Dataset.read_ldif(StringIO.new(str))
+ assert_equal(true, ds.key?("CN=Base64 dn test with continuation line,OU=Test,OU=Units,DC=example,DC=com"))
end
# TODO, INADEQUATE. We need some more tests
# to verify the content.
def test_ldif
- File.open(TestLdifFilename, "r") {|f|
- ds = Net::LDAP::Dataset::read_ldif(f)
+ File.open(TestLdifFilename, "r") do |f|
+ ds = Net::LDAP::Dataset.read_ldif(f)
assert_equal(13, ds.length)
- }
+ end
end
# Must test folded lines and base64-encoded lines as well as normal ones.
def test_to_ldif
- data = File.open(TestLdifFilename, "rb") { |f| f.read }
+ data = File.open(TestLdifFilename, "rb", &:read)
io = StringIO.new(data)
# added .lines to turn to array because 1.9 doesn't have
@@ -84,13 +84,13 @@ def test_to_ldif
entries = data.lines.grep(/^dn:\s*/) { $'.chomp }
dn_entries = entries.dup
- ds = Net::LDAP::Dataset::read_ldif(io) { |type, value|
+ ds = Net::LDAP::Dataset.read_ldif(io) do |type, value|
case type
when :dn
assert_equal(dn_entries.first, value)
dn_entries.shift
end
- }
+ end
assert_equal(entries.size, ds.size)
assert_equal(entries.sort, ds.to_ldif.grep(/^dn:\s*/) { $'.chomp })
end
diff --git a/test/test_password.rb b/test/test_password.rb
index 87b47d91..407cde94 100644
--- a/test/test_password.rb
+++ b/test/test_password.rb
@@ -4,7 +4,19 @@
class TestPassword < Test::Unit::TestCase
def test_psw
- assert_equal("{MD5}xq8jwrcfibi0sZdZYNkSng==", Net::LDAP::Password.generate( :md5, "cashflow" ))
- assert_equal("{SHA}YE4eGkN4BvwNN1f5R7CZz0kFn14=", Net::LDAP::Password.generate( :sha, "cashflow" ))
+ assert_equal("{MD5}xq8jwrcfibi0sZdZYNkSng==", Net::LDAP::Password.generate(:md5, "cashflow"))
+ assert_equal("{SHA}YE4eGkN4BvwNN1f5R7CZz0kFn14=", Net::LDAP::Password.generate(:sha, "cashflow"))
+ end
+
+ def test_psw_with_ssha256_should_not_contain_linefeed
+ flexmock(SecureRandom).should_receive(:random_bytes).and_return('\xE5\x8A\x99\xF8\xCB\x15GW\xE8\xEA\xAD\x0F\xBF\x95\xB0\xDC')
+ assert_equal("{SSHA256}Cc7MXboTyUP5PnPAeJeCrgMy8+7Gus0sw7kBJuTrmf1ceEU1XHg4QVx4OTlceEY4XHhDQlx4MTVHV1x4RThceEVBXHhBRFx4MEZceEJGXHg5NVx4QjBceERD", Net::LDAP::Password.generate(:ssha256, "cashflow"))
+ end
+
+ def test_utf8_psw
+ flexmock(SecureRandom).should_receive(:random_bytes).and_return('\xE5\x8A\x99\xF8\xCB\x15GW\xE8\xEA\xAD\x0F\xBF\x95\xB0\xDC')
+ utf8_psw = "iHVh©NjrLR§h!cru"
+ assert_equal("{SSHA}shzNiWgSPr3DoDm+Re7QPCcu1g1ceEU1XHg4QVx4OTlceEY4XHhDQlx4MTVHV1x4RThceEVBXHhBRFx4MEZceEJGXHg5NVx4QjBceERD", Net::LDAP::Password.generate(:ssha, utf8_psw))
+ assert_equal("{SSHA256}/aS06GodUyRYx+z436t+WZsH2aQCSac9FY4ewaXzhSNceEU1XHg4QVx4OTlceEY4XHhDQlx4MTVHV1x4RThceEVBXHhBRFx4MEZceEJGXHg5NVx4QjBceERD", Net::LDAP::Password.generate(:ssha256, utf8_psw))
end
end
diff --git a/test/test_search.rb b/test/test_search.rb
index e349d0b8..c577a6a2 100644
--- a/test/test_search.rb
+++ b/test/test_search.rb
@@ -32,8 +32,8 @@ def test_instrumentation_publishes_event
@connection.search(:filter => "test")
payload, result = events.pop
- assert payload.has_key?(:result)
- assert payload.has_key?(:filter)
+ assert payload.key?(:result)
+ assert payload.key?(:filter)
assert_equal "test", payload[:filter]
end
end
diff --git a/test/test_snmp.rb b/test/test_snmp.rb
index fe1ee168..fa064d41 100644
--- a/test/test_snmp.rb
+++ b/test/test_snmp.rb
@@ -1,7 +1,7 @@
# $Id: testsnmp.rb 231 2006-12-21 15:09:29Z blackhedd $
require_relative 'test_helper'
-require 'net/snmp'
+require_relative '../lib/net/snmp'
class TestSnmp < Test::Unit::TestCase
def self.raw_string(s)
@@ -16,9 +16,9 @@ def self.raw_string(s)
def test_invalid_packet
data = "xxxx"
- assert_raise(Net::BER::BerError) {
-ary = data.read_ber(Net::SNMP::AsnSyntax)
- }
+ assert_raise(Net::BER::BerError) do
+ data.read_ber(Net::SNMP::AsnSyntax)
+ end
end
# The method String#read_ber! added by Net::BER consumes a well-formed BER
@@ -40,9 +40,9 @@ def _test_consume_string
end
def test_weird_packet
- assert_raise(Net::SnmpPdu::Error) {
-Net::SnmpPdu.parse("aaaaaaaaaaaaaa")
- }
+ assert_raise(Net::SnmpPdu::Error) do
+ Net::SnmpPdu.parse("aaaaaaaaaaaaaa")
+ end
end
def test_get_request
@@ -93,7 +93,7 @@ def test_make_response
def test_make_bad_response
pdu = Net::SnmpPdu.new
- assert_raise(Net::SnmpPdu::Error) {pdu.to_ber_string}
+ assert_raise(Net::SnmpPdu::Error) { pdu.to_ber_string }
pdu.pdu_type = :get_response
pdu.request_id = 999
pdu.to_ber_string
@@ -115,5 +115,4 @@ def test_community
pdu = Net::SnmpPdu.parse(ary)
assert_equal("xxxxxx", pdu.community)
end
-
end
diff --git a/test/test_ssl_ber.rb b/test/test_ssl_ber.rb
index 7711558b..766c8b84 100644
--- a/test/test_ssl_ber.rb
+++ b/test/test_ssl_ber.rb
@@ -5,7 +5,7 @@ class TestSSLBER < Test::Unit::TestCase
# Transmits str to @to and reads it back from @from.
#
def transmit(str)
- Timeout::timeout(1) do
+ Timeout.timeout(1) do
@to.write(str)
@to.close
@@ -22,18 +22,24 @@ def setup
#
# TODO: Replace test with real socket
# https://github.com/ruby-ldap/ruby-net-ldap/pull/121#discussion_r18746386
- flexmock(OpenSSL::SSL::SSLSocket).
- new_instances.should_receive(:connect => nil)
+ flexmock(OpenSSL::SSL::SSLSocket)
+ .new_instances.should_receive(:connect => nil)
@to = Net::LDAP::Connection.wrap_with_ssl(@to)
@from = Net::LDAP::Connection.wrap_with_ssl(@from)
end
def test_transmit_strings
+ omit_if RUBY_PLATFORM == "java", "JRuby throws an error without a real socket"
+ omit_if (RUBY_VERSION >= "3.1" || RUBY_ENGINE == "truffleruby"), "Ruby complains about connection not being open"
+
assert_equal "foo", transmit("foo")
end
def test_transmit_ber_encoded_numbers
+ omit_if RUBY_PLATFORM == "java", "JRuby throws an error without a real socket"
+ omit_if (RUBY_VERSION >= "3.1" || RUBY_ENGINE == "truffleruby"), "Ruby complains about connection not being open"
+
@to.write 1234.to_ber
assert_equal 1234, @from.read_ber
end
diff --git a/testserver/ldapserver.rb b/testserver/ldapserver.rb
index eba130ce..9adeacb0 100644
--- a/testserver/ldapserver.rb
+++ b/testserver/ldapserver.rb
@@ -15,8 +15,7 @@
#------------------------------------------------
module LdapServer
-
- LdapServerAsnSyntax = {
+ LdapServerAsnSyntaxTemplate = {
:application => {
:constructed => {
0 => :array, # LDAP BindRequest
@@ -24,7 +23,7 @@ module LdapServer
},
:primitive => {
2 => :string, # ldapsearch sends this to unbind
- }
+ },
},
:context_specific => {
:primitive => {
@@ -34,7 +33,7 @@ module LdapServer
:constructed => {
3 => :array # equality filter
},
- }
+ },
}
def post_init
@@ -46,7 +45,7 @@ def receive_data data
@data ||= ""; @data << data
while pdu = @data.read_ber!(LdapServerAsnSyntax)
begin
- handle_ldap_pdu pdu
+ handle_ldap_pdu pdu
rescue
$logger.error "closing connection due to error #{$!}"
close_connection
@@ -87,9 +86,7 @@ def handle_bind_request pdu
end
end
-
-
- #--
+ # --
# Search Response ::=
# CHOICE {
# entry [APPLICATION 4] SEQUENCE {
@@ -119,9 +116,9 @@ def handle_search_request pdu
# pdu[1][7] is the list of requested attributes.
# If it's an empty array, that means that *all* attributes were requested.
requested_attrs = if pdu[1][7].length > 0
- pdu[1][7].map {|a| a.downcase}
- else
- :all
+ pdu[1][7].map(&:downcase)
+ else
+ :all
end
filters = pdu[1][6]
@@ -131,50 +128,45 @@ def handle_search_request pdu
end
# TODO, what if this returns nil?
- filter = Net::LDAP::Filter.parse_ldap_filter( filters )
+ filter = Net::LDAP::Filter.parse_ldap_filter(filters)
- $ldif.each {|dn, entry|
- if filter.match( entry )
+ $ldif.each do |dn, entry|
+ if filter.match(entry)
attrs = []
- entry.each {|k, v|
- if requested_attrs == :all or requested_attrs.include?(k.downcase)
- attrvals = v.map {|v1| v1.to_ber}.to_ber_set
+ entry.each do |k, v|
+ if requested_attrs == :all || requested_attrs.include?(k.downcase)
+ attrvals = v.map(&:to_ber).to_ber_set
attrs << [k.to_ber, attrvals].to_ber_sequence
end
- }
+ end
appseq = [dn.to_ber, attrs.to_ber_sequence].to_ber_appsequence(4)
pkt = [msgid.to_ber, appseq].to_ber_sequence
send_data pkt
end
- }
-
+ end
send_ldap_response 5, pdu[0].to_i, 0, "", "Was that what you wanted?"
end
-
-
def send_ldap_response pkt_tag, msgid, code, dn, text
- send_data( [msgid.to_ber, [code.to_ber, dn.to_ber, text.to_ber].to_ber_appsequence(pkt_tag) ].to_ber )
+ send_data([msgid.to_ber, [code.to_ber, dn.to_ber, text.to_ber].to_ber_appsequence(pkt_tag)].to_ber)
end
-
end
-
#------------------------------------------------
# Rather bogus, a global method, which reads a HARDCODED filename
# parses out LDIF data. It will be used to serve LDAP queries out of this server.
#
def load_test_data
- ary = File.readlines( "./testdata.ldif" )
+ ary = File.readlines("./testdata.ldif")
hash = {}
- while line = ary.shift and line.chomp!
+ while (line = ary.shift) && line.chomp!
if line =~ /^dn:[\s]*/i
dn = $'
hash[dn] = {}
- while attr = ary.shift and attr.chomp! and attr =~ /^([\w]+)[\s]*:[\s]*/
+ while (attr = ary.shift) && attr.chomp! && attr =~ /^([\w]+)[\s]*:[\s]*/
hash[dn][$1.downcase] ||= []
hash[dn][$1.downcase] << $'
end
@@ -183,7 +175,6 @@ def load_test_data
hash
end
-
#------------------------------------------------
if __FILE__ == $0
@@ -200,11 +191,10 @@ def load_test_data
$ldif = load_test_data
require 'net/ldap'
-
- EventMachine.run {
+ LdapServerAsnSyntax = Net::BER.compile_syntax(LdapServerAsnSyntaxTemplate)
+ EventMachine.run do
$logger.info "starting LDAP server on 127.0.0.1 port 3890"
EventMachine.start_server "127.0.0.1", 3890, LdapServer
- EventMachine.add_periodic_timer 60, proc {$logger.info "heartbeat"}
- }
+ EventMachine.add_periodic_timer 60, proc { $logger.info "heartbeat" }
+ end
end
-