diff --git a/.rubocop.yml b/.rubocop.yml
index 85ffa202..7bdfa631 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -3,3 +3,18 @@ inherit_from: .rubocop_todo.yml
AllCops:
Exclude:
- 'pkg/**/*'
+
+Style/ExtraSpacing:
+ Enabled: false
+
+Lint/AssignmentInCondition:
+ Enabled: false
+
+Style/ParallelAssignment:
+ Enabled: false
+
+Style/TrailingCommaInLiteral:
+ EnforcedStyleForMultiline: comma
+
+Style/TrailingCommaInArguments:
+ EnforcedStyleForMultiline: comma
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 5a5dcbc7..50c86e74 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -1,41 +1,74 @@
-# 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 2016-08-17 14:58:12 -0700 using RuboCop version 0.42.0.
# 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: 1
-# Configuration parameters: AlignWith, SupportedStyles.
+# Cop supports --auto-correct.
+# Configuration parameters: AlignWith, SupportedStyles, AutoCorrect.
+# SupportedStyles: keyword, variable, start_of_line
Lint/EndAlignment:
- Enabled: false
+ Exclude:
+ - 'testserver/ldapserver.rb'
+
+# Offense count: 30
+Lint/ImplicitStringConcatenation:
+ Exclude:
+ - 'test/test_filter.rb'
+
+# Offense count: 1
+Lint/NonLocalExitFromIterator:
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
# Offense count: 1
Lint/RescueException:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/pdu.rb'
# Offense count: 1
Lint/ShadowingOuterLocalVariable:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/instrumentation.rb'
-# Offense count: 9
+# Offense count: 10
# Cop supports --auto-correct.
+# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
Lint/UnusedBlockArgument:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/snmp.rb'
+ - 'test/support/vm/openldap/Vagrantfile'
-# Offense count: 3
+# Offense count: 7
# Cop supports --auto-correct.
+# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
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
+# Configuration parameters: ContextCreatingMethods.
+Lint/UselessAccessModifier:
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
+
+# Offense count: 9
Lint/UselessAssignment:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/password.rb'
+ - 'test/integration/test_add.rb'
+ - 'test/test_ldap_connection.rb'
+ - 'test/test_search.rb'
+ - 'test/test_snmp.rb'
# Offense count: 47
Metrics/AbcSize:
@@ -45,418 +78,676 @@ Metrics/AbcSize:
Metrics/BlockNesting:
Max: 4
-# Offense count: 9
+# Offense count: 10
# Configuration parameters: CountComments.
Metrics/ClassLength:
- Max: 470
+ Max: 431
-# Offense count: 20
+# Offense count: 22
Metrics/CyclomaticComplexity:
Max: 41
-# Offense count: 193
-# Configuration parameters: AllowURI, URISchemes.
+# Offense count: 225
+# Configuration parameters: AllowHeredoc, AllowURI, URISchemes.
+# URISchemes: http, https
Metrics/LineLength:
Max: 360
-# Offense count: 71
+# Offense count: 70
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 130
-# Offense count: 13
+# Offense count: 1
+# Configuration parameters: CountComments.
+Metrics/ModuleLength:
+ Max: 104
+
+# Offense count: 14
Metrics/PerceivedComplexity:
- Max: 36
+ Max: 37
# Offense count: 1
Style/AccessorMethodName:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap.rb'
+
+# Offense count: 10
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# 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: 4
# Cop supports --auto-correct.
Style/AlignArray:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/auth_adapter/sasl.rb'
+ - 'lib/net/ldap/connection.rb'
-# Offense count: 3
+# Offense count: 10
# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
+# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
+# SupportedStyles: with_first_parameter, with_fixed_indentation
Style/AlignParameters:
- Enabled: false
+ Exclude:
+ - 'test/ber/test_ber.rb'
+ - 'test/integration/test_ber.rb'
+ - 'test/integration/test_bind.rb'
+ - 'test/integration/test_password_modify.rb'
-# Offense count: 36
+# Offense count: 37
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: always, conditionals
Style/AndOr:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber/ber_parser.rb'
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/dataset.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'lib/net/ldap/pdu.rb'
+ - 'testserver/ldapserver.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: percent_q, bare_percent
Style/BarePercentLiterals:
- Enabled: false
+ Exclude:
+ - 'test/test_entry.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/BlockComments:
- Enabled: false
+ Exclude:
+ - 'test/test_rename.rb'
-# Offense count: 20
-# Cop supports --auto-correct.
-Style/Blocks:
- Enabled: false
-
-# Offense count: 2
+# Offense count: 6
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: braces, no_braces, context_dependent
Style/BracesAroundHashParameters:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/auth_adapter/gss_spnego.rb'
+ - 'lib/net/snmp.rb'
+ - 'test/test_ldap.rb'
# Offense count: 4
-# Configuration parameters: IndentWhenRelativeTo, SupportedStyles, IndentOneStep.
+# Cop supports --auto-correct.
+# Configuration parameters: IndentWhenRelativeTo, SupportedStyles, IndentOneStep, IndentationWidth.
+# SupportedStyles: case, end
Style/CaseIndentation:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
# Offense count: 4
# Cop supports --auto-correct.
Style/CharacterLiteral:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/dataset.rb'
+ - 'lib/net/ldap/entry.rb'
-# Offense count: 22
+# Offense count: 1
+Style/ClassAndModuleCamelCase:
+ Exclude:
+ - 'lib/net/ldap/auth_adapter/gss_spnego.rb'
+
+# Offense count: 23
# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: nested, compact
Style/ClassAndModuleChildren:
Enabled: false
-# Offense count: 1
+# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: is_a?, kind_of?
Style/ClassCheck:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber/core_ext/array.rb'
+ - 'lib/net/ldap/error.rb'
# Offense count: 13
# Cop supports --auto-correct.
Style/ColonMethodCall:
- Enabled: false
+ Exclude:
+ - 'test/test_ldif.rb'
+ - 'test/test_ssl_ber.rb'
-# Offense count: 2
+# Offense count: 1
+# Cop supports --auto-correct.
# Configuration parameters: Keywords.
+# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW
Style/CommentAnnotation:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber.rb'
-# Offense count: 86
-Style/ConstantName:
- Enabled: false
-
-# Offense count: 18
+# Offense count: 1
# Cop supports --auto-correct.
-Style/DeprecatedHashMethods:
- Enabled: false
+# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly.
+# SupportedStyles: assign_to_condition, assign_inside_condition
+Style/ConditionalAssignment:
+ Exclude:
+ - 'lib/net/ldap/dn.rb'
-# Offense count: 46
+# Offense count: 88
+Style/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: 17
Style/Documentation:
- Enabled: false
-
-# Offense count: 23
+ Exclude:
+ - 'spec/**/*'
+ - 'test/**/*'
+ - 'lib/net/ber/core_ext.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/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: 19
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: leading, trailing
Style/DotPosition:
- Enabled: false
+ Exclude:
+ - 'test/test_ldap_connection.rb'
+ - 'test/test_ssl_ber.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/ElseAlignment:
- Enabled: false
+ Exclude:
+ - 'testserver/ldapserver.rb'
-# Offense count: 4
+# Offense count: 5
# Cop supports --auto-correct.
# Configuration parameters: AllowAdjacentOneLineDefs.
Style/EmptyLineBetweenDefs:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/dataset.rb'
+ - 'lib/net/snmp.rb'
-# Offense count: 9
+# Offense count: 8
# Cop supports --auto-correct.
Style/EmptyLines:
- Enabled: false
+ Exclude:
+ - 'lib/net/snmp.rb'
+ - 'testserver/ldapserver.rb'
-# Offense count: 1
+# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: empty_lines, no_empty_lines
Style/EmptyLinesAroundClassBody:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'test/test_snmp.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: empty_lines, no_empty_lines
Style/EmptyLinesAroundModuleBody:
- Enabled: false
+ Exclude:
+ - 'testserver/ldapserver.rb'
# Offense count: 3
+# Cop supports --auto-correct.
Style/EvenOdd:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/dn.rb'
# Offense count: 1
-# Configuration parameters: Exclude.
+# Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts.
Style/FileName:
- Enabled: false
+ Exclude:
+ - 'lib/net-ldap.rb'
# Offense count: 9
# Configuration parameters: AllowedVariables.
Style/GlobalVars:
- Enabled: false
+ Exclude:
+ - 'testserver/ldapserver.rb'
-# Offense count: 3
-# Configuration parameters: MinBodyLength.
-Style/GuardClause:
- Enabled: false
-
-# Offense count: 150
+# Offense count: 161
# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
+# Configuration parameters: EnforcedStyle, SupportedStyles, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
+# SupportedStyles: ruby19, ruby19_no_mixed_keys, hash_rockets
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/integration/test_bind.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
+# Offense count: 1
+Style/IfInsideElse:
+ Exclude:
+ - 'lib/net/ldap/instrumentation.rb'
+
+# Offense count: 7
+# Cop supports --auto-correct.
# Configuration parameters: MaxLineLength.
Style/IfUnlessModifier:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber.rb'
+ - 'lib/net/ber/core_ext/integer.rb'
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'lib/net/snmp.rb'
+ - 'test/test_ldap_connection.rb'
# Offense count: 2
# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
+# Configuration parameters: SupportedStyles, IndentationWidth.
+# SupportedStyles: special_inside_parentheses, consistent, align_brackets
+Style/IndentArray:
+ EnforcedStyle: consistent
+
+# Offense count: 2
+# Cop supports --auto-correct.
+# Configuration parameters: SupportedStyles, IndentationWidth.
+# SupportedStyles: special_inside_parentheses, consistent, align_braces
Style/IndentHash:
- Enabled: false
+ EnforcedStyle: consistent
-# Offense count: 6
+# Offense count: 10
# Cop supports --auto-correct.
# Configuration parameters: Width.
Style/IndentationWidth:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber.rb'
+ - 'lib/net/ldap/password.rb'
+ - 'lib/net/snmp.rb'
+ - 'test/test_snmp.rb'
+ - 'testserver/ldapserver.rb'
-# Offense count: 2
+# Offense count: 3
# Cop supports --auto-correct.
Style/LeadingCommentSpace:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber/core_ext/array.rb'
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/connection.rb'
# Offense count: 21
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
+# 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: 2
+Style/MethodMissing:
+ Exclude:
+ - 'lib/net/ldap/dn.rb'
+ - 'lib/net/ldap/entry.rb'
# Offense count: 1
# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: snake_case, camelCase
Style/MethodName:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
-# Offense count: 5
+# Offense count: 4
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/MultilineOperationIndentation:
- Enabled: false
+# SupportedStyles: symmetrical, new_line, same_line
+Style/MultilineMethodCallBraceLayout:
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
+ - 'test/test_entry.rb'
+ - 'test/test_ldap_connection.rb'
+
+# Offense count: 1
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
+# SupportedStyles: aligned, indented, indented_relative_to_receiver
+Style/MultilineMethodCallIndentation:
+ Exclude:
+ - 'test/test_ldap_connection.rb'
# Offense count: 1
Style/MultilineTernaryOperator:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
+
+# Offense count: 26
+# Cop supports --auto-correct.
+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/support/vm/openldap/Vagrantfile'
+ - 'test/test_ldif.rb'
+ - 'testserver/ldapserver.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/NegatedIf:
- Enabled: false
+ Exclude:
+ - 'test/test_helper.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/NegatedWhile:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
# Offense count: 3
+# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
+# 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.
Style/NilComparison:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: IncludeSemanticChanges.
Style/NonNilCheck:
- Enabled: false
+ Exclude:
+ - 'lib/net/ber/ber_parser.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/Not:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
-# Offense count: 10
+# Offense count: 11
# Cop supports --auto-correct.
Style/NumericLiterals:
MinDigits: 8
+# Offense count: 4
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: predicate, comparison
+Style/NumericPredicate:
+ Exclude:
+ - 'lib/net/ber/core_ext/integer.rb'
+ - 'lib/net/ldap/dn.rb'
+ - 'testserver/ldapserver.rb'
+
# Offense count: 3
Style/OpMethod:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
# Offense count: 6
# Cop supports --auto-correct.
# Configuration parameters: AllowSafeAssignment.
Style/ParenthesesAroundCondition:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap.rb'
+ - 'lib/net/ldap/auth_adapter/gss_spnego.rb'
+ - 'lib/net/ldap/auth_adapter/sasl.rb'
+ - 'lib/net/ldap/auth_adapter/simple.rb'
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: PreferredDelimiters.
Style/PercentLiteralDelimiters:
- Enabled: false
+ Exclude:
+ - 'net-ldap.gemspec'
+ - 'test/test_entry.rb'
# Offense count: 11
# Cop supports --auto-correct.
Style/PerlBackrefs:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/dataset.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'testserver/ldapserver.rb'
-# Offense count: 9
+# Offense count: 10
+# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
+# 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.
Style/RedundantBegin:
- Enabled: false
+ Exclude:
+ - 'lib/net/snmp.rb'
-# Offense count: 3
+# Offense count: 4
+# Cop supports --auto-correct.
+Style/RedundantParentheses:
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
+ - 'test/test_filter.rb'
+
+# Offense count: 4
# Cop supports --auto-correct.
# 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'
+ - 'lib/net/ldap/password.rb'
-# Offense count: 7
+# Offense count: 8
# Cop supports --auto-correct.
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
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, 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
+# Cop supports --auto-correct.
Style/RescueModifier:
- Enabled: false
+ Exclude:
+ - 'test/ber/core_ext/test_string.rb'
-# Offense count: 7
+# Offense count: 8
# 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:
+ - 'lib/net/ldap/dn.rb'
+ - 'lib/net/ldap/error.rb'
+ - 'testserver/ldapserver.rb'
# Offense count: 2
# Configuration parameters: Methods.
+# Methods: {"reduce"=>["a", "e"]}, {"inject"=>["a", "e"]}
Style/SingleLineBlockParams:
- Enabled: false
-
-# Offense count: 2
-# Cop supports --auto-correct.
-Style/SingleSpaceBeforeFirstArg:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
-# Offense count: 24
-# Cop supports --auto-correct.
-Style/SpaceAfterComma:
- Enabled: false
-
-# Offense count: 2
+# Offense count: 5
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: space, no_space
Style/SpaceAroundEqualsInParameterDefault:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/snmp.rb'
-# Offense count: 8
+# Offense count: 4
# Cop supports --auto-correct.
-Style/SpaceAroundOperators:
- Enabled: false
+Style/SpaceAroundKeyword:
+ Exclude:
+ - 'lib/net/ldap/entry.rb'
+ - 'lib/net/snmp.rb'
-# Offense count: 2
+# Offense count: 9
# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-Style/SpaceBeforeBlockBraces:
- Enabled: false
+# Configuration parameters: AllowForAlignment.
+Style/SpaceAroundOperators:
+ Exclude:
+ - 'lib/net/ber/ber_parser.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/entry.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'test/test_entry.rb'
+ - 'test/test_ldap_connection.rb'
-# Offense count: 18
+# Offense count: 5
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
+# SupportedStyles: space, no_space
Style/SpaceInsideBlockBraces:
- Enabled: false
-
-# Offense count: 37
-# Cop supports --auto-correct.
-Style/SpaceInsideBrackets:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/dataset.rb'
+ - 'test/test_snmp.rb'
+ - 'testserver/ldapserver.rb'
-# Offense count: 1
+# Offense count: 13
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SupportedStyles.
+# SupportedStyles: space, no_space, compact
Style/SpaceInsideHashLiteralBraces:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/dataset.rb'
+ - 'test/test_ldap.rb'
# Offense count: 20
# Cop supports --auto-correct.
Style/SpaceInsideParens:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/entry.rb'
+ - 'lib/net/snmp.rb'
+ - 'test/test_password.rb'
+ - 'testserver/ldapserver.rb'
# Offense count: 5
# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: use_perl_names, use_english_names
Style/SpecialGlobalVars:
- Enabled: false
+ Exclude:
+ - 'lib/net/snmp.rb'
+ - 'net-ldap.gemspec'
+ - 'testserver/ldapserver.rb'
-# Offense count: 645
+# Offense count: 679
# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
+# Configuration parameters: EnforcedStyle, SupportedStyles, 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
+Style/StructInheritance:
+ Exclude:
+ - 'test/test_ldap.rb'
-# Offense count: 9
+# Offense count: 4
# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
-Style/TrailingComma:
- Enabled: false
+# Configuration parameters: EnforcedStyle, SupportedStyles, AllowSafeAssignment.
+# SupportedStyles: require_parentheses, require_no_parentheses
+Style/TernaryParentheses:
+ Exclude:
+ - 'lib/net/ber/core_ext/integer.rb'
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/dataset.rb'
# Offense count: 1
# Cop supports --auto-correct.
-# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, Whitelist.
+# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist.
+# Whitelist: 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
+ Exclude:
+ - 'net-ldap.gemspec'
+ - 'test/test_entry.rb'
# Offense count: 1
+# Cop supports --auto-correct.
# Configuration parameters: MaxLineLength.
Style/WhileUntilModifier:
- Enabled: false
+ Exclude:
+ - 'lib/net/ldap/filter.rb'
# Offense count: 1
# Cop supports --auto-correct.
-# Configuration parameters: WordRegex.
+# Configuration parameters: SupportedStyles, WordRegex.
+# SupportedStyles: percent, brackets
Style/WordArray:
- MinSize: 2
+ EnforcedStyle: percent
+ MinSize: 3
+
+# Offense count: 6
+# Cop supports --auto-correct.
+Style/ZeroLengthPredicate:
+ Exclude:
+ - 'lib/net/ldap/connection.rb'
+ - 'lib/net/ldap/filter.rb'
+ - 'testserver/ldapserver.rb'
diff --git a/.travis.yml b/.travis.yml
index 4131d6e4..fc764963 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,5 @@
language: ruby
rvm:
- - 1.9.3
- 2.0.0
- 2.1
- 2.2
@@ -13,6 +12,9 @@ rvm:
env:
- INTEGRATION=openldap
+before_install:
+ - gem update bundler
+
install:
- if [ "$INTEGRATION" = "openldap" ]; then sudo script/install-openldap; fi
- bundle install
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/History.rdoc b/History.rdoc
index fa7ff5a1..3fcc291b 100644
--- a/History.rdoc
+++ b/History.rdoc
@@ -1,3 +1,67 @@
+=== 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]
+* Define auth adapters {#226}[https://github.com/ruby-ldap/ruby-net-ldap/pull/226]
+* add slash to attribute value filter {#225}[https://github.com/ruby-ldap/ruby-net-ldap/pull/225]
+* Add the ability to provide a list of hosts for a connection {#223}[https://github.com/ruby-ldap/ruby-net-ldap/pull/223]
+* Specify the port of LDAP server by giving INTEGRATION_PORT {#221}[https://github.com/ruby-ldap/ruby-net-ldap/pull/221]
+* Correctly set BerIdentifiedString values to UTF-8 {#212}[https://github.com/ruby-ldap/ruby-net-ldap/pull/212]
+* Raise Net::LDAP::ConnectionRefusedError when new connection is refused. {#213}[https://github.com/ruby-ldap/ruby-net-ldap/pull/213]
+* obscure auth password upon #inspect, added test, closes #216 {#217}[https://github.com/ruby-ldap/ruby-net-ldap/pull/217]
+* Fixing incorrect error class name {#207}[https://github.com/ruby-ldap/ruby-net-ldap/pull/207]
+* Travis update {#205}[https://github.com/ruby-ldap/ruby-net-ldap/pull/205]
+* Remove obsolete rbx-19mode from Travis {#204}[https://github.com/ruby-ldap/ruby-net-ldap/pull/204]
+* mv "sudo" from script/install-openldap to .travis.yml {#199}[https://github.com/ruby-ldap/ruby-net-ldap/pull/199]
+* Remove meaningless shebang {#200}[https://github.com/ruby-ldap/ruby-net-ldap/pull/200]
+* Fix Travis CI build {#202}[https://github.com/ruby-ldap/ruby-net-ldap/pull/202]
+* README.rdoc: fix travis link {#195}[https://github.com/ruby-ldap/ruby-net-ldap/pull/195]
+
=== Net::LDAP 0.11
* Major enhancements:
* #183 Specific errors subclassing Net::LDAP::Error
diff --git a/README.rdoc b/README.rdoc
index 89b2d7d7..f1b1ea36 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -25,7 +25,7 @@ See 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
@@ -37,6 +37,14 @@ sources.
Simply require either 'net-ldap' or 'net/ldap'.
+== Extensions
+
+This library focuses on the core LDAP RFCs referenced in the description.
+However, we recognize there are commonly used extensions to the spec that are
+useful. If there is another library which handles it, we list it here.
+
+* {resolv-srv}[https://rubygems.org/gems/resolv-srv]: Support RFC2782 SRV record lookup and failover
+
== Develop
This task will run the test suite and the
@@ -44,21 +52,20 @@ This task will run the test suite and the
rake rubotest
-To run the integration tests against an LDAP server:
-
- cd test/support/vm/openldap
- vagrant up
- cd ../../../..
- INTEGRATION=openldap bundle exec rake rubotest
+CI takes too long? If your local box supports
+{Vagrant}[https://www.vagrantup.com/], you can run most of the tests
+in a VM on your local box. For more details and setup instructions, see
+{test/support/vm/openldap/README.md}[https://github.com/ruby-ldap/ruby-net-ldap/tree/master/test/support/vm/openldap/README.md]
== 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 `History.rdoc`. Get latest changes with `git log --oneline vLAST_RELEASE..HEAD | grep Merge`
-
-* On the master branch, run `script/release`
+* 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`
:include: Contributors.rdoc
diff --git a/lib/net/ber.rb b/lib/net/ber.rb
index b8992a92..eb6f04b3 100644
--- a/lib/net/ber.rb
+++ b/lib/net/ber.rb
@@ -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,12 +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 args
- # LDAP uses UTF-8 encoded strings
- self.encode('UTF-8') if self.respond_to?(:encoding) rescue self
+ 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
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/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 75b463fb..f7a98ef5 100644
--- a/lib/net/ldap.rb
+++ b/lib/net/ldap.rb
@@ -27,6 +27,12 @@ class LDAP
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'
+
+Net::LDAP::AuthAdapter.register([:simple, :anon, :anonymous], Net::LDAP::AuthAdapter::Simple)
+Net::LDAP::AuthAdapter.register(:sasl, Net::LDAP::AuthAdapter::Sasl)
# == Quick-start for the Impatient
# === Quick Example of a user-authentication against an LDAP directory:
@@ -73,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
#
@@ -250,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 = {
@@ -309,7 +323,14 @@ class Net::LDAP
:constructed => constructed,
}
+ universal = {
+ constructed: {
+ 107 => :array, #ExtendedResponse (PasswdModifyResponseValue)
+ },
+ }
+
AsnSyntax = Net::BER.compile_syntax(:application => application,
+ :universal => universal,
:context_specific => context_specific)
DefaultHost = "127.0.0.1"
@@ -318,7 +339,8 @@ 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'
# https://tools.ietf.org/html/rfc4511#section-4.1.9
# https://tools.ietf.org/html/rfc4511#appendix-A
@@ -367,14 +389,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
@@ -416,7 +438,7 @@ class Net::LDAP
ResultCodeEntryAlreadyExists => "Entry Already Exists",
ResultCodeObjectClassModsProhibited => "ObjectClass Modifications Prohibited",
ResultCodeAffectsMultipleDSAs => "Affects Multiple DSAs",
- ResultCodeOther => "Other"
+ ResultCodeOther => "Other",
}
module LDAPControls
@@ -432,6 +454,7 @@ def self.result2string(code) #:nodoc:
attr_accessor :host
attr_accessor :port
+ attr_accessor :hosts
attr_accessor :base
# Instantiate an object of type Net::LDAP to perform directory operations.
@@ -440,6 +463,8 @@ def self.result2string(code) #:nodoc:
# described below. The following arguments are supported:
# * :host => the LDAP server's IP-address (default 127.0.0.1)
# * :port => the LDAP server's TCP port (default 389)
+ # * :hosts => an enumerable of pairs of hosts and corresponding ports with
+ # which to attempt opening connections (default [[host, port]])
# * :auth => a Hash containing authorization parameters. Currently
# supported values include: {:method => :anonymous} and {:method =>
# :simple, :username => your_user_name, :password => your_password }
@@ -451,28 +476,83 @@ 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.
+ # * :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
+ @hosts = args[:hosts]
@verbose = false # Make this configurable with a switch on the class.
@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
@@ -523,7 +603,7 @@ def authenticate(username, password)
@auth = {
:method => :simple,
:username => username,
- :password => password
+ :password => password,
}
end
alias_method :auth, :authenticate
@@ -536,54 +616,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
@@ -627,8 +665,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.
@@ -740,10 +781,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
@@ -882,7 +923,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]
@@ -890,7 +931,7 @@ def bind_as(args = {})
result = rs if bind(:method => :simple, :username => dn,
:password => password)
end
- }
+ end
result
end
@@ -1017,6 +1058,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
@@ -1135,7 +1214,7 @@ def search_root_dse
:supportedExtension,
:supportedFeatures,
:supportedLdapVersion,
- :supportedSASLMechanisms
+ :supportedSASLMechanisms,
])
(rs and rs.first) or Net::LDAP::Entry.new
end
@@ -1195,6 +1274,18 @@ def paged_searches_supported?
@server_caps[:supportedcontrol].include?(Net::LDAP::LDAPControls::PAGED_RESULTS)
end
+ # Mask auth password
+ def inspect
+ inspected = super
+ inspected.gsub! @auth[:password], "*******" if @auth[:password]
+ 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
@@ -1207,11 +1298,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
@@ -1220,10 +1309,35 @@ 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, Net::LDAP::ConnectionRefusedError => 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
+
end # class LDAP
diff --git a/lib/net/ldap/auth_adapter.rb b/lib/net/ldap/auth_adapter.rb
new file mode 100644
index 00000000..f74232d1
--- /dev/null
+++ b/lib/net/ldap/auth_adapter.rb
@@ -0,0 +1,29 @@
+module Net
+ class LDAP
+ class AuthAdapter
+ def self.register(names, adapter)
+ names = Array(names)
+ @adapters ||= {}
+ names.each do |name|
+ @adapters[name] = adapter
+ end
+ end
+
+ def self.[](name)
+ a = @adapters[name]
+ if a.nil?
+ raise Net::LDAP::AuthMethodUnsupportedError, "Unsupported auth method (#{name})"
+ end
+ return a
+ end
+
+ def initialize(conn)
+ @connection = conn
+ end
+
+ def bind
+ raise "bind method must be overwritten"
+ end
+ end
+ end
+end
diff --git a/lib/net/ldap/auth_adapter/gss_spnego.rb b/lib/net/ldap/auth_adapter/gss_spnego.rb
new file mode 100644
index 00000000..9f773454
--- /dev/null
+++ b/lib/net/ldap/auth_adapter/gss_spnego.rb
@@ -0,0 +1,41 @@
+require 'net/ldap/auth_adapter'
+require 'net/ldap/auth_adapter/sasl'
+
+module Net
+ class LDAP
+ module AuthAdapers
+ #--
+ # PROVISIONAL, only for testing SASL implementations. DON'T USE THIS YET.
+ # Uses Kohei Kajimoto's Ruby/NTLM. We have to find a clean way to
+ # integrate it without introducing an external dependency.
+ #
+ # This authentication method is accessed by calling #bind with a :method
+ # parameter of :gss_spnego. It requires :username and :password
+ # attributes, just like the :simple authentication method. It performs a
+ # GSS-SPNEGO authentication with the server, which is presumed to be a
+ # Microsoft Active Directory.
+ #++
+ class GSS_SPNEGO < Net::LDAP::AuthAdapter
+ def bind(auth)
+ require 'ntlm'
+
+ user, psw = [auth[:username] || auth[:dn], auth[:password]]
+ raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
+
+ 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
+ end
+ end
+ end
+ end
+end
diff --git a/lib/net/ldap/auth_adapter/sasl.rb b/lib/net/ldap/auth_adapter/sasl.rb
new file mode 100644
index 00000000..139e8593
--- /dev/null
+++ b/lib/net/ldap/auth_adapter/sasl.rb
@@ -0,0 +1,62 @@
+require 'net/ldap/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
+ #
+ # Mechanism is a string value that will be passed in the SASL-packet's
+ # "mechanism" field.
+ #
+ # Initial credential is most likely a string. It's passed in the initial
+ # BindRequest that goes to the server. In some protocols, it may be empty.
+ #
+ # Challenge-response is a Ruby proc that takes a single parameter and
+ # returns an object that will typically be a string. The
+ # challenge-response block is called when the server returns a
+ # BindResponse with a result code of 14 (saslBindInProgress). The
+ # challenge-response block receives a parameter containing the data
+ # returned by the server in the saslServerCreds field of the LDAP
+ # BindResponse packet. The challenge-response block may be called multiple
+ # times during the course of a SASL authentication, and each time it must
+ # return a value that will be passed back to the server as the credential
+ # data in the next BindRequest packet.
+ #++
+ 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)
+
+ message_id = @connection.next_msgid
+
+ n = 0
+ loop do
+ sasl = [mech.to_ber, cred.to_ber].to_ber_contextspecific(3)
+ request = [
+ Net::LDAP::Connection::LdapVersion.to_ber, "".to_ber, sasl
+ ].to_ber_appsequence(Net::LDAP::PDU::BindRequest)
+
+ @connection.send(:write, request, nil, message_id)
+ pdu = @connection.queued_read(message_id)
+
+ if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult
+ raise Net::LDAP::NoBindResultError, "no bind result"
+ end
+
+ return pdu unless pdu.result_code == Net::LDAP::ResultCodeSaslBindInProgress
+ 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
+ end
+ end
+ end
+end
diff --git a/lib/net/ldap/auth_adapter/simple.rb b/lib/net/ldap/auth_adapter/simple.rb
new file mode 100644
index 00000000..d01b57ae
--- /dev/null
+++ b/lib/net/ldap/auth_adapter/simple.rb
@@ -0,0 +1,34 @@
+require 'net/ldap/auth_adapter'
+
+module Net
+ class LDAP
+ class AuthAdapter
+ class Simple < AuthAdapter
+ def bind(auth)
+ user, psw = if auth[:method] == :simple
+ [auth[:username] || auth[:dn], auth[:password]]
+ else
+ ["", ""]
+ end
+
+ raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
+
+ message_id = @connection.next_msgid
+ request = [
+ Net::LDAP::Connection::LdapVersion.to_ber, user.to_ber,
+ psw.to_ber_contextspecific(0)
+ ].to_ber_appsequence(Net::LDAP::PDU::BindRequest)
+
+ @connection.send(:write, request, nil, message_id)
+ pdu = @connection.queued_read(message_id)
+
+ if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult
+ raise Net::LDAP::NoBindResultError, "no bind result"
+ end
+
+ pdu
+ end
+ end
+ end
+ end
+end
diff --git a/lib/net/ldap/connection.rb b/lib/net/ldap/connection.rb
index b51bcc10..b01984f4 100644
--- a/lib/net/ldap/connection.rb
+++ b/lib/net/ldap/connection.rb
@@ -3,29 +3,73 @@
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]
- begin
- @conn = server[:socket] || TCPSocket.new(server[:host], server[:port])
- rescue SocketError
- raise Net::LDAP::Error, "No such address or other socket error."
- rescue Errno::ECONNREFUSED
- raise Net::LDAP::Error, "Server #{server[:host]} refused connection on port #{server[:port]}."
- rescue Errno::EHOSTUNREACH => error
- raise Net::LDAP::Error, "Host #{server[:host]} was unreachable (#{error.message})"
- rescue Errno::ETIMEDOUT
- raise Net::LDAP::Error, "Connection to #{server[:host]} timed out."
- end
+ # Allows tests to parameterize what socket class to use
+ @socket_class = server.fetch(:socket_class, DefaultSocket)
+
+ yield self if block_given?
+ end
- if server[:encryption]
- setup_encryption server[:encryption]
+ def socket_class=(socket_class)
+ @socket_class = socket_class
+ end
+
+ def prepare_socket(server, timeout=nil)
+ socket = server[:socket]
+ encryption = server[:encryption]
+
+ @conn = socket
+ setup_encryption(encryption, timeout) 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: @socket_class.new(host, port, socket_opts)), timeout)
+ 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
+ # Ensure the connection is closed in the event a setup failure.
+ close
+ errors << [e, host, port]
+ end
end
- yield self if block_given?
+ raise Net::LDAP::ConnectionError.new(errors)
end
module GetbyteForSSLSocket
@@ -41,7 +85,7 @@ def close
end
end
- def self.wrap_with_ssl(io, tls_options = {})
+ def self.wrap_with_ssl(io, tls_options = {}, timeout=nil)
raise Net::LDAP::NoOpenSSLError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL
ctx = OpenSSL::SSL::SSLContext.new
@@ -51,7 +95,22 @@ 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
+
+ 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
@@ -63,18 +122,18 @@ def self.wrap_with_ssl(io, tls_options = {})
end
#--
- # Helper method called only from new, and only after we have a
- # successfully-opened @conn instance variable, which is a TCP connection.
- # Depending on the received arguments, we establish SSL, potentially
- # replacing the value of @conn accordingly. Don't generate any errors here
- # if no encryption is requested. DO raise Net::LDAP::Error objects if encryption
- # is requested and we have trouble setting it up. That includes if OpenSSL
- # is not set up on the machine. (Question: how does the Ruby OpenSSL
- # wrapper react in that case?) DO NOT filter exceptions raised by the
- # OpenSSL library. Let them pass back to the user. That should make it
- # easier for us to debug the problem reports. Presumably (hopefully?) that
- # will also produce recognizable errors if someone tries to use this on a
- # machine without OpenSSL.
+ # Helper method called only from prepare_socket or open_connection, and only
+ # after we have a successfully-opened @conn instance variable, which is a TCP
+ # connection. Depending on the received arguments, we establish SSL,
+ # potentially replacing the value of @conn accordingly. Don't generate any
+ # errors here if no encryption is requested. DO raise Net::LDAP::Error objects
+ # if encryption is requested and we have trouble setting it up. That includes
+ # if OpenSSL is not set up on the machine. (Question: how does the Ruby
+ # OpenSSL wrapper react in that case?) DO NOT filter exceptions raised by the
+ # OpenSSL library. Let them pass back to the user. That should make it easier
+ # for us to debug the problem reports. Presumably (hopefully?) that will also
+ # produce recognizable errors if someone tries to use this on a machine
+ # without OpenSSL.
#
# The simple_tls method is intended as the simplest, stupidest, easiest
# solution for people who want nothing more than encrypted comms with the
@@ -88,17 +147,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)
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)
# 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)
@@ -108,11 +167,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)
else
raise Net::LDAP::EncMethodUnsupportedError, "unsupported encryption method #{args[:method]}"
end
@@ -124,6 +181,7 @@ def setup_encryption(args)
# have to call it, but perhaps it will come in handy someday.
#++
def close
+ return if @conn.nil?
@conn.close
@conn = nil
end
@@ -141,12 +199,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
@@ -175,7 +231,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
@@ -205,7 +261,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
@@ -218,130 +274,11 @@ def next_msgid
def bind(auth)
instrument "bind.net_ldap_connection" do |payload|
payload[:method] = meth = auth[:method]
- if [:simple, :anonymous, :anon].include?(meth)
- bind_simple auth
- elsif meth == :sasl
- bind_sasl(auth)
- elsif meth == :gss_spnego
- bind_gss_spnego(auth)
- else
- raise Net::LDAP::AuthMethodUnsupportedError, "Unsupported auth method (#{meth})"
- end
+ adapter = Net::LDAP::AuthAdapter[meth]
+ adapter.new(self).bind(auth)
end
end
- #--
- # Implements a simple user/psw authentication. Accessed by calling #bind
- # with a method of :simple or :anonymous.
- #++
- def bind_simple(auth)
- user, psw = if auth[:method] == :simple
- [auth[:username] || auth[:dn], auth[:password]]
- else
- ["", ""]
- end
-
- raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
-
- message_id = next_msgid
- request = [
- LdapVersion.to_ber, user.to_ber,
- psw.to_ber_contextspecific(0)
- ].to_ber_appsequence(Net::LDAP::PDU::BindRequest)
-
- write(request, nil, message_id)
- pdu = queued_read(message_id)
-
- if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult
- raise Net::LDAP::NoBindResultError, "no bind result"
- end
-
- pdu
- end
-
- #--
- # Required parameters: :mechanism, :initial_credential and
- # :challenge_response
- #
- # Mechanism is a string value that will be passed in the SASL-packet's
- # "mechanism" field.
- #
- # Initial credential is most likely a string. It's passed in the initial
- # BindRequest that goes to the server. In some protocols, it may be empty.
- #
- # Challenge-response is a Ruby proc that takes a single parameter and
- # returns an object that will typically be a string. The
- # challenge-response block is called when the server returns a
- # BindResponse with a result code of 14 (saslBindInProgress). The
- # challenge-response block receives a parameter containing the data
- # returned by the server in the saslServerCreds field of the LDAP
- # BindResponse packet. The challenge-response block may be called multiple
- # times during the course of a SASL authentication, and each time it must
- # return a value that will be passed back to the server as the credential
- # data in the next BindRequest packet.
- #++
- def bind_sasl(auth)
- mech, cred, chall = auth[:mechanism], auth[:initial_credential],
- auth[:challenge_response]
- raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (mech && cred && chall)
-
- message_id = next_msgid
-
- n = 0
- loop {
- sasl = [mech.to_ber, cred.to_ber].to_ber_contextspecific(3)
- request = [
- LdapVersion.to_ber, "".to_ber, sasl
- ].to_ber_appsequence(Net::LDAP::PDU::BindRequest)
-
- write(request, nil, message_id)
- pdu = queued_read(message_id)
-
- if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult
- raise Net::LDAP::NoBindResultError, "no bind result"
- end
-
- return pdu unless pdu.result_code == Net::LDAP::ResultCodeSaslBindInProgress
- raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges)
-
- cred = chall.call(pdu.result_server_sasl_creds)
- }
-
- raise Net::LDAP::SASLChallengeOverflowError, "why are we here?"
- end
- private :bind_sasl
-
- #--
- # PROVISIONAL, only for testing SASL implementations. DON'T USE THIS YET.
- # Uses Kohei Kajimoto's Ruby/NTLM. We have to find a clean way to
- # integrate it without introducing an external dependency.
- #
- # This authentication method is accessed by calling #bind with a :method
- # parameter of :gss_spnego. It requires :username and :password
- # attributes, just like the :simple authentication method. It performs a
- # GSS-SPNEGO authentication with the server, which is presumed to be a
- # Microsoft Active Directory.
- #++
- def bind_gss_spnego(auth)
- require 'ntlm'
-
- user, psw = [auth[:username] || auth[:dn], auth[:password]]
- raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
-
- nego = proc { |challenge|
- t2_msg = NTLM::Message.parse(challenge)
- t3_msg = t2_msg.response({ :user => user, :password => psw },
- { :ntlmv2 => true })
- t3_msg.serialize
- }
-
- bind_sasl(:method => :sasl, :mechanism => "GSS-SPNEGO",
- :initial_credential => NTLM::Message::Type1.new.serialize,
- :challenge_response => nego)
- end
- private :bind_gss_spnego
-
-
#--
# Allow the caller to specify a sort control
#
@@ -366,7 +303,7 @@ def encode_sort_controls(sort_definitions)
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
@@ -463,12 +400,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 = [
@@ -479,7 +415,7 @@ 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
@@ -492,7 +428,7 @@ def search(args = nil)
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)
@@ -531,6 +467,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
@@ -586,20 +526,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
@@ -618,7 +558,7 @@ 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)
@@ -631,6 +571,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::ResponseMissingError, "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
@@ -641,9 +626,9 @@ 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)
@@ -699,4 +684,33 @@ def delete(args)
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
+
+ # 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..9027ed28 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)
@@ -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
diff --git a/lib/net/ldap/dn.rb b/lib/net/ldap/dn.rb
index 3037eefd..e314b80e 100644
--- a/lib/net/ldap/dn.rb
+++ b/lib/net/ldap/dn.rb
@@ -169,11 +169,10 @@ def each_pair
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 "DN badly formed" unless
+ [:value, :value_normal, :value_hexstring, :value_end].include? state
+
+ yield key.string.strip, value.string.rstrip
end
##
diff --git a/lib/net/ldap/entry.rb b/lib/net/ldap/entry.rb
index c2615268..10965c7c 100644
--- a/lib/net/ldap/entry.rb
+++ b/lib/net/ldap/entry.rb
@@ -140,11 +140,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
diff --git a/lib/net/ldap/error.rb b/lib/net/ldap/error.rb
index c9a25f90..50442d06 100644
--- a/lib/net/ldap/error.rb
+++ b/lib/net/ldap/error.rb
@@ -9,7 +9,42 @@ class Error < StandardError; end
class AlreadyOpenedError < Error; end
class SocketError < Error; end
- class ConnectionRefusedError < 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 Net::LDAP::Error.new(error.message)
+ end
+
+ super
+ end
+
+ def initialize(errors)
+ message = "Unable to connect to any given server: \n #{errors.map { |e, h, p| "#{e.class}: #{e.message} (#{h}:#{p})" }.join("\n ")}"
+ super(message)
+ end
+ end
class NoOpenSSLError < Error; end
class NoStartTLSResultError < Error; end
class NoSearchBaseError < Error; end
diff --git a/lib/net/ldap/filter.rb b/lib/net/ldap/filter.rb
index 0ab847b8..6f064488 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"
@@ -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
@@ -752,7 +759,7 @@ def parse_filter_branch(scanner)
scanner.scan(/\s*/)
if op = scanner.scan(/<=|>=|!=|:=|=/)
scanner.scan(/\s*/)
- if value = scanner.scan(/(?:[-\[\]{}\w*.+:@=,#\$%&!'^~\s\xC3\x80-\xCA\xAF]|[^\x00-\x7F]|\\[a-fA-F\d]{2})+/u)
+ if value = scanner.scan(/(?:[-\[\]{}\w*.+\/:@=,#\$%&!'^~\s\xC3\x80-\xCA\xAF]|[^\x00-\x7F]|\\[a-fA-F\d]{2})+/u)
# 20100313 AZ: Assumes that "(uid=george*)" is the same as
# "(uid=george* )". The standard doesn't specify, but I can find
# no examples that suggest otherwise.
diff --git a/lib/net/ldap/pdu.rb b/lib/net/ldap/pdu.rb
index f749f669..382c7acb 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,7 +121,7 @@ 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}")
end
@@ -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 >= 3 or raise Net::LDAP::PDU::Error, "Invalid LDAP result length."
+ @ldap_result = {
+ :resultCode => sequence[0],
+ :matchedDN => sequence[1],
+ :errorMessage => sequence[2],
+ }
+ @extended_response = sequence[3]
+ 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 98d557cf..0a57d621 100644
--- a/lib/net/ldap/version.rb
+++ b/lib/net/ldap/version.rb
@@ -1,5 +1,5 @@
module Net
class LDAP
- VERSION = "0.11"
+ VERSION = "0.16.1"
end
end
diff --git a/lib/net/snmp.rb b/lib/net/snmp.rb
index 501df851..258e8060 100644
--- a/lib/net/snmp.rb
+++ b/lib/net/snmp.rb
@@ -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..7516759b 100644
--- a/net-ldap.gemspec
+++ b/net-ldap.gemspec
@@ -26,11 +26,12 @@ the most recent LDAP RFCs (4510-4519, plutions of 4520-4532).}
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 = ">= 2.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("rubocop", "~> 0.42.0")
s.add_development_dependency("test-unit")
+ s.add_development_dependency("byebug")
end
diff --git a/script/changelog b/script/changelog
new file mode 100755
index 00000000..cda2ad83
--- /dev/null
+++ b/script/changelog
@@ -0,0 +1,47 @@
+#!/bin/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.
+# base: git ref to compare from. e.g. "v1.3.1". Defaults to latest git tag.
+# head: git ref to compare to. Defaults to "HEAD".
+#
+# Generate a changelog preview from pull requests merged between `base` and
+# `head`.
+#
+# https://github.com/jch/release-scripts/blob/master/changelog
+set -e
+
+[ $# -eq 0 ] && set -- --help
+while [[ $# > 1 ]]
+do
+ key="$1"
+ case $key in
+ -r|--repo)
+ repo="$2"
+ shift
+ ;;
+ -b|--base)
+ base="$2"
+ shift
+ ;;
+ -h|--head)
+ head="$2"
+ shift
+ ;;
+ *)
+ ;;
+ esac
+ shift
+done
+
+repo="${repo:-$(git remote -v | grep push | awk '{print $2}' | cut -d'/' -f4- | sed 's/\.git//')}"
+base="${base:-$(git tag -l | sort -t. -k 1,1n -k 2,2n -k 3,3n | tail -n 1)}"
+head="${head:-HEAD}"
+api_url="https://api.github.com"
+
+# get merged PR's. Better way is to query the API for these, but this is easier
+for pr in $(git log --oneline $base..$head | grep "Merge pull request" | awk '{gsub("#",""); print $5}')
+do
+ # frustrated with trying to pull out the right values, fell back to ruby
+ curl -s "$api_url/repos/$repo/pulls/$pr" | ruby -rjson -e 'pr=JSON.parse(STDIN.read); puts "* #{pr[%q(title)]} {##{pr[%q(number)]}}[#{pr[%q(html_url)]}]"'
+done
diff --git a/script/generate-fixture-ca b/script/generate-fixture-ca
new file mode 100755
index 00000000..89eb3d8d
--- /dev/null
+++ b/script/generate-fixture-ca
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+BASE_PATH=$( cd "`dirname $0`/../test/fixtures/ca" && pwd )
+cd "${BASE_PATH}" || exit 4
+
+USAGE=$( cat << EOS
+Usage:
+ $0 --regenerate
+
+Generates a new self-signed CA, for integration testing. This should only need
+to be run if you are writing new TLS/SSL tests, and need to generate
+additional fixtuer CAs.
+
+This script uses the GnuTLS certtool CLI. If you are on macOS,
+'brew install gnutls', and it will be installed as 'gnutls-certtool'.
+Apple unfortunately ships with an incompatible /usr/bin/certtool that does
+different things.
+EOS
+)
+
+if [ "x$1" != 'x--regenerate' ]; then
+ echo "${USAGE}"
+ exit 1
+fi
+
+TOOL=`type -p certtool`
+if [ "$(uname)" = "Darwin" ]; then
+ TOOL=`type -p gnutls-certtool`
+ if [ ! -x "${TOOL}" ]; then
+ echo "Sorry, Darwin requires gnutls-certtool; try `brew install gnutls`"
+ exit 2
+ fi
+fi
+
+if [ ! -x "${TOOL}" ]; then
+ echo "Sorry, no certtool found!"
+ exit 3
+fi
+export TOOL
+
+
+${TOOL} --generate-privkey > ./cakey.pem
+${TOOL} --generate-self-signed \
+ --load-privkey ./cakey.pem \
+ --template ./ca.info \
+ --outfile ./cacert.pem
+
+echo "cert and private key generated! Don't forget to check them in"
diff --git a/script/install-openldap b/script/install-openldap
index b9efac98..3e391d87 100755
--- a/script/install-openldap
+++ b/script/install-openldap
@@ -2,8 +2,8 @@
set -e
set -x
-BASE_PATH="$( cd `dirname $0`/../test/fixtures/openldap && pwd )"
-SEED_PATH="$( cd `dirname $0`/../test/fixtures && pwd )"
+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 && \
@@ -48,47 +48,58 @@ chown -R openldap.openldap /var/lib/ldap
rm -rf $TMPDIR
# SSL
+export CA_CERT="/usr/local/share/ca-certificates/rubyldap-ca.crt"
+export CA_KEY="/etc/ssl/private/rubyldap-ca.key"
-sh -c "certtool --generate-privkey > /etc/ssl/private/cakey.pem"
+# The self-signed fixture CA cert & key are generated by
+# `script/generate-fiuxture-ca` and checked into version control.
+# You shouldn't need to muck with these unless you're writing more
+# TLS/SSL integration tests, and need special magic values in the cert.
-sh -c "cat > /etc/ssl/ca.info < /etc/ssl/ldap01.info <> /etc/ssl/ldap01.info
+done
+
# Create the server certificate
certtool --generate-certificate \
--load-privkey /etc/ssl/private/ldap01_slapd_key.pem \
- --load-ca-certificate /etc/ssl/certs/cacert.pem \
- --load-ca-privkey /etc/ssl/private/cakey.pem \
+ --load-ca-certificate "${CA_CERT}" \
+ --load-ca-privkey "${CA_KEY}" \
--template /etc/ssl/ldap01.info \
--outfile /etc/ssl/certs/ldap01_slapd_cert.pem
ldapmodify -Y EXTERNAL -H ldapi:/// <> /etc/hosts
+grep ldap02 /etc/hosts || echo "127.0.0.1 ldap02.example.com" >> /etc/hosts
+grep bogus /etc/hosts || echo "127.0.0.1 bogus.example.com" >> /etc/hosts
+
service slapd restart
diff --git a/test/ber/core_ext/test_array.rb b/test/ber/core_ext/test_array.rb
index 308fffc5..2d1e957a 100644
--- a/test/ber/core_ext/test_array.rb
+++ b/test/ber/core_ext/test_array.rb
@@ -6,7 +6,7 @@ def test_control_code_array
control_codes << ['1.2.3'.to_ber, true.to_ber].to_ber_sequence
control_codes << ['1.7.9'.to_ber, false.to_ber].to_ber_sequence
control_codes = control_codes.to_ber_sequence
- res = [['1.2.3', true],['1.7.9',false]].to_ber_control
+ res = [['1.2.3', true], ['1.7.9', false]].to_ber_control
assert_equal control_codes, res
end
diff --git a/test/ber/test_ber.rb b/test/ber/test_ber.rb
index 92b3902d..5d5c1266 100644
--- a/test/ber/test_ber.rb
+++ b/test/ber/test_ber.rb
@@ -6,8 +6,8 @@ def test_empty_array
end
def test_array
- ary = [1,2,3]
- encoded_ary = ary.map { |el| el.to_ber }.to_ber
+ ary = [1, 2, 3]
+ encoded_ary = ary.map(&:to_ber).to_ber
assert_equal ary, encoded_ary.read_ber
end
@@ -135,7 +135,15 @@ def test_ascii_data_in_utf8
assert_equal "UTF-8", bis.encoding.name
end
- def test_ut8_data_in_utf8
+ def test_umlaut_data_in_utf8
+ data = "Müller".force_encoding("UTF-8")
+ bis = Net::BER::BerIdentifiedString.new(data)
+
+ assert bis.valid_encoding?, "should be a valid encoding"
+ assert_equal "UTF-8", bis.encoding.name
+ end
+
+ def test_utf8_data_in_utf8
data = ["e4b8ad"].pack("H*").force_encoding("UTF-8")
bis = Net::BER::BerIdentifiedString.new(data)
diff --git a/test/fixtures/ca/ca.info b/test/fixtures/ca/ca.info
new file mode 100644
index 00000000..c0fd3629
--- /dev/null
+++ b/test/fixtures/ca/ca.info
@@ -0,0 +1,4 @@
+cn = rubyldap
+ca
+cert_signing_key
+expiration_days = 7200
diff --git a/test/fixtures/ca/cacert.pem b/test/fixtures/ca/cacert.pem
new file mode 100644
index 00000000..0218dd8a
--- /dev/null
+++ b/test/fixtures/ca/cacert.pem
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIID7zCCAlegAwIBAgIMV7zWei6SNfABx6jMMA0GCSqGSIb3DQEBCwUAMBMxETAP
+BgNVBAMTCHJ1YnlsZGFwMB4XDTE2MDgyMzIzMDQyNloXDTM2MDUxMDIzMDQyNlow
+EzERMA8GA1UEAxMIcnVieWxkYXAwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGK
+AoIBgQDGe9wziGHZJhIf+IEKSk1tpT9Mu7YgsUwjrlutvkoO1Q6K+amTAVDXizPf
+1DVSDpZP5+CfBOznhgLMsPvrQ02w4qx5/6X9L+zJcMk8jTNYSKj5uIKpK52E7Uok
+aygMXeaqroPONGkoJIZiVGgdbWfTvcffTm8FOhztXUbMrMXJNinFsocGHEoMNN8b
+vqgAyG4+DFHoK4L0c6eQjE4nZBChieZdShUhaBpV7r2qSNbPw67cvAKuEzml58mV
+1ZF1F73Ua8gPWXHEfUe2GEfG0NnRq6sGbsDYe/DIKxC7AZ89udZF3WZXNrPhvXKj
+ZT7njwcMQemns4dNPQ0k2V4vAQ8pD8r8Qvb65FiSopUhVaGQswAnIMS1DnFq88AQ
+KJTKIXbBuMwuaNNSs6R/qTS2RDk1w+CGpRXAg7+1SX5NKdrEsu1IaABA/tQ/zKKk
+OLLJaD0giX1weBVmNeFcKxIoT34VS59eEt5APmPcguJnx+aBrA9TLzSO788apBN0
+4lGAmR0CAwEAAaNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwQA
+MB0GA1UdDgQWBBRTvXSkge03oqLu7UUjFI+oLYwnujANBgkqhkiG9w0BAQsFAAOC
+AYEATSZQWH+uSN5GvOUvJ8LHWkeVovn0UhboK0K7GzmMeGz+dp/Xrj6eQ4ONK0zI
+RCJyoo/nCR7CfQ5ujVXr03XD2SUgyD565ulXuhw336DasL5//fucmQYDeqhwbKML
+FTzsF9H9dO4J5TjxJs7e5dRJ0wrP/XEY+WFhXXdSHTl8vGCI6QqWc7TvDpmbS4iX
+uTzjJswu9Murt9JUJNMN2DlDi/vBBeruaj4c2cMMnKMvkfj14kd8wMocmzj+gVQl
+r+fRQbKAJNec65lA4/Zeb6sD9SAi0ZIVgxA4a7g8/sdNWHIAxPicpJkIJf30TsyY
+F+8+Hd5mBtCbvFfAVkT6bHBP1OiAgNke+Rh/j/sQbyWbKCKw0+jpFJgO9KUNGfC0
+O/CqX+J4G7HqL8VJqrLnBvOdhfetAvNQtf1gcw5ZwpeEFM+Kvx/lsILaIYdAUSjX
+ePOc5gI2Bi9WXq+T9AuhSf+TWUR874m/rdTWe5fM8mXCNl7C4I5zCqLltEDkSoMP
+jDj/
+-----END CERTIFICATE-----
diff --git a/test/fixtures/ca/cakey.pem b/test/fixtures/ca/cakey.pem
new file mode 100644
index 00000000..d75ab299
--- /dev/null
+++ b/test/fixtures/ca/cakey.pem
@@ -0,0 +1,190 @@
+Public Key Info:
+ Public Key Algorithm: RSA
+ Key Security Level: High (3072 bits)
+
+modulus:
+ 00:c6:7b:dc:33:88:61:d9:26:12:1f:f8:81:0a:4a:4d
+ 6d:a5:3f:4c:bb:b6:20:b1:4c:23:ae:5b:ad:be:4a:0e
+ d5:0e:8a:f9:a9:93:01:50:d7:8b:33:df:d4:35:52:0e
+ 96:4f:e7:e0:9f:04:ec:e7:86:02:cc:b0:fb:eb:43:4d
+ b0:e2:ac:79:ff:a5:fd:2f:ec:c9:70:c9:3c:8d:33:58
+ 48:a8:f9:b8:82:a9:2b:9d:84:ed:4a:24:6b:28:0c:5d
+ e6:aa:ae:83:ce:34:69:28:24:86:62:54:68:1d:6d:67
+ d3:bd:c7:df:4e:6f:05:3a:1c:ed:5d:46:cc:ac:c5:c9
+ 36:29:c5:b2:87:06:1c:4a:0c:34:df:1b:be:a8:00:c8
+ 6e:3e:0c:51:e8:2b:82:f4:73:a7:90:8c:4e:27:64:10
+ a1:89:e6:5d:4a:15:21:68:1a:55:ee:bd:aa:48:d6:cf
+ c3:ae:dc:bc:02:ae:13:39:a5:e7:c9:95:d5:91:75:17
+ bd:d4:6b:c8:0f:59:71:c4:7d:47:b6:18:47:c6:d0:d9
+ d1:ab:ab:06:6e:c0:d8:7b:f0:c8:2b:10:bb:01:9f:3d
+ b9:d6:45:dd:66:57:36:b3:e1:bd:72:a3:65:3e:e7:8f
+ 07:0c:41:e9:a7:b3:87:4d:3d:0d:24:d9:5e:2f:01:0f
+ 29:0f:ca:fc:42:f6:fa:e4:58:92:a2:95:21:55:a1:90
+ b3:00:27:20:c4:b5:0e:71:6a:f3:c0:10:28:94:ca:21
+ 76:c1:b8:cc:2e:68:d3:52:b3:a4:7f:a9:34:b6:44:39
+ 35:c3:e0:86:a5:15:c0:83:bf:b5:49:7e:4d:29:da:c4
+ b2:ed:48:68:00:40:fe:d4:3f:cc:a2:a4:38:b2:c9:68
+ 3d:20:89:7d:70:78:15:66:35:e1:5c:2b:12:28:4f:7e
+ 15:4b:9f:5e:12:de:40:3e:63:dc:82:e2:67:c7:e6:81
+ ac:0f:53:2f:34:8e:ef:cf:1a:a4:13:74:e2:51:80:99
+ 1d:
+
+public exponent:
+ 01:00:01:
+
+private exponent:
+ 1d:0d:9a:50:ec:c0:ad:e1:75:bb:ba:4b:61:2f:39:20
+ 38:95:08:6d:5d:9e:71:75:5c:af:b3:f9:bd:a5:e7:7f
+ e6:4e:0f:77:73:ee:38:60:24:9f:26:3f:50:c2:bf:21
+ df:76:68:99:be:45:d3:29:f9:94:ee:bf:21:53:cb:b6
+ 7d:a7:93:80:09:53:03:45:dc:c2:a6:a2:37:64:f1:a2
+ 49:21:ac:91:6b:a3:d7:bd:d2:62:0c:ec:a6:83:10:e7
+ a7:ca:3d:be:dc:4b:1c:36:24:79:96:33:5b:43:5d:74
+ 50:0e:46:b0:9b:6d:9f:71:06:89:a5:c8:65:ed:d9:a3
+ 15:00:3c:3e:a9:75:50:9d:72:cb:c9:aa:e1:ba:a3:9c
+ 07:77:14:32:30:d4:4d:65:f4:7c:23:1d:79:84:9b:2e
+ 9a:19:df:43:ed:cd:e3:08:1f:d5:ff:6b:42:98:36:f7
+ 44:cc:48:b4:f7:b8:16:b3:23:37:8d:b8:22:3f:8a:86
+ db:71:b3:85:2d:6d:42:44:b7:dc:c1:36:e0:c4:0f:fe
+ cb:76:84:81:e2:83:f5:82:76:a9:7b:35:d5:44:00:d1
+ 1a:fc:ef:b9:a4:2b:62:aa:f8:56:eb:60:e5:16:33:f1
+ 28:e1:da:91:50:e3:a4:c7:d6:30:21:cf:04:07:cd:8c
+ b6:9e:b0:a7:6c:96:57:2e:09:5b:39:26:d0:60:be:e3
+ 90:59:a3:8e:e7:6e:3f:62:7e:b4:2a:e1:8f:00:37:7a
+ 83:9e:7a:9c:d2:ae:ba:50:84:73:65:3a:64:95:d8:48
+ f9:fd:0e:c3:5b:6e:08:3b:c5:c9:1c:29:55:bb:67:e8
+ fa:50:40:30:2a:d1:b7:cf:54:a8:f0:f0:76:89:ad:19
+ e7:a0:3a:56:6c:75:c5:bc:d8:46:ce:1e:66:f2:61:96
+ 11:e4:57:cc:52:ff:e4:ed:6b:2c:ce:78:15:ba:b7:ed
+ 31:f2:68:88:79:bf:7c:29:3c:2f:66:71:0b:09:b7:41
+
+
+prime1:
+ 00:fd:c2:37:b9:6f:77:88:51:a2:f7:4f:c2:3c:a4:57
+ bf:ba:71:14:f3:61:f4:39:78:22:3d:bc:d8:d2:4e:c0
+ 4b:9e:c2:6d:38:a8:21:e2:70:1a:96:48:95:18:85:01
+ 46:fb:62:a4:81:09:f8:2a:3a:87:78:07:5d:93:54:ce
+ 2a:51:b3:51:6f:61:0a:2e:9d:b0:51:37:e3:13:bd:81
+ 23:2b:61:53:fa:ac:08:dc:a0:e6:63:a3:b0:cc:cf:73
+ 1d:65:b7:11:bc:29:70:fb:72:ea:63:9d:67:02:d6:35
+ 24:13:1d:bc:72:fb:9e:3d:ab:0b:57:6e:bd:a1:51:56
+ f9:bc:96:15:74:a3:31:16:c6:b8:98:1b:0a:a2:59:7c
+ c8:b7:14:b8:5b:f3:2e:26:b4:f0:46:c4:3d:27:dd:41
+ 31:52:a7:15:a8:af:6a:98:a5:9c:20:17:f9:1d:54:54
+ ff:10:91:a3:a5:ca:ac:63:e7:16:2b:71:3c:3a:cd:4f
+ ed:
+
+prime2:
+ 00:c8:3c:a8:9f:8a:db:42:b5:8d:cf:2a:a1:2f:e5:73
+ 05:de:30:d8:17:b9:5c:9d:08:60:02:c9:66:9d:88:50
+ ac:cd:0f:b5:47:b4:a8:73:3b:7d:65:79:bf:4c:6f:d0
+ e2:03:ed:d4:28:4e:00:07:23:00:01:4f:05:de:9b:44
+ 1a:84:ae:09:4a:d6:ed:61:5d:77:e2:fa:13:99:4c:b7
+ 76:72:3d:f8:53:93:69:78:e8:bd:26:cb:b0:f9:01:f4
+ 1d:20:4f:60:f5:ab:3c:19:85:73:34:f3:ec:d2:67:ef
+ 56:b8:5d:93:73:8e:d9:3e:28:ff:87:f5:4a:26:fa:b1
+ ae:c6:d3:9d:03:e3:fd:c2:24:48:af:85:2a:8e:3b:5b
+ 93:07:38:91:21:ae:49:cb:6d:e3:30:81:15:ed:65:eb
+ dc:01:df:3b:9d:43:fd:a6:e1:df:ef:ad:22:42:34:f1
+ 3f:81:5e:57:0a:e0:56:94:f2:2a:00:d0:cc:c5:50:67
+ f1:
+
+coefficient:
+ 00:bd:23:8c:2e:a7:7b:6b:1e:85:77:db:7d:77:f6:e5
+ b0:15:c6:e1:9e:35:57:72:df:35:6d:93:89:7f:83:9f
+ 63:7f:08:0a:b3:d4:ba:63:9b:10:7f:0f:d3:55:e9:38
+ cf:90:37:3d:85:3d:a7:97:8c:33:f2:c2:b1:38:2b:db
+ 39:ca:a8:d0:23:d7:89:cc:8d:02:7d:61:9b:b6:04:69
+ 14:e8:c9:84:34:36:6c:fb:84:58:cc:9a:53:74:a4:42
+ bd:1d:25:1b:ba:82:c0:fb:23:2c:90:bb:35:4b:5b:b0
+ 98:d0:ab:9d:61:6e:ea:e8:84:e7:a7:6c:ae:1b:2c:00
+ cb:0f:1a:f8:e2:7c:fd:42:1a:e2:13:52:c7:50:fa:65
+ c9:5f:ed:40:a8:7f:46:0e:ce:f6:56:83:6f:0e:8e:39
+ f8:33:5f:83:de:be:be:ef:8c:66:ad:16:c8:ec:98:d4
+ b2:b2:55:66:a2:9e:27:6a:84:f1:31:07:e8:bf:a7:a7
+ bd:
+
+exp1:
+ 00:b6:50:0c:53:19:07:8b:14:03:fe:a4:fa:0b:31:93
+ ad:b7:18:b9:91:a6:c5:9d:68:77:49:5d:dd:75:33:89
+ 2a:8b:54:6a:be:32:e5:ad:57:17:72:f3:90:d2:fd:f4
+ 0d:f8:5c:45:8e:44:08:5c:e6:92:1f:a5:43:10:af:f4
+ 33:29:61:a8:d7:59:a3:c4:1c:1c:ea:2d:39:e3:1b:da
+ a4:d6:ec:e5:36:0a:d5:8f:15:b6:90:cd:b1:1f:64:c7
+ f2:cd:fa:3a:2e:b2:a3:6e:b4:80:3b:b3:81:a7:e3:18
+ 68:e3:a7:10:96:97:ba:77:d9:e4:9b:1b:7f:f8:5f:85
+ 1a:85:e8:5a:5f:e3:43:48:76:db:76:c4:ae:de:37:66
+ d4:99:dc:b4:1b:b3:da:6b:8a:c1:ba:46:11:1e:0b:f3
+ 63:a9:5b:4b:cf:56:c0:42:0d:71:df:08:fa:3c:9d:33
+ 37:d1:c2:a1:0d:63:50:79:b2:34:16:60:13:82:b7:b1
+ 7d:
+
+exp2:
+ 00:98:38:2c:c4:24:4e:2c:b7:52:17:a4:43:a6:e2:99
+ ff:62:fa:e4:bb:9c:49:40:83:66:61:97:f3:af:5c:3a
+ 60:32:ff:77:03:0c:de:65:c3:5a:bf:72:bf:2f:7f:6d
+ 5e:f4:37:af:69:f8:69:e3:03:03:74:fb:3a:ee:10:40
+ c4:9c:0a:a5:bb:c4:09:ef:53:9b:d8:eb:dd:4c:53:da
+ c0:6b:76:9a:ba:06:3d:4f:12:37:01:30:25:d8:16:59
+ 1a:6f:3e:88:ea:19:83:75:af:52:76:75:dc:99:d3:33
+ 4a:4c:9b:ae:85:51:99:ea:bc:46:0d:78:36:27:cd:ba
+ 97:b0:44:9c:7f:a1:a9:7e:16:11:3f:85:4f:65:92:d0
+ 39:c4:6a:87:42:00:79:ce:f1:39:9d:dc:f3:eb:65:e8
+ d8:76:7f:da:94:e2:64:08:a2:7b:97:7b:99:a8:95:10
+ b5:03:46:d1:8a:ce:22:63:d6:78:81:e8:39:52:e2:9e
+ 31:
+
+
+Public Key ID: 53:BD:74:A4:81:ED:37:A2:A2:EE:ED:45:23:14:8F:A8:2D:8C:27:BA
+Public key's random art:
++--[ RSA 3072]----+
+| . o. . |
+| . +...+ |
+| . o o.+ . |
+| o o . . .ooo |
+| o = . S o..o . |
+| . o . .+.. |
+|. . .. |
+| . .. . |
+|E oo.o |
++-----------------+
+
+-----BEGIN RSA PRIVATE KEY-----
+MIIG5QIBAAKCAYEAxnvcM4hh2SYSH/iBCkpNbaU/TLu2ILFMI65brb5KDtUOivmp
+kwFQ14sz39Q1Ug6WT+fgnwTs54YCzLD760NNsOKsef+l/S/syXDJPI0zWEio+biC
+qSudhO1KJGsoDF3mqq6DzjRpKCSGYlRoHW1n073H305vBToc7V1GzKzFyTYpxbKH
+BhxKDDTfG76oAMhuPgxR6CuC9HOnkIxOJ2QQoYnmXUoVIWgaVe69qkjWz8Ou3LwC
+rhM5pefJldWRdRe91GvID1lxxH1HthhHxtDZ0aurBm7A2HvwyCsQuwGfPbnWRd1m
+Vzaz4b1yo2U+548HDEHpp7OHTT0NJNleLwEPKQ/K/EL2+uRYkqKVIVWhkLMAJyDE
+tQ5xavPAECiUyiF2wbjMLmjTUrOkf6k0tkQ5NcPghqUVwIO/tUl+TSnaxLLtSGgA
+QP7UP8yipDiyyWg9IIl9cHgVZjXhXCsSKE9+FUufXhLeQD5j3ILiZ8fmgawPUy80
+ju/PGqQTdOJRgJkdAgMBAAECggGAHQ2aUOzAreF1u7pLYS85IDiVCG1dnnF1XK+z
++b2l53/mTg93c+44YCSfJj9Qwr8h33Zomb5F0yn5lO6/IVPLtn2nk4AJUwNF3MKm
+ojdk8aJJIayRa6PXvdJiDOymgxDnp8o9vtxLHDYkeZYzW0NddFAORrCbbZ9xBoml
+yGXt2aMVADw+qXVQnXLLyarhuqOcB3cUMjDUTWX0fCMdeYSbLpoZ30PtzeMIH9X/
+a0KYNvdEzEi097gWsyM3jbgiP4qG23GzhS1tQkS33ME24MQP/st2hIHig/WCdql7
+NdVEANEa/O+5pCtiqvhW62DlFjPxKOHakVDjpMfWMCHPBAfNjLaesKdsllcuCVs5
+JtBgvuOQWaOO524/Yn60KuGPADd6g556nNKuulCEc2U6ZJXYSPn9DsNbbgg7xckc
+KVW7Z+j6UEAwKtG3z1So8PB2ia0Z56A6Vmx1xbzYRs4eZvJhlhHkV8xS/+TtayzO
+eBW6t+0x8miIeb98KTwvZnELCbdBAoHBAP3CN7lvd4hRovdPwjykV7+6cRTzYfQ5
+eCI9vNjSTsBLnsJtOKgh4nAalkiVGIUBRvtipIEJ+Co6h3gHXZNUzipRs1FvYQou
+nbBRN+MTvYEjK2FT+qwI3KDmY6OwzM9zHWW3EbwpcPty6mOdZwLWNSQTHbxy+549
+qwtXbr2hUVb5vJYVdKMxFsa4mBsKoll8yLcUuFvzLia08EbEPSfdQTFSpxWor2qY
+pZwgF/kdVFT/EJGjpcqsY+cWK3E8Os1P7QKBwQDIPKifittCtY3PKqEv5XMF3jDY
+F7lcnQhgAslmnYhQrM0PtUe0qHM7fWV5v0xv0OID7dQoTgAHIwABTwXem0QahK4J
+StbtYV134voTmUy3dnI9+FOTaXjovSbLsPkB9B0gT2D1qzwZhXM08+zSZ+9WuF2T
+c47ZPij/h/VKJvqxrsbTnQPj/cIkSK+FKo47W5MHOJEhrknLbeMwgRXtZevcAd87
+nUP9puHf760iQjTxP4FeVwrgVpTyKgDQzMVQZ/ECgcEAtlAMUxkHixQD/qT6CzGT
+rbcYuZGmxZ1od0ld3XUziSqLVGq+MuWtVxdy85DS/fQN+FxFjkQIXOaSH6VDEK/0
+MylhqNdZo8QcHOotOeMb2qTW7OU2CtWPFbaQzbEfZMfyzfo6LrKjbrSAO7OBp+MY
+aOOnEJaXunfZ5Jsbf/hfhRqF6Fpf40NIdtt2xK7eN2bUmdy0G7Paa4rBukYRHgvz
+Y6lbS89WwEINcd8I+jydMzfRwqENY1B5sjQWYBOCt7F9AoHBAJg4LMQkTiy3Uhek
+Q6bimf9i+uS7nElAg2Zhl/OvXDpgMv93AwzeZcNav3K/L39tXvQ3r2n4aeMDA3T7
+Ou4QQMScCqW7xAnvU5vY691MU9rAa3aaugY9TxI3ATAl2BZZGm8+iOoZg3WvUnZ1
+3JnTM0pMm66FUZnqvEYNeDYnzbqXsEScf6GpfhYRP4VPZZLQOcRqh0IAec7xOZ3c
+8+tl6Nh2f9qU4mQIonuXe5molRC1A0bRis4iY9Z4geg5UuKeMQKBwQC9I4wup3tr
+HoV323139uWwFcbhnjVXct81bZOJf4OfY38ICrPUumObEH8P01XpOM+QNz2FPaeX
+jDPywrE4K9s5yqjQI9eJzI0CfWGbtgRpFOjJhDQ2bPuEWMyaU3SkQr0dJRu6gsD7
+IyyQuzVLW7CY0KudYW7q6ITnp2yuGywAyw8a+OJ8/UIa4hNSx1D6Zclf7UCof0YO
+zvZWg28Ojjn4M1+D3r6+74xmrRbI7JjUsrJVZqKeJ2qE8TEH6L+np70=
+-----END RSA PRIVATE KEY-----
diff --git a/test/fixtures/cacert.pem b/test/fixtures/cacert.pem
deleted file mode 100644
index f8b134e1..00000000
--- a/test/fixtures/cacert.pem
+++ /dev/null
@@ -1,20 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDRzCCAf+gAwIBAgIEVHpbmjANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwhy
-dWJ5bGRhcDAeFw0xNDExMjkyMzQ5NDZaFw0xNTExMjkyMzQ5NDZaMBMxETAPBgNV
-BAMTCHJ1YnlsZGFwMIIBUjANBgkqhkiG9w0BAQEFAAOCAT8AMIIBOgKCATEA4pKe
-cDCNuL53fkpO/WSAS+gmMTsOs+oOK71kZlk2QT/MBz8TxC6m358qCADjnXcMVVxa
-ySQbQlVKZMkIvLNciZbiLDgC5II0NbHACNa8rqenoKRjS4J9W3OhA8EmnXn/Me+8
-uMCI9tfnKNRZYdkQZlra4I+Idn+xYfl/5q5b/7ZjPS2zY/585hFEYE+5vfOZVBSU
-3HMNSeuJvTehLv7dD7aQfXNM4cRgHXequkJQ/HLLFAO4AgJ+LJrFWpj7GWz3crgr
-9G5px4T78wJH3NQiOsG6UBXPw8c4T+Z6GAWX2l1zs1gZsaiCVbAraqK3404lL7yp
-+ThbsW3ifzgNPhmjScXBLdbEDrrAKosW7kkTOGzxiMCBmNlj2SKhcztoduAtfF1f
-Fs2Jk8MRTHwO8ThD7wIDAQABo0MwQTAPBgNVHRMBAf8EBTADAQH/MA8GA1UdDwEB
-/wQFAwMHBAAwHQYDVR0OBBYEFJDm67ekyFu4/Z7VcO6Vk/5pinGcMA0GCSqGSIb3
-DQEBCwUAA4IBMQDHeEPzfYRtjynpUKyrtxx/6ZVOfCLuz4eHkBZggz/pJacDCv/a
-I//W03XCk8RWq/fWVVUzvxXgPwnYcw992PLM7XW81zp6ruRUDWooYnjHZZz3bRhe
-kC4QvM2mZhcsMVmhmWWKZn81qXgVdUY1XNRhk87cuXjF/UTpEieFvWAsCUkFZkqB
-AmySCuI/FuPaauT1YAltkIlYAEIGNJGZDMf2BTVUQpXhTXeS9/AZWLNDBwiq+fwo
-YYnsr9MnBXCEmg1gVSR/Ay2AZmbYfiYtb5kU8uq2lSWAUb4LX6HZl82wo3OilrJ2
-WXl6Qf+Fcy4qqkRt4AKHjtzizpEDCOVYuuG0Zoy+QnxNXRsEzpb8ymnJFrcgYfk/
-6Lv2gWAFl5FqCZp7gBWg55eL2coT4C+mbNTF
------END CERTIFICATE-----
diff --git a/test/fixtures/openldap/slapd.conf.ldif b/test/fixtures/openldap/slapd.conf.ldif
index 6ba5cf77..77a6af09 100644
--- a/test/fixtures/openldap/slapd.conf.ldif
+++ b/test/fixtures/openldap/slapd.conf.ldif
@@ -3,7 +3,7 @@ objectClass: olcGlobal
cn: config
olcPidFile: /var/run/slapd/slapd.pid
olcArgsFile: /var/run/slapd/slapd.args
-olcLogLevel: none
+olcLogLevel: -1
olcToolThreads: 1
dn: olcDatabase={-1}frontend,cn=config
diff --git a/test/integration/test_add.rb b/test/integration/test_add.rb
index 3cddb18a..dcac6149 100644
--- a/test/integration/test_add.rb
+++ b/test/integration/test_add.rb
@@ -14,7 +14,7 @@ def test_add
uid: "added-user1",
cn: "added-user1",
sn: "added-user1",
- mail: "added-user1@rubyldap.com"
+ mail: "added-user1@rubyldap.com",
}
assert @ldap.add(dn: @dn, attributes: attrs), @ldap.get_operation_result.inspect
diff --git a/test/integration/test_ber.rb b/test/integration/test_ber.rb
index 8fb4d374..51e93334 100644
--- a/test/integration/test_ber.rb
+++ b/test/integration/test_ber.rb
@@ -12,7 +12,7 @@ def test_true_ber_encoding
filter: "(uid=user1)",
size: 1,
attributes: attrs,
- attributes_only: true
+ attributes_only: true,
).first
# matches attributes we requested
diff --git a/test/integration/test_bind.rb b/test/integration/test_bind.rb
index bea6b034..bd1281e2 100644
--- a/test/integration/test_bind.rb
+++ b/test/integration/test_bind.rb
@@ -2,11 +2,23 @@
class TestBindIntegration < LDAPIntegrationTestCase
def test_bind_success
- assert @ldap.bind(method: :simple, username: "uid=user1,ou=People,dc=rubyldap,dc=com", password: "passworD1"), @ldap.get_operation_result.inspect
+ assert @ldap.bind(BIND_CREDS),
+ @ldap.get_operation_result.inspect
+ end
+
+ def test_bind_timeout
+ @ldap.port = 8389
+ error = assert_raise Net::LDAP::Error do
+ @ldap.bind BIND_CREDS
+ end
+ msgs = ['Operation timed out - user specified timeout',
+ 'Connection timed out - user specified timeout']
+ assert_send([msgs, :include?, error.message])
end
def test_bind_anonymous_fail
- refute @ldap.bind(method: :simple, username: "uid=user1,ou=People,dc=rubyldap,dc=com", password: ""), @ldap.get_operation_result.inspect
+ refute @ldap.bind(BIND_CREDS.merge(password: '')),
+ @ldap.get_operation_result.inspect
result = @ldap.get_operation_result
assert_equal Net::LDAP::ResultCodeUnwillingToPerform, result.code
@@ -17,18 +29,216 @@ def test_bind_anonymous_fail
end
def test_bind_fail
- refute @ldap.bind(method: :simple, username: "uid=user1,ou=People,dc=rubyldap,dc=com", password: "not my password"), @ldap.get_operation_result.inspect
+ refute @ldap.bind(BIND_CREDS.merge(password: "not my password")),
+ @ldap.get_operation_result.inspect
end
def test_bind_tls_with_cafile
- tls_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.merge(:ca_file => 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
+ @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 = '127.0.0.1'
+ @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 = '127.0.0.1'
+ @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_bad_hostname_verify_peer_ca_fails
+ @ldap.host = '127.0.0.1'
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: { verify_mode: OpenSSL::SSL::VERIFY_PEER,
+ ca_file: CA_FILE },
+ )
+ error = assert_raise Net::LDAP::Error,
+ Net::LDAP::ConnectionRefusedError 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
+ @ldap.host = '127.0.0.1'
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: TLS_OPTS.merge(ca_file: CA_FILE),
+ )
+ error = assert_raise Net::LDAP::Error,
+ Net::LDAP::ConnectionRefusedError 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
+ @ldap.host = '127.0.0.1'
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: { ca_file: CA_FILE },
+ )
+ error = assert_raise Net::LDAP::Error,
+ Net::LDAP::ConnectionRefusedError 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
+ @ldap.host = 'localhost'
+ @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
+ @ldap.host = 'localhost'
+ @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 = '127.0.0.1'
+ @ldap.encryption(method: :start_tls, tls_options: {})
+ error = assert_raise Net::LDAP::Error,
+ Net::LDAP::ConnectionRefusedError 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_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
+ # The following depend on /etc/hosts hacking.
+ # We can do that on CI, but it's less than cool on people's dev boxes
+ def test_bind_tls_with_multiple_hosts
+ omit_unless ENV['TRAVIS'] == 'true'
+
+ @ldap.host = nil
+ @ldap.hosts = [['ldap01.example.com', 389], ['ldap02.example.com', 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_unless ENV['TRAVIS'] == 'true'
+
+ @ldap.host = nil
+ @ldap.hosts = [['127.0.0.1', 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_unless ENV['TRAVIS'] == 'true'
+
+ @ldap.host = nil
+ @ldap.hosts = [['127.0.0.1', 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
+ omit_unless ENV['TRAVIS'] == 'true'
+
+ @ldap.host = nil
+ @ldap.hosts = [['127.0.0.1', 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_unless ENV['TRAVIS'] == 'true'
+
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: { verify_mode: OpenSSL::SSL::VERIFY_PEER },
+ )
+ assert @ldap.bind(BIND_CREDS),
+ @ldap.get_operation_result.inspect
+ end
+
+ # Inverse of the above! Don't run this on Travis, only on Vagrant.
+ # Since Vagrant's hypervisor *won't* have the CA in the system
+ # x509 store, we can assume validation will fail
+ def test_bind_tls_valid_hostname_system_on_vagrant_fails
+ omit_if ENV['TRAVIS'] == 'true'
+
+ @ldap.encryption(
+ method: :start_tls,
+ tls_options: { verify_mode: OpenSSL::SSL::VERIFY_PEER },
+ )
+ error = assert_raise Net::LDAP::Error do
+ @ldap.bind BIND_CREDS
+ end
+ assert_equal(
+ "SSL_connect returned=1 errno=0 state=error: certificate verify failed",
+ error.message,
+ )
end
end
diff --git a/test/integration/test_delete.rb b/test/integration/test_delete.rb
index 355df7b9..0cca32a9 100644
--- a/test/integration/test_delete.rb
+++ b/test/integration/test_delete.rb
@@ -12,7 +12,7 @@ def setup
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
diff --git a/test/integration/test_open.rb b/test/integration/test_open.rb
index 36724f5d..a7ac09da 100644
--- a/test/integration/test_open.rb
+++ b/test/integration/test_open.rb
@@ -63,7 +63,7 @@ def test_nested_add_with_open
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"
diff --git a/test/integration/test_password_modify.rb b/test/integration/test_password_modify.rb
new file mode 100644
index 00000000..ed8d4f5b
--- /dev/null
+++ b/test/integration/test_password_modify.rb
@@ -0,0 +1,93 @@
+require_relative '../test_helper'
+
+class TestPasswordModifyIntegration < LDAPIntegrationTestCase
+ def setup
+ super
+ @admin_account = {dn: 'cn=admin,dc=rubyldap,dc=com', password: 'passworD1', method: :simple}
+ @ldap.authenticate @admin_account[:dn], @admin_account[:password]
+
+ @dn = 'uid=modify-password-user1,ou=People,dc=rubyldap,dc=com'
+
+ 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: 'passworD1',
+ }
+ 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: 'passworD1',
+ }
+ end
+
+ def test_password_modify
+ assert @ldap.password_modify(dn: @dn,
+ auth: @auth,
+ old_password: 'passworD1',
+ new_password: 'passworD2')
+
+ assert @ldap.get_operation_result.extended_response.nil?,
+ 'Should not have generated a new password'
+
+ refute @ldap.bind(username: @dn, password: 'passworD1', 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: 'passworD1')
+
+ generated_password = @ldap.get_operation_result.extended_response[0][0]
+
+ assert generated_password, 'Should have generated a password'
+
+ refute @ldap.bind(username: @dn, password: 'passworD1', 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)
+
+ generated_password = @ldap.get_operation_result.extended_response[0][0]
+
+ assert generated_password, 'Should have generated a password'
+
+ refute @ldap.bind(username: @dn, password: 'passworD1', 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: 'passworD1', 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_search.rb b/test/integration/test_search.rb
index b56052ce..96f9ff42 100644
--- a/test/integration/test_search.rb
+++ b/test/integration/test_search.rb
@@ -57,7 +57,7 @@ def test_search_timeout
entries << entry
end
- payload, _ = events.pop
+ payload, = events.pop
assert_equal 5, payload[:time]
assert_equal entries, result
end
diff --git a/test/support/vm/openldap/README.md b/test/support/vm/openldap/README.md
index a2769567..f79f4dc6 100644
--- a/test/support/vm/openldap/README.md
+++ b/test/support/vm/openldap/README.md
@@ -1,8 +1,31 @@
# Local OpenLDAP Integration Testing
-Set up a [Vagrant](http://www.vagrantup.com/) VM to run integration tests against OpenLDAP locally.
+Set up a [Vagrant](http://www.vagrantup.com/) VM to run integration
+tests against OpenLDAP locally. *NOTE*: To support some of the SSL tests,
+Vagrant forwards localhost port 9389 to VM host port 9389. The port mapping
+goes away when you run `vagrant destroy`.
-To run integration tests locally:
+## Install Vagrant
+
+*NOTE*: The Vagrant gem (`gem install vagrant`) is
+[no longer supported](https://www.vagrantup.com/docs/installation/). If you've
+previously installed it, run `gem uninstall vagrant`. If you're an rbenv
+user, you probably want to follow that up with `rbenv rehash; hash -r`.
+
+If you use Homebrew on macOS:
+``` bash
+$ brew update
+$ brew cask install virtualbox
+$ brew cask install vagrant
+$ brew cask install vagrant-manager
+$ vagrant plugin install vagrant-vbguest
+```
+
+Installing Vagrant and virtualbox on other operating systems is left
+as an exercise to the reader. Note the `vagrant-vbguest` plugin is required
+to update the VirtualBox guest extensions in the guest VM image.
+
+## Run the tests
``` bash
# start VM (from the correct directory)
@@ -15,6 +38,9 @@ $ ip=$(vagrant ssh -- "ifconfig eth1 | grep -o -E '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]
# change back to root project directory
$ cd ../../../..
+# set the TCP port for testing
+$ export INTEGRATION_PORT=9389
+
# run all tests, including integration tests
$ time INTEGRATION=openldap INTEGRATION_HOST=$ip bundle exec rake
@@ -27,6 +53,12 @@ $ export INTEGRATION_HOST=$ip
# now run tests without having to set ENV variables
$ time bundle exec rake
+
+# Once you're all done
+$ cd test/support/vm/openldap
+$ vagrant destroy
```
-You may need to `gem install vagrant` first in order to provision the VM.
+If at any point your VM appears to have broken itself, `vagrant destroy`
+from the `test/support/vm/openldap` directory will blow it away. You can
+then do `vagrant up` and start over.
diff --git a/test/support/vm/openldap/Vagrantfile b/test/support/vm/openldap/Vagrantfile
index 96233e92..1f375e76 100644
--- a/test/support/vm/openldap/Vagrantfile
+++ b/test/support/vm/openldap/Vagrantfile
@@ -10,6 +10,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "hashicorp/precise64"
config.vm.network "private_network", type: :dhcp
+ config.vm.network "forwarded_port", guest: 389, host: 9389
config.ssh.forward_agent = true
diff --git a/test/test_auth_adapter.rb b/test/test_auth_adapter.rb
new file mode 100644
index 00000000..9e4c6002
--- /dev/null
+++ b/test/test_auth_adapter.rb
@@ -0,0 +1,15 @@
+require 'test_helper'
+
+class TestAuthAdapter < Test::Unit::TestCase
+ class FakeSocket
+ def initialize(*args)
+ end
+ end
+
+ def test_undefined_auth_adapter
+ 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
+ end
+end
diff --git a/test/test_dn.rb b/test/test_dn.rb
index 0cb2ec5a..5fff6ae8 100644
--- a/test/test_dn.rb
+++ b/test/test_dn.rb
@@ -13,17 +13,17 @@ 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
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 210e0218..6f1ca48b 100644
--- a/test/test_filter_parser.rb
+++ b/test/test_filter_parser.rb
@@ -14,6 +14,10 @@ def test_brackets
assert_kind_of Net::LDAP::Filter, Net::LDAP::Filter::FilterParser.parse("(cn=[{something}])")
end
+ def test_slash
+ assert_kind_of Net::LDAP::Filter, Net::LDAP::Filter::FilterParser.parse("(departmentNumber=FOO//BAR/FOO)")
+ end
+
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
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 640b0e23..0a976be4 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -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/cacert.pem", File.dirname(__FILE__))
end
end
+BIND_CREDS = {
+ method: :simple,
+ username: "uid=user1,ou=People,dc=rubyldap,dc=com",
+ password: "passworD1",
+}.freeze
+
+TLS_OPTS = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.merge({}).freeze
+
if RUBY_VERSION < "2.0"
class String
def b
@@ -56,7 +64,7 @@ def setup
@service = MockInstrumentationService.new
@ldap = Net::LDAP.new \
host: ENV.fetch('INTEGRATION_HOST', 'localhost'),
- port: 389,
+ port: ENV.fetch('INTEGRATION_PORT', 389),
admin_user: 'uid=admin,dc=rubyldap,dc=com',
admin_password: 'passworD1',
search_domains: %w(dc=rubyldap,dc=com),
diff --git a/test/test_ldap.rb b/test/test_ldap.rb
index 9704b346..8d6a9a72 100644
--- a/test/test_ldap.rb
+++ b/test/test_ldap.rb
@@ -1,6 +1,28 @@
require '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)
@@ -57,4 +78,37 @@ def test_instrument_search_with_size
assert_equal "(uid=user1)", payload[:filter]
assert_equal result.size, payload[:size]
end
+
+ def test_obscure_auth
+ password = "opensesame"
+ assert_include(@subject.inspect, "anonymous")
+ @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 96b542ac..8489c377 100644
--- a/test/test_ldap_connection.rb
+++ b/test/test_ldap_connection.rb
@@ -1,45 +1,132 @@
require_relative 'test_helper'
class TestLDAPConnection < Test::Unit::TestCase
+ def capture_stderr
+ stderr, $stderr = $stderr, StringIO.new
+ yield
+ $stderr.string
+ ensure
+ $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 = [
+ ["success.host", 636],
+ ["fail.SocketError", 636],
+ ["fail.SocketError", 636],
+ ]
+
+ connection = Net::LDAP::Connection.new(:hosts => hosts, :socket_class => FakeTCPSocket)
+ connection.socket
+ end
+
+ def test_list_of_hosts_with_first_host_failure
+ hosts = [
+ ["fail.SocketError", 636],
+ ["success.host", 636],
+ ["fail.SocketError", 636],
+ ]
+
+ connection = Net::LDAP::Connection.new(:hosts => hosts, :socket_class => FakeTCPSocket)
+ connection.socket
+ end
+
+ def test_list_of_hosts_with_all_hosts_failure
+ hosts = [
+ ["fail.SocketError", 636],
+ ["fail.SocketError", 636],
+ ["fail.SocketError", 636],
+ ]
+
+ connection = Net::LDAP::Connection.new(:hosts => hosts, :socket_class => FakeTCPSocket)
+ assert_raise Net::LDAP::ConnectionError do
+ 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 Net::LDAP::ConnectionRefusedError 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
+ connection = Net::LDAP::Connection.new(:host => "fail.Errno::ECONNREFUSED", :port => 636, :socket_class => FakeTCPSocket)
+ stderr = capture_stderr do
+ assert_raise Net::LDAP::ConnectionRefusedError do
+ connection.socket
+ end
+ end
+ assert_equal("Deprecation warning: Net::LDAP::ConnectionRefused will be deprecated. Use Errno::ECONNREFUSED instead.\n", stderr)
+ end
+
+ def test_connection_timeout
+ connection = Net::LDAP::Connection.new(:host => "fail.Errno::ETIMEDOUT", :port => 636, :socket_class => FakeTCPSocket)
+ stderr = capture_stderr do
+ assert_raise Net::LDAP::Error do
+ connection.socket
+ end
end
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
@@ -73,7 +160,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]
@@ -168,7 +255,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
@@ -202,7 +289,7 @@ def test_queued_read_setup_encryption_with_start_tls
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, {}).
+ flexmock(Net::LDAP::Connection).should_receive(:wrap_with_ssl).with(mock, {}, nil).
and_return(mock)
conn.next_msgid # simulates ongoing query
@@ -259,7 +346,7 @@ 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
@@ -288,7 +375,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 \
@@ -310,8 +397,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
@@ -327,7 +414,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
@@ -344,9 +431,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]
@@ -366,7 +453,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
@@ -374,7 +461,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]
@@ -393,8 +480,8 @@ 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
diff --git a/test/test_ldif.rb b/test/test_ldif.rb
index 988c3155..cc1ee2bf 100644
--- a/test/test_ldif.rb
+++ b/test/test_ldif.rb
@@ -38,45 +38,45 @@ def test_ldif_with_password
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"))
+ 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"))
+ 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.has_key?("abcdefghij klmn"))
+ 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"))
+ 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"))
+ 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"))
+ 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|
+ 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_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..6a809a80 100644
--- a/test/test_snmp.rb
+++ b/test/test_snmp.rb
@@ -16,9 +16,9 @@ def self.raw_string(s)
def test_invalid_packet
data = "xxxx"
- assert_raise(Net::BER::BerError) {
+ assert_raise(Net::BER::BerError) do
ary = 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) {
+ assert_raise(Net::SnmpPdu::Error) do
Net::SnmpPdu.parse("aaaaaaaaaaaaaa")
- }
+ end
end
def test_get_request
diff --git a/testserver/ldapserver.rb b/testserver/ldapserver.rb
index eba130ce..809f9e7e 100644
--- a/testserver/ldapserver.rb
+++ b/testserver/ldapserver.rb
@@ -24,7 +24,7 @@ module LdapServer
},
:primitive => {
2 => :string, # ldapsearch sends this to unbind
- }
+ },
},
:context_specific => {
:primitive => {
@@ -34,7 +34,7 @@ module LdapServer
:constructed => {
3 => :array # equality filter
},
- }
+ },
}
def post_init
@@ -119,7 +119,7 @@ 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}
+ pdu[1][7].map(&:downcase)
else
:all
end
@@ -133,21 +133,21 @@ def handle_search_request pdu
# TODO, what if this returns nil?
filter = Net::LDAP::Filter.parse_ldap_filter( filters )
- $ldif.each {|dn, entry|
+ $ldif.each do |dn, entry|
if filter.match( entry )
attrs = []
- entry.each {|k, v|
+ entry.each do |k, v|
if requested_attrs == :all or requested_attrs.include?(k.downcase)
- attrvals = v.map {|v1| v1.to_ber}.to_ber_set
+ 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?"
@@ -156,7 +156,7 @@ def handle_search_request pdu
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
@@ -201,10 +201,9 @@ def load_test_data
require 'net/ldap'
- EventMachine.run {
+ 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"}
- }
+ end
end
-