pax_global_header00006660000000000000000000000064126427440110014513gustar00rootroot0000000000000052 comment=82b3a4e8f70692ec679d880628fdb0f5844d42b9 gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/000077500000000000000000000000001264274401100216705ustar00rootroot00000000000000gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/.gitignore000066400000000000000000000001511264274401100236550ustar00rootroot00000000000000config.yml tmp/* *.log /*.log* authorized_keys.lock coverage/ .gitlab_shell_secret .bundle tags .bundle/ gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/.gitlab-ci.yml000066400000000000000000000006341264274401100243270ustar00rootroot00000000000000before_script: - export PATH=~/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin - apt-get update - apt-get install -y git-annex - gem install --bindir /usr/local/bin bundler - cp config.yml.example config.yml - bundle install rspec: script: - bundle exec rspec spec tags: - ruby except: - tags rubocop: script: - bundle exec rubocop tags: - ruby except: - tags gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/.hound.yml000066400000000000000000000000771264274401100236120ustar00rootroot00000000000000StringLiterals: EnforcedStyle: single_quotes Enabled: true gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/.rspec000066400000000000000000000000101264274401100227740ustar00rootroot00000000000000--color gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/.rubocop.yml000066400000000000000000000757031264274401100241560ustar00rootroot00000000000000Style/AccessModifierIndentation: Description: Check indentation of private/protected visibility modifiers. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected' Enabled: true Style/AccessorMethodName: Description: Check the naming of accessor methods for get_/set_. Enabled: false Style/Alias: Description: 'Use alias_method instead of alias.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#alias-method' Enabled: true Style/AlignArray: Description: >- Align the elements of an array literal if they span more than one line. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays' Enabled: true Style/AlignHash: Description: >- Align the elements of a hash literal if they span more than one line. Enabled: true Style/AlignParameters: Description: >- Align the parameters of a method call if they span more than one line. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-double-indent' Enabled: false Style/AndOr: Description: 'Use &&/|| instead of and/or.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-and-or-or' Enabled: false Style/ArrayJoin: Description: 'Use Array#join instead of Array#*.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#array-join' Enabled: false Style/AsciiComments: Description: 'Use only ascii symbols in comments.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-comments' Enabled: true Style/AsciiIdentifiers: Description: 'Use only ascii symbols in identifiers.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-identifiers' Enabled: true Style/Attr: Description: 'Checks for uses of Module#attr.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr' Enabled: false Style/BeginBlock: Description: 'Avoid the use of BEGIN blocks.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks' Enabled: true Style/BarePercentLiterals: Description: 'Checks if usage of %() or %Q() matches configuration.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand' Enabled: false Style/BlockComments: Description: 'Do not use block comments.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-block-comments' Enabled: false Style/BlockEndNewline: Description: 'Put end statement of multiline block on its own line.' Enabled: true Style/Blocks: Description: >- Avoid using {...} for multi-line blocks (multiline chaining is always ugly). Prefer {...} over do...end for single-line blocks. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' Enabled: true Style/BracesAroundHashParameters: Description: 'Enforce braces style around hash parameters.' Enabled: false Style/CaseEquality: Description: 'Avoid explicit use of the case equality operator(===).' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-case-equality' Enabled: false Style/CaseIndentation: Description: 'Indentation of when in a case/when/[else/]end.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-when-to-case' Enabled: true Style/CharacterLiteral: Description: 'Checks for uses of character literals.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-character-literals' Enabled: true Style/ClassAndModuleCamelCase: Description: 'Use CamelCase for classes and modules.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#camelcase-classes' Enabled: true Style/ClassAndModuleChildren: Description: 'Checks style of children classes and modules.' Enabled: false Style/ClassCheck: Description: 'Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.' Enabled: false Style/ClassMethods: Description: 'Use self when defining module/class methods.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#def-self-singletons' Enabled: false Style/ClassVars: Description: 'Avoid the use of class variables.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-class-vars' Enabled: true Style/ColonMethodCall: Description: 'Do not use :: for method call.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#double-colons' Enabled: false Style/CommentAnnotation: Description: >- Checks formatting of special comments (TODO, FIXME, OPTIMIZE, HACK, REVIEW). StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#annotate-keywords' Enabled: false Style/CommentIndentation: Description: 'Indentation of comments.' Enabled: true Style/ConstantName: Description: 'Constants should use SCREAMING_SNAKE_CASE.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#screaming-snake-case' Enabled: true Style/DefWithParentheses: Description: 'Use def with parentheses when there are arguments.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' Enabled: false Style/DeprecatedHashMethods: Description: 'Checks for use of deprecated Hash methods.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key' Enabled: false Style/Documentation: Description: 'Document classes and non-namespace modules.' Enabled: false Style/DotPosition: Description: 'Checks the position of the dot in multi-line method calls.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains' Enabled: false Style/DoubleNegation: Description: 'Checks for uses of double negation (!!).' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-bang-bang' Enabled: false Style/EachWithObject: Description: 'Prefer `each_with_object` over `inject` or `reduce`.' Enabled: false Style/ElseAlignment: Description: 'Align elses and elsifs correctly.' Enabled: true Style/EmptyElse: Description: 'Avoid empty else-clauses.' Enabled: false Style/EmptyLineBetweenDefs: Description: 'Use empty lines between defs.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods' Enabled: false Style/EmptyLines: Description: "Don't use several empty lines in a row." Enabled: false Style/EmptyLinesAroundAccessModifier: Description: "Keep blank lines around access modifiers." Enabled: false Style/EmptyLinesAroundBlockBody: Description: "Keeps track of empty lines around block bodies." Enabled: false Style/EmptyLinesAroundClassBody: Description: "Keeps track of empty lines around class bodies." Enabled: false Style/EmptyLinesAroundModuleBody: Description: "Keeps track of empty lines around module bodies." Enabled: false Style/EmptyLinesAroundMethodBody: Description: "Keeps track of empty lines around method bodies." Enabled: false Style/EmptyLiteral: Description: 'Prefer literals to Array.new/Hash.new/String.new.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#literal-array-hash' Enabled: false Style/EndBlock: Description: 'Avoid the use of END blocks.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-END-blocks' Enabled: false Style/EndOfLine: Description: 'Use Unix-style line endings.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#crlf' Enabled: false Style/EvenOdd: Description: 'Favor the use of Fixnum#even? && Fixnum#odd?' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' Enabled: false Style/FileName: Description: 'Use snake_case for source file names.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files' Enabled: false Style/FlipFlop: Description: 'Checks for flip flops' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-flip-flops' Enabled: false Style/For: Description: 'Checks use of for or each in multiline loops.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-for-loops' Enabled: false Style/FormatString: Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#sprintf' Enabled: false Style/GlobalVars: Description: 'Do not introduce global variables.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#instance-vars' Enabled: false Style/GuardClause: Description: 'Check for conditionals that can be replaced with guard clauses' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' Enabled: false Style/HashSyntax: Description: >- Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax { :a => 1, :b => 2 }. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-literals' Enabled: true Style/IfUnlessModifier: Description: >- Favor modifier if/unless usage when you have a single-line body. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier' Enabled: false Style/IfWithSemicolon: Description: 'Do not use if x; .... Use the ternary operator instead.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs' Enabled: false Style/IndentationConsistency: Description: 'Keep indentation straight.' Enabled: true Style/IndentationWidth: Description: 'Use 2 spaces for indentation.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' Enabled: true Style/IndentArray: Description: >- Checks the indentation of the first element in an array literal. Enabled: false Style/IndentHash: Description: 'Checks the indentation of the first key in a hash literal.' Enabled: false Style/InfiniteLoop: Description: 'Use Kernel#loop for infinite loops.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#infinite-loop' Enabled: false Style/Lambda: Description: 'Use the new lambda literal syntax for single-line blocks.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#lambda-multi-line' Enabled: false Style/LambdaCall: Description: 'Use lambda.call(...) instead of lambda.(...).' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc-call' Enabled: false Style/LeadingCommentSpace: Description: 'Comments should start with a space.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-space' Enabled: false Style/LineEndConcatenation: Description: >- Use \ instead of + or << to concatenate two string literals at line end. Enabled: false Style/MethodCallParentheses: Description: 'Do not use parentheses for method calls with no arguments.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-args-no-parens' Enabled: false Style/MethodDefParentheses: Description: >- Checks if the method definitions have or don't have parentheses. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' Enabled: false Style/MethodName: Description: 'Use the configured style when naming methods.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' Enabled: false Style/ModuleFunction: Description: 'Checks for usage of `extend self` in modules.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#module-function' Enabled: false Style/MultilineBlockChain: Description: 'Avoid multi-line chains of blocks.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' Enabled: false Style/MultilineBlockLayout: Description: 'Ensures newlines after multiline block do statements.' Enabled: false Style/MultilineIfThen: Description: 'Do not use then for multi-line if/unless.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-then' Enabled: false Style/MultilineOperationIndentation: Description: >- Checks indentation of binary operations that span more than one line. Enabled: false Style/MultilineTernaryOperator: Description: >- Avoid multi-line ?: (the ternary operator); use if/unless instead. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary' Enabled: false Style/NegatedIf: Description: >- Favor unless over if for negative conditions (or control flow or). StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#unless-for-negatives' Enabled: false Style/NegatedWhile: Description: 'Favor until over while for negative conditions.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#until-for-negatives' Enabled: false Style/NestedTernaryOperator: Description: 'Use one expression per branch in a ternary operator.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-ternary' Enabled: false Style/Next: Description: 'Use `next` to skip iteration instead of a condition at the end.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' Enabled: false Style/NilComparison: Description: 'Prefer x.nil? to x == nil.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' Enabled: false Style/NonNilCheck: Description: 'Checks for redundant nil checks.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks' Enabled: false Style/Not: Description: 'Use ! instead of not.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not' Enabled: false Style/NumericLiterals: Description: >- Add underscores to large numeric literals to improve their readability. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics' Enabled: false Style/OneLineConditional: Description: >- Favor the ternary operator(?:) over if/then/else/end constructs. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator' Enabled: false Style/OpMethod: Description: 'When defining binary operators, name the argument other.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg' Enabled: false Style/ParenthesesAroundCondition: Description: >- Don't use parentheses around the condition of an if/unless/while. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-parens-if' Enabled: false Style/PercentLiteralDelimiters: Description: 'Use `%`-literal delimiters consistently' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-literal-braces' Enabled: false Style/PercentQLiterals: Description: 'Checks if uses of %Q/%q match the configured preference.' Enabled: false Style/PerlBackrefs: Description: 'Avoid Perl-style regex back references.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers' Enabled: false Style/PredicateName: Description: 'Check the names of predicate methods.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark' Enabled: false Style/Proc: Description: 'Use proc instead of Proc.new.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc' Enabled: false Style/RaiseArgs: Description: 'Checks the arguments passed to raise/fail.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#exception-class-messages' Enabled: false Style/RedundantBegin: Description: "Don't use begin blocks when they are not needed." StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#begin-implicit' Enabled: false Style/RedundantException: Description: "Checks for an obsolete RuntimeException argument in raise/fail." StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror' Enabled: false Style/RedundantReturn: Description: "Don't use return where it's not required." StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-return' Enabled: false Style/RedundantSelf: Description: "Don't use self where it's not needed." StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-self-unless-required' Enabled: false Style/RegexpLiteral: Description: >- Use %r for regular expressions matching more than `MaxSlashes` '/' characters. Use %r only for regular expressions matching more than `MaxSlashes` '/' character. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-r' Enabled: false Style/RescueModifier: Description: 'Avoid using rescue in its modifier form.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers' Enabled: false Style/SelfAssignment: Description: >- Checks for places where self-assignment shorthand should have been used. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#self-assignment' Enabled: false Style/Semicolon: Description: "Don't use semicolons to terminate expressions." StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon' Enabled: false Style/SignalException: Description: 'Checks for proper usage of fail and raise.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#fail-method' Enabled: false Style/SingleLineBlockParams: Description: 'Enforces the names of some block params.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#reduce-blocks' Enabled: false Style/SingleLineMethods: Description: 'Avoid single-line methods.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-single-line-methods' Enabled: false Style/SingleSpaceBeforeFirstArg: Description: >- Checks that exactly one space is used between a method name and the first argument for method calls without parentheses. Enabled: false Style/SpaceAfterColon: Description: 'Use spaces after colons.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' Enabled: false Style/SpaceAfterComma: Description: 'Use spaces after commas.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' Enabled: false Style/SpaceAfterControlKeyword: Description: 'Use spaces after if/elsif/unless/while/until/case/when.' Enabled: false Style/SpaceAfterMethodName: Description: >- Do not put a space between a method name and the opening parenthesis in a method definition. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' Enabled: false Style/SpaceAfterNot: Description: Tracks redundant space after the ! operator. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-bang' Enabled: false Style/SpaceAfterSemicolon: Description: 'Use spaces after semicolons.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' Enabled: false Style/SpaceBeforeBlockBraces: Description: >- Checks that the left block brace has or doesn't have space before it. Enabled: false Style/SpaceBeforeComma: Description: 'No spaces before commas.' Enabled: false Style/SpaceBeforeComment: Description: >- Checks for missing space between code and a comment on the same line. Enabled: false Style/SpaceBeforeSemicolon: Description: 'No spaces before semicolons.' Enabled: false Style/SpaceInsideBlockBraces: Description: >- Checks that block braces have or don't have surrounding space. For blocks taking parameters, checks that the left brace has or doesn't have trailing space. Enabled: false Style/SpaceAroundEqualsInParameterDefault: Description: >- Checks that the equals signs in parameter default assignments have or don't have surrounding space depending on configuration. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-around-equals' Enabled: false Style/SpaceAroundOperators: Description: 'Use spaces around operators.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' Enabled: false Style/SpaceBeforeModifierKeyword: Description: 'Put a space before the modifier keyword.' Enabled: false Style/SpaceInsideBrackets: Description: 'No spaces after [ or before ].' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' Enabled: false Style/SpaceInsideHashLiteralBraces: Description: "Use spaces inside hash literal braces - or don't." StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' Enabled: true Style/SpaceInsideParens: Description: 'No spaces after ( or before ).' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' Enabled: false Style/SpaceInsideRangeLiteral: Description: 'No spaces inside range literals.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals' Enabled: false Style/SpecialGlobalVars: Description: 'Avoid Perl-style global variables.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms' Enabled: false Style/StringLiterals: Description: 'Checks if uses of quotes match the configured preference.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-string-literals' Enabled: false Style/StringLiteralsInInterpolation: Description: >- Checks if uses of quotes inside expressions in interpolated strings match the configured preference. Enabled: false Style/SymbolProc: Description: 'Use symbols as procs instead of blocks when possible.' Enabled: false Style/Tab: Description: 'No hard tabs.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' Enabled: false Style/TrailingBlankLines: Description: 'Checks trailing blank lines and final newline.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#newline-eof' Enabled: true Style/TrailingComma: Description: 'Checks for trailing comma in parameter lists and literals.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' Enabled: false Style/TrailingWhitespace: Description: 'Avoid trailing whitespace.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace' Enabled: false Style/TrivialAccessors: Description: 'Prefer attr_* methods to trivial readers/writers.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family' Enabled: false Style/UnlessElse: Description: >- Do not use unless with else. Rewrite these with the positive case first. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-else-with-unless' Enabled: false Style/UnneededCapitalW: Description: 'Checks for %W when interpolation is not needed.' Enabled: false Style/UnneededPercentQ: Description: 'Checks for %q/%Q when single quotes or double quotes would do.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q' Enabled: false Style/UnneededPercentX: Description: 'Checks for %x when `` would do.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-x' Enabled: false Style/VariableInterpolation: Description: >- Don't interpolate global, instance and class variables directly in strings. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#curlies-interpolate' Enabled: false Style/VariableName: Description: 'Use the configured style when naming variables.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' Enabled: false Style/WhenThen: Description: 'Use when x then ... for one-line cases.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#one-line-cases' Enabled: false Style/WhileUntilDo: Description: 'Checks for redundant do after while or until.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do' Enabled: false Style/WhileUntilModifier: Description: >- Favor modifier while/until usage when you have a single-line body. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier' Enabled: false Style/WordArray: Description: 'Use %w or %W for arrays of words.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w' Enabled: false #################### Metrics ################################ Metrics/AbcSize: Description: >- A calculated magnitude based on number of assignments, branches, and conditions. Enabled: false Metrics/BlockNesting: Description: 'Avoid excessive block nesting' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count' Enabled: false Metrics/ClassLength: Description: 'Avoid classes longer than 100 lines of code.' Enabled: false Metrics/CyclomaticComplexity: Description: >- A complexity metric that is strongly correlated to the number of test cases needed to validate a method. Enabled: false Metrics/LineLength: Description: 'Limit lines to 80 characters.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits' Enabled: false Metrics/MethodLength: Description: 'Avoid methods longer than 10 lines of code.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods' Enabled: false Metrics/ParameterLists: Description: 'Avoid parameter lists longer than three or four parameters.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params' Enabled: false Metrics/PerceivedComplexity: Description: >- A complexity metric geared towards measuring complexity for a human reader. Enabled: false #################### Lint ################################ ### Warnings Lint/AmbiguousOperator: Description: >- Checks for ambiguous operators in the first argument of a method invocation without parentheses. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-as-args' Enabled: false Lint/AmbiguousRegexpLiteral: Description: >- Checks for ambiguous regexp literals in the first argument of a method invocation without parenthesis. Enabled: false Lint/AssignmentInCondition: Description: "Don't use assignment in conditions." StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition' Enabled: false Lint/BlockAlignment: Description: 'Align block ends correctly.' Enabled: false Lint/ConditionPosition: Description: >- Checks for condition placed in a confusing position relative to the keyword. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#same-line-condition' Enabled: false Lint/Debugger: Description: 'Check for debugger calls.' Enabled: false Lint/DefEndAlignment: Description: 'Align ends corresponding to defs correctly.' Enabled: false Lint/DeprecatedClassMethods: Description: 'Check for deprecated class method calls.' Enabled: false Lint/ElseLayout: Description: 'Check for odd code arrangement in an else block.' Enabled: false Lint/EmptyEnsure: Description: 'Checks for empty ensure block.' Enabled: false Lint/EmptyInterpolation: Description: 'Checks for empty string interpolation.' Enabled: false Lint/EndAlignment: Description: 'Align ends correctly.' Enabled: false Lint/EndInMethod: Description: 'END blocks should not be placed inside method definitions.' Enabled: false Lint/EnsureReturn: Description: 'Do not use return in an ensure block.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-return-ensure' Enabled: false Lint/Eval: Description: 'The use of eval represents a serious security risk.' Enabled: false Lint/HandleExceptions: Description: "Don't suppress exception." StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions' Enabled: false Lint/InvalidCharacterLiteral: Description: >- Checks for invalid character literals with a non-escaped whitespace character. Enabled: false Lint/LiteralInCondition: Description: 'Checks of literals used in conditions.' Enabled: false Lint/LiteralInInterpolation: Description: 'Checks for literals used in interpolation.' Enabled: false Lint/Loop: Description: >- Use Kernel#loop with break rather than begin/end/until or begin/end/while for post-loop tests. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#loop-with-break' Enabled: false Lint/ParenthesesAsGroupedExpression: Description: >- Checks for method calls with a space before the opening parenthesis. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' Enabled: true Lint/RequireParentheses: Description: >- Use parentheses in the method call to avoid confusion about precedence. Enabled: false Lint/RescueException: Description: 'Avoid rescuing the Exception class.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues' Enabled: false Lint/ShadowingOuterLocalVariable: Description: >- Do not use the same name as outer local variable for block arguments or block local variables. Enabled: false Lint/SpaceBeforeFirstArg: Description: >- Put a space between a method name and the first argument in a method call without parentheses. Enabled: false Lint/StringConversionInInterpolation: Description: 'Checks for Object#to_s usage in string interpolation.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-to-s' Enabled: false Lint/UnderscorePrefixedVariableName: Description: 'Do not use prefix `_` for a variable that is used.' Enabled: true Lint/UnusedBlockArgument: Description: 'Checks for unused block arguments.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' Enabled: false Lint/UnusedMethodArgument: Description: 'Checks for unused method arguments.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' Enabled: false Lint/UnreachableCode: Description: 'Unreachable code.' Enabled: false Lint/UselessAccessModifier: Description: 'Checks for useless access modifiers.' Enabled: false Lint/UselessAssignment: Description: 'Checks for useless assignment to a local variable.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' Enabled: false Lint/UselessComparison: Description: 'Checks for comparison of something with itself.' Enabled: false Lint/UselessElseWithoutRescue: Description: 'Checks for useless `else` in `begin..end` without `rescue`.' Enabled: false Lint/UselessSetterCall: Description: 'Checks for useless setter call to a local variable.' Enabled: false Lint/Void: Description: 'Possible use of operator/literal/variable in void context.' Enabled: false # Exclude some of GitLab files # # AllCops: Exclude: - 'spec/**/*' - 'features/**/*' - 'vendor/**/*' - 'db/**/*' - 'tmp/**/*' - 'bin/**/*' - 'lib/backup/**/*' - 'lib/tasks/**/*' - 'lib/email_validator.rb' - 'lib/gitlab/upgrader.rb' - 'lib/gitlab/seeder.rb' gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/CHANGELOG000066400000000000000000000115071264274401100231060ustar00rootroot00000000000000v2.6.10 - Add git gc for housekeeping v2.6.9 - Remove trailing slashes from gitlab_url v2.6.8 - Revert git-lfs-authenticate command from white list v2.6.7 - Exit with non-zero status when import-repository fails - Add fetch-remote command v2.6.6 - Do not clean LANG environment variable for the git hooks when working through the SSH-protocol - Add git-lfs-authenticate command to white list (this command is used by git-lfs for SSO authentication through SSH-protocol) - Handle git-annex and gcryptsetup v2.6.5 - Handle broken symlinks in create-hooks v2.6.4 - Remove keys from authorized_keys in-place - Increase batch_add_keys lock timeout to 300 seconds - If git-annex is enabled set GIT_ANNEX_SHELL_LIMITED variable v2.6.3 - Prevent keys with a very specific comment from accidentally being deleted. v2.6.2 - Include ecdsa keys in `gitlab_keys list-keys`. - Refactor logic around GL_ID v2.6.1 - Write errors to stderr to get git to abort and show them as such. v2.6.0 - Prevent character encoding issues by sending received changes as raw data. v2.5.4 - Remove recursive commands from bin/install v2.5.3 - Improve git-annex integration v2.5.2 - Safer line sub for git-annex command v2.5.1 - Expect broadcast message to return empty JSON if no message now v2.5.0 - Support git-annex tool (disabled by default) - Add rubocop (Ruby static code analyzer) for development v2.4.3 - Print broadcast message if one is available v2.4.2 - Pass git changes list as string instead of array v2.4.1 - Access token masking in url before loging v2.4.0 - Show error message when git push is rejected v2.2.0 - Support for custom hooks (Drew Blessing and Jose Kahan) v2.1.0 - Use secret token with GitLab internal API. Requires GitLab 7.5 or higher v2.0.1 - Send post-receive changes to redis as a string instead of array v2.0.0 - Works with GitLab v7.3+ - Replace raise with abort when checking path to prevent path exposure - Handle invalid number of arguments on remote commands - Replace update hook with pre-receive and post-receive hooks. - Symlink the whole hooks directory - Ignore missing repositories in create-hooks - Connect to Redis via sockets by default v1.9.7 - Increased test coverage - By default use direct unicorn connection (localhost:8080) - Fix wrong repo path send to GitLab by GitlabUpdate hook v1.9.6 - Explicitly require 'timeout' from the standard library v1.9.5 - Put authorized_keys.lock in the same directory as authorized_keys - Use lock file when add new entries to authorized_keys v1.9.4 - Use lock file when modify authorized_keys v1.9.3 - Ignore force push detection for new branch or branch remove push v1.9.2 - Add support for force push detection v1.9.1 - Update hook sends branch and tag name v1.9.0 - Call api in update hook for both ssdh and http push. Requires GitLab 6.7+ - Pass oldrev and newrev to api.allowed? v1.8.5 - Add `gitlab-keys batch-add-keys` subcommand for authorized_keys rebuilds v1.8.4 - Dont do import if repository exists v1.8.3 - Add timeout option for repository import v1.8.2 - Fix broken 1.8.1 v1.8.1 - Restrict Environment Variables - Add bin/create-hooks command - More safe shell execution v1.8.0 - Fix return values in GitlabKeys v1.7.9 - Fix escape of repository path for custom ssh port v1.7.8 - Escape repository path to prevent relative links (CVE-2013-4583) v1.7.7 - Separate options from arguments with -- (CVE-2013-4582) - Bypass shell and use stdlib JSON for GitlabUpdate (CVE-2013-4581) v1.7.6 - Fix gitlab-projects update-head for improted repo when branch exists but not listed in refs/head v1.7.5 - Remove keys from authorized_keys using ruby instead of shell v1.7.4 - More protection against shell injection (CVE-2013-4546) v1.7.3 - Use Kernel#open to append lines to authorized_keys (CVE-2013-4490) v1.7.2 - More safe command execution v1.7.1 - Fixed issue when developers are able to push to protected branches that contain a '/' in the branch name. v1.7.0 - Clean authorized_keys file with `gitlab-keys clear` v1.6.0 - Create branch/tag functionality - Remove branch/tag functionality v1.5.0 - Logger - Ability to specify ca_file/ca_path - Update-head command for project - Better regexp for key_id inside shell v1.4.0 - Regex used in rm-key command was too lax v1.3.0 - Fork-project command - Custom redis configuration - Interpret login with deploy key as anonymous one v1.2.0 - Return non-zero result if gitlab-projects and gitlab-keys execution was not successful - http_settings configuration option added v1.1.0 - added mv-project feature - increased test coverage v1.0.4 - requires gitlab c9ca15e - don't use post-receive file any more. Make all updates in update - fixed issue with invalid GL_USER - use GL_ID instead of GL_USER gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/Gemfile000066400000000000000000000003441264274401100231640ustar00rootroot00000000000000source "http://rubygems.org" group :development, :test do gem 'coveralls', require: false gem 'rspec', '~> 2.14.0' gem 'webmock' gem 'guard' gem 'guard-rspec' gem 'vcr' gem 'rubocop', '0.28.0', require: false end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/Gemfile.lock000066400000000000000000000034001264274401100241070ustar00rootroot00000000000000GEM remote: http://rubygems.org/ specs: addressable (2.3.2) ast (2.0.0) astrolabe (1.3.0) parser (>= 2.2.0.pre.3, < 3.0) coderay (1.0.8) coveralls (0.7.1) multi_json (~> 1.3) rest-client simplecov (>= 0.7) term-ansicolor thor crack (0.3.1) diff-lcs (1.2.5) docile (1.1.5) guard (1.5.4) listen (>= 0.4.2) lumberjack (>= 1.0.2) pry (>= 0.9.10) thor (>= 0.14.6) guard-rspec (2.1.2) guard (>= 1.1) rspec (~> 2.11) listen (0.5.3) lumberjack (1.0.2) method_source (0.8.1) mime-types (2.3) multi_json (1.10.1) netrc (0.7.7) parser (2.2.0.2) ast (>= 1.1, < 3.0) powerpack (0.0.9) pry (0.9.10) coderay (~> 1.0.5) method_source (~> 0.8) slop (~> 3.3.1) rainbow (2.0.0) rest-client (1.7.2) mime-types (>= 1.16, < 3.0) netrc (~> 0.7) rspec (2.14.1) rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) rspec-core (2.14.8) rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) rspec-mocks (2.14.6) rubocop (0.28.0) astrolabe (~> 1.3) parser (>= 2.2.0.pre.7, < 3.0) powerpack (~> 0.0.6) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.4) ruby-progressbar (1.7.1) simplecov (0.9.1) docile (~> 1.1.0) multi_json (~> 1.0) simplecov-html (~> 0.8.0) simplecov-html (0.8.0) slop (3.3.3) term-ansicolor (1.3.0) tins (~> 1.0) thor (0.19.1) tins (1.3.3) vcr (2.4.0) webmock (1.9.0) addressable (>= 2.2.7) crack (>= 0.1.7) PLATFORMS ruby DEPENDENCIES coveralls guard guard-rspec rspec (~> 2.14.0) rubocop (= 0.28.0) vcr webmock gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/Guardfile000066400000000000000000000021761264274401100235230ustar00rootroot00000000000000# A sample Guardfile # More info at https://github.com/guard/guard#readme guard 'rspec' do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } watch('spec/spec_helper.rb') { "spec" } # Rails example watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } watch(%r{^spec/support/(.+)\.rb$}) { "spec" } watch('config/routes.rb') { "spec/routing" } watch('app/controllers/application_controller.rb') { "spec/controllers" } # Capybara features specs watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" } # Turnip features and steps watch(%r{^spec/acceptance/(.+)\.feature$}) watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' } end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/LICENSE000066400000000000000000000020501264274401100226720ustar00rootroot00000000000000Copyright (c) 2013 Dmitriy Zaporozhets Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/README.md000066400000000000000000000150661264274401100231570ustar00rootroot00000000000000# GitLab Shell ## GitLab Shell handles git commands for GitLab GitLab Shell handles git commands for GitLab and modifies the list of authorized keys. GitLab Shell is not a Unix shell nor a replacement for Bash or Zsh. When you access the GitLab server over ssh then GitLab Shell will: 1. Limits you to predefined git commands (git push, git pull, git annex). 1. Call the GitLab Rails API to check if you are authorized 1. It will execute the pre-receive hooks (called Git Hooks in GitLab Enterprise Edition) 1. It will excute the action you requested 1. Process the GitLab post-receive actions 1. Process any custom post-receive actions If you access a GitLab server over http(s) what happens depends on if you pull from or push to the git repository. If you pull from git repositories over http(s) the GitLab Rails app will completely handle the authentication and execution. If you push to git repositories over http(s) the GitLab Rails app will not handle any authentication or execution but it will delegate the following to GitLab Shell: 1. Call the GitLab Rails API to check if you are authorized 1. It will execute the pre-receive hooks (called Git Hooks in GitLab Enterprise Edition) 1. It will excute the action you requested 1. Process the GitLab post-receive actions 1. Process any custom post-receive actions Maybe you wonder why in the case of git push over http(s) the Rails app doesn't handle authentication before delegating to GitLab Shell. This is because GitLab Rails doesn't have the logic to interpret git push commands. The idea is to have these interpretation code in only one place and this is GitLab Shell so we can reuse it for ssh access. Actually GitLab Shell executes all git push commands without checking authorizations and relies on the pre-receive hooks to check authorizations. When you do a git pull command the authorizations are checked before executing the commands (either in GitLab Rails or GitLab Shell with an API call to GitLab Rails). The authorization checks for git pull are much simpler since you only have to check if a user can access the repo (no need to check branch permissions). An overview of the four cases described above: 1. git pull over ssh -> gitlab-shell -> API call to gitlab-rails (Authorization) -> accept or decline -> execute git command 1. git pull over http -> gitlab-rails (AUthorization) -> accept or decline -> execute git command 1. git push over ssh -> gitlab-shell (git command is not executed yet) -> execute git command -> gitlab-shell pre-receive hook -> API call to gitlab-rails (authorization) -> accept or decline push 1. git push over http -> gitlab-rails (git command is not executed yet) -> execute git command -> gitlab-shell pre-receive hook -> API call to gitlab-rails (authorization) -> accept or decline push ## Code status [![CI](https://ci.gitlab.org/projects/4/status.svg?ref=master)](https://ci.gitlab.org/projects/4?ref=master) [![Build Status](https://semaphoreapp.com/api/v1/projects/a71ddd46-a9cc-4062-875e-7ade19a44927/243336/badge.svg)](https://semaphoreapp.com/gitlabhq/gitlab-shell) [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlab-shell.svg)](https://codeclimate.com/github/gitlabhq/gitlab-shell) [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlab-shell/badge.svg?branch=master)](https://coveralls.io/r/gitlabhq/gitlab-shell) ## Requirements **GitLab shell will always use your system ruby (normally located at /usr/bin/ruby) and will not use the ruby your installed with a ruby version manager (such as RVM).** It requires ruby 2.0 or higher. Please uninstall any old ruby versions from your system: ``` sudo apt-get remove ruby1.8 ``` Download Ruby and compile it with: ``` mkdir /tmp/ruby && cd /tmp/ruby curl -L --progress http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.5.tar.gz | tar xz cd ruby-2.1.5 ./configure --disable-install-rdoc make sudo make install ``` ## Setup ./bin/install ## Check ./bin/check ## Repos Add repo: ./bin/gitlab-projects add-project gitlab/gitlab-ci.git Remove repo: ./bin/gitlab-projects rm-project gitlab/gitlab-ci.git List repos: ./bin/gitlab-projects list-projects Import repo: # Default timeout is 2 minutes ./bin/gitlab-projects import-project randx/six.git https://github.com/randx/six.git # Override timeout in seconds ./bin/gitlab-projects import-project randx/six.git https://github.com/randx/six.git 90 Fork repo: ./bin/gitlab-projects fork-project gitlab/gitlab-ci.git randx Update HEAD: ./bin/gitlab-projects update-head gitlab/gitlab-ci.git 3-2-stable Create branch: ./bin/gitlab-projects create-branch gitlab/gitlab-ci.git 3-2-stable master Remove branch: ./bin/gitlab-projects rm-branch gitlab/gitlab-ci.git 3-0-stable Create tag (lightweight & annotated): ./bin/gitlab-projects create-tag gitlab/gitlab-ci.git v3.0.0 3-0-stable ./bin/gitlab-projects create-tag gitlab/gitlab-ci.git v3.0.0 3-0-stable 'annotated message goes here' Remove tag: ./bin/gitlab-projects rm-tag gitlab/gitlab-ci.git v3.0.0 Gc repo: ./bin/gitlab-projects gc gitlab/gitlab-ci.git ## Keys Add key: ./bin/gitlab-keys add-key key-782 "ssh-rsa AAAAx321..." Remove key: ./bin/gitlab-keys rm-key key-23 "ssh-rsa AAAAx321..." List all keys: ./bin/gitlab-keys list-keys Remove all keys from authorized_keys file: ./bin/gitlab-keys clear ## Git LFS remark If you want to play with git-lfs (https://git-lfs.github.com/) on GitLab, you should do the following: * Install LFS-server (no production-ready implementation yet, but you can use https://github.com/github/lfs-test-server) on any host; * Add some user on LFS-server (for example: user ```foo``` with password ```bar```); * Add ```git-lfs-authenticate``` script in any PATH-available directory on GIT-server like this: ``` #!/bin/sh echo "{ \"href\": \"http://lfs.test.local:9999/test/test\", \"header\": { \"Authorization\": \"Basic `echo -n foo:bar | base64`\" } }" ``` After that you can play with git-lfs (git-lfs feature will be available via ssh protocol). This design will work without a script git-lfs-authenticate, but with the following limitations: * You will need to manually configure lfs-server URL for every user working copy; * SSO don't work and you need to manually add lfs-server credentials for every user working copy (otherwise, git-lfs will ask for the password for each file). Usefull links: * https://github.com/github/git-lfs/tree/master/docs/api - Git LFS API, also contains more information about ```git-lfs-authenticate```; * https://github.com/github/git-lfs/wiki/Implementations - Git LFS-server implementations. gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/VERSION000066400000000000000000000000071264274401100227350ustar00rootroot000000000000002.6.10 gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/bin/000077500000000000000000000000001264274401100224405ustar00rootroot00000000000000gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/bin/check000077500000000000000000000015471264274401100234520ustar00rootroot00000000000000#!/usr/bin/env ruby require_relative '../lib/gitlab_init' require_relative '../lib/gitlab_net' # # GitLab shell check task # print "Check GitLab API access: " begin resp = GitlabNet.new.check if resp.code == "200" print 'OK' else abort "FAILED. code: #{resp.code}" end rescue GitlabNet::ApiUnreachableError abort "FAILED: Failed to connect to internal API" end puts "\nCheck directories and files: " config = GitlabConfig.new dirs = [config.repos_path, config.auth_file] dirs.each do |dir| abort("ERROR: missing option in config.yml") unless dir print "\t#{dir}: " if File.exists?(dir) print 'OK' else abort "FAILED" end puts "\n" end print "Test redis-cli executable: " abort('FAILED') unless system(*config.redis_command, '--version') print "Send ping to redis server: " abort unless system(*config.redis_command, 'ping') gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/bin/create-hooks000077500000000000000000000006661264274401100247620ustar00rootroot00000000000000#!/usr/bin/env ruby # Recreate GitLab hooks in the Git repositories managed by GitLab. # # This script is used when restoring a GitLab backup. require_relative '../lib/gitlab_init' require File.join(ROOT_PATH, 'lib', 'gitlab_projects') Dir["#{GitlabConfig.new.repos_path}/*/*.git"].each do |repo| begin GitlabProjects.create_hooks(repo) rescue Errno::ENOENT # The user must have deleted their repository. Ignore. end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/bin/gitlab-keys000077500000000000000000000010611264274401100245770ustar00rootroot00000000000000#!/usr/bin/env ruby require_relative '../lib/gitlab_init' # # GitLab Keys shell. Add/remove keys from ~/.ssh/authorized_keys # # Ex. # /bin/gitlab-keys add-key key-782 "ssh-rsa AAAAx321..." # # printf "key-782\tssh-rsa AAAAx321...\n" | /bin/gitlab-keys batch-add-keys # # /bin/gitlab-keys rm-key key-23 "ssh-rsa AAAAx321..." # # /bin/gitlab-keys list-keys # # /bin/gitlab-keys clear # require File.join(ROOT_PATH, 'lib', 'gitlab_keys') # Return non-zero if command execution was not successful if GitlabKeys.new.exec exit 0 else exit 1 end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/bin/gitlab-projects000077500000000000000000000014051264274401100254570ustar00rootroot00000000000000#!/usr/bin/env ruby require_relative '../lib/gitlab_init' # # GitLab Projects shell. Add/remove projects from /home/git/repositories # # Ex. # /bin/gitlab-projects add-project gitlab/gitlab-ci.git # # /bin/gitlab-projects rm-project gitlab/gitlab-ci.git # # /bin/gitlab-projects list-projects # # /bin/gitlab-projects mv-project gitlab/gitlab-ci.git randx/fork.git # # /bin/gitlab-projects fork-project gitlab/gitlab-ci.git randx # # /bin/gitlab-projects import-project randx/six.git https://github.com/randx/six.git # # /bin/gitlab-projects update-head gitlab/gitlab-ci.git 5-2-stable # require File.join(ROOT_PATH, 'lib', 'gitlab_projects') # Return non-zero if command execution was not successful if GitlabProjects.new.exec exit 0 else exit 1 end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/bin/gitlab-shell000077500000000000000000000006211264274401100247340ustar00rootroot00000000000000#!/usr/bin/env ruby unless ENV['SSH_CONNECTION'] puts "Only ssh allowed" exit end key_id = /key-[0-9]+/.match(ARGV.join).to_s original_cmd = ENV['SSH_ORIGINAL_COMMAND'] require_relative '../lib/gitlab_init' # # # GitLab shell, invoked from ~/.ssh/authorized_keys # # require File.join(ROOT_PATH, 'lib', 'gitlab_shell') if GitlabShell.new(key_id).exec(original_cmd) exit 0 else exit 1 end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/bin/install000077500000000000000000000011001264274401100240240ustar00rootroot00000000000000#!/usr/bin/env ruby require_relative '../lib/gitlab_init' # # GitLab shell, invoked from ~/.ssh/authorized_keys # config = GitlabConfig.new key_dir = File.dirname("#{config.auth_file}") commands = [ %W(mkdir -p #{config.repos_path}), %W(mkdir -p #{key_dir}), %W(chmod 700 #{key_dir}), %W(touch #{config.auth_file}), %W(chmod 600 #{config.auth_file}), %W(chmod ug+rwX,o-rwx #{config.repos_path}), ] commands.each do |cmd| print "#{cmd.join(' ')}: " if system(*cmd) puts 'OK' else puts 'Failed' abort "#{$PROGRAM_NAME} failed" end end exit gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/config.yml.example000066400000000000000000000046221264274401100253160ustar00rootroot00000000000000# # If you change this file in a Merge Request, please also create # a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests # # GitLab user. git by default user: git # Url to gitlab instance. Used for api calls. # Default: http://localhost:8080 # You only have to change the default if you have configured Unicorn # to listen on a custom port, or if you have configured Unicorn to # only listen on a Unix domain socket. For Unix domain sockets use # "http+unix://", e.g. # "http+unix://%2Fpath%2Fto%2Fsocket" gitlab_url: "http://localhost:8080" # See installation.md#using-https for additional HTTPS configuration details. http_settings: # user: someone # password: somepass # ca_file: /etc/ssl/cert.pem # ca_path: /etc/pki/tls/certs self_signed_cert: false # Repositories path # Give the canonicalized absolute pathname, # REPOS_PATH MUST NOT CONTAIN ANY SYMLINK!!! # Check twice that none of the components is a symlink, including "/home". repos_path: "/home/git/repositories" # File used as authorized_keys for gitlab user auth_file: "/home/git/.ssh/authorized_keys" # File that contains the secret key for verifying access to GitLab. # Default is .gitlab_shell_secret in the root directory. # secret_file: "/home/git/gitlab-shell/.gitlab_shell_secret" # Redis settings used for pushing commit notices to gitlab redis: bin: /usr/bin/redis-cli # host: 127.0.0.1 # port: 6379 # pass: redispass # Allows you to specify the password for Redis database: 0 socket: /var/run/redis/redis.sock # Comment out this line if you want to use TCP namespace: resque:gitlab # Log file. # Default is gitlab-shell.log in the root directory. # log_file: "/home/git/gitlab-shell/gitlab-shell.log" # Log level. INFO by default log_level: INFO # Audit usernames. # Set to true to see real usernames in the logs instead of key ids, which is easier to follow, but # incurs an extra API call on every gitlab-shell command. audit_usernames: false # Enable git-annex support # git-annex allows managing files with git, without checking the file contents into git # See https://git-annex.branchable.com/ for documentation # If enabled, git-annex needs to be installed on the server where gitlab-shell is setup # For Debian and Ubuntu systems this can be done with: sudo apt-get install git-annex # For CentOS: sudo yum install epel-release && sudo yum install git-annex git_annex_enabled: false gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/hooks/000077500000000000000000000000001264274401100230135ustar00rootroot00000000000000gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/hooks/post-receive000077500000000000000000000007541264274401100253540ustar00rootroot00000000000000#!/usr/bin/env ruby # This file was placed here by GitLab. It makes sure that your pushed commits # will be processed properly. refs = ARGF.read key_id = ENV['GL_ID'] repo_path = Dir.pwd # reset GL_ID env since we already got its value ENV['GL_ID'] = nil require_relative '../lib/gitlab_custom_hook' require_relative '../lib/gitlab_post_receive' if GitlabPostReceive.new(repo_path, key_id, refs).exec && GitlabCustomHook.new.post_receive(refs, repo_path) exit 0 else exit 1 end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/hooks/pre-receive000077500000000000000000000007431264274401100251530ustar00rootroot00000000000000#!/usr/bin/env ruby # This file was placed here by GitLab. It makes sure that your pushed commits # will be processed properly. refs = ARGF.read key_id = ENV['GL_ID'] repo_path = Dir.pwd require_relative '../lib/gitlab_custom_hook' require_relative '../lib/gitlab_access' if GitlabAccess.new(repo_path, key_id, refs).exec && GitlabCustomHook.new.pre_receive(refs, repo_path) exit 0 else # reset GL_ID env since we stop git push here ENV['GL_ID'] = nil exit 1 end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/hooks/update000077500000000000000000000005451264274401100242270ustar00rootroot00000000000000#!/usr/bin/env ruby # This file was placed here by GitLab. It makes sure that your pushed commits # will be processed properly. ref_name = ARGV[0] old_value = ARGV[1] new_value = ARGV[2] repo_path = Dir.pwd require_relative '../lib/gitlab_custom_hook' if GitlabCustomHook.new.update(ref_name, old_value, new_value, repo_path) exit 0 else exit 1 end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/000077500000000000000000000000001264274401100224365ustar00rootroot00000000000000gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/gitlab_access.rb000066400000000000000000000017311264274401100255500ustar00rootroot00000000000000require_relative 'gitlab_init' require_relative 'gitlab_net' require_relative 'gitlab_access_status' require_relative 'names_helper' require 'json' class GitlabAccess class AccessDeniedError < StandardError; end include NamesHelper attr_reader :config, :repo_path, :repo_name, :changes def initialize(repo_path, actor, changes) @config = GitlabConfig.new @repo_path = repo_path.strip @actor = actor @repo_name = extract_repo_name(@repo_path.dup, config.repos_path.to_s) @changes = changes.lines end def exec status = api.check_access('git-receive-pack', @repo_name, @actor, @changes) raise AccessDeniedError, status.message unless status.allowed? true rescue GitlabNet::ApiUnreachableError $stderr.puts "GitLab: Failed to authorize your Git request: internal API unreachable" false rescue AccessDeniedError => ex $stderr.puts "GitLab: #{ex.message}" false end protected def api GitlabNet.new end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/gitlab_access_status.rb000066400000000000000000000006111264274401100271470ustar00rootroot00000000000000require 'json' class GitAccessStatus attr_accessor :status, :message alias_method :allowed?, :status def initialize(status, message = '') @status = status @message = message end def self.create_from_json(json) values = JSON.parse(json) self.new(values["status"], values["message"]) end def to_json { status: @status, message: @message }.to_json end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/gitlab_config.rb000066400000000000000000000034021264274401100255510ustar00rootroot00000000000000require 'yaml' class GitlabConfig attr_reader :config def initialize @config = YAML.load_file(File.join(ROOT_PATH, 'config.yml')) end def home ENV['HOME'] end def repos_path @config['repos_path'] ||= File.join(home, "repositories") end def auth_file @config['auth_file'] ||= File.join(home, ".ssh/authorized_keys") end def secret_file @config['secret_file'] ||= File.join(ROOT_PATH, '.gitlab_shell_secret') end def gitlab_url (@config['gitlab_url'] ||= "http://localhost:8080").sub(%r{/*$}, '') end def http_settings @config['http_settings'] ||= {} end def redis @config['redis'] ||= {} end def redis_namespace redis['namespace'] || 'resque:gitlab' end def log_file @config['log_file'] ||= File.join(ROOT_PATH, 'gitlab-shell.log') end def log_level @config['log_level'] ||= 'INFO' end def audit_usernames @config['audit_usernames'] ||= false end def git_annex_enabled? @config['git_annex_enabled'] ||= false end # Build redis command to write update event in gitlab queue def redis_command if redis.empty? # Default to old method of connecting to redis # for users that haven't updated their configuration %W(env -i redis-cli) else redis['database'] ||= 0 redis['host'] ||= '127.0.0.1' redis['port'] ||= '6379' if redis.has_key?("socket") %W(#{redis['bin']} -s #{redis['socket']} -n #{redis['database']}) else if redis.has_key?("pass") %W(#{redis['bin']} -h #{redis['host']} -p #{redis['port']} -n #{redis['database']} -a #{redis['pass']}) else %W(#{redis['bin']} -h #{redis['host']} -p #{redis['port']} -n #{redis['database']}) end end end end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/gitlab_custom_hook.rb000066400000000000000000000031331264274401100266370ustar00rootroot00000000000000require 'open3' class GitlabCustomHook def pre_receive(changes, repo_path) hook = hook_file('pre-receive', repo_path) return true if hook.nil? call_receive_hook(hook, changes) end def post_receive(changes, repo_path) hook = hook_file('post-receive', repo_path) return true if hook.nil? call_receive_hook(hook, changes) end def update(ref_name, old_value, new_value, repo_path) hook = hook_file('update', repo_path) return true if hook.nil? system(hook, ref_name, old_value, new_value) end private def call_receive_hook(hook, changes) # function will return true if succesful exit_status = false # we combine both stdout and stderr as we don't know what stream # will be used by the custom hook Open3.popen2e(hook) do |stdin, stdout_stderr, wait_thr| exit_status = true stdin.sync = true # in git, pre- and post- receive hooks may just exit without # reading stdin. We catch the exception to avoid a broken pipe # warning begin # inject all the changes as stdin to the hook changes.lines do |line| stdin.puts(line) end rescue Errno::EPIPE end # need to close stdin before reading stdout stdin.close unless wait_thr.value == 0 exit_status = false end stdout_stderr.each_line { |line| puts line } end exit_status end def hook_file(hook_type, repo_path) hook_path = File.join(repo_path.strip, 'custom_hooks') hook_file = "#{hook_path}/#{hook_type}" hook_file if File.exist?(hook_file) end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/gitlab_init.rb000066400000000000000000000001501264274401100252440ustar00rootroot00000000000000ROOT_PATH = File.expand_path(File.join(File.dirname(__FILE__), "..")) require_relative 'gitlab_config' gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/gitlab_keys.rb000066400000000000000000000056321264274401100252660ustar00rootroot00000000000000require 'timeout' require_relative 'gitlab_config' require_relative 'gitlab_logger' class GitlabKeys attr_accessor :auth_file, :key def initialize @command = ARGV.shift @key_id = ARGV.shift @key = ARGV.shift @auth_file = GitlabConfig.new.auth_file end def exec case @command when 'add-key'; add_key when 'batch-add-keys'; batch_add_keys when 'rm-key'; rm_key when 'list-keys'; puts list_keys when 'clear'; clear else $logger.warn "Attempt to execute invalid gitlab-keys command #{@command.inspect}." puts 'not allowed' false end end protected def add_key lock do $logger.info "Adding key #{@key_id} => #{@key.inspect}" auth_line = key_line(@key_id, @key) open(auth_file, 'a') { |file| file.puts(auth_line) } end true end def list_keys $logger.info 'Listing all keys' keys = '' File.readlines(auth_file).each do |line| # key_id & public_key # command=".../bin/gitlab-shell key-741" ... ssh-rsa AAAAB3NzaDAxx2E\n # ^^^^^^^ ^^^^^^^^^^^^^^^ matches = /^command=\".+?\s+(.+?)\".+?(?:ssh|ecdsa)-.*?\s(.+)\s*.*\n*$/.match(line) keys << "#{matches[1]} #{matches[2]}\n" unless matches.nil? end keys end def batch_add_keys lock(300) do # Allow 300 seconds (5 minutes) for batch_add_keys open(auth_file, 'a') do |file| stdin.each_line do |input| tokens = input.strip.split("\t") abort("#{$0}: invalid input #{input.inspect}") unless tokens.count == 2 key_id, public_key = tokens $logger.info "Adding key #{key_id} => #{public_key.inspect}" file.puts(key_line(key_id, public_key)) end end end true end def stdin $stdin end def key_command(key_id) "#{ROOT_PATH}/bin/gitlab-shell #{key_id}" end def key_line(key_id, public_key) auth_line = "command=\"#{key_command(key_id)}\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty #{public_key}" end def rm_key lock do $logger.info "Removing key #{@key_id}" open(auth_file, 'r+') do |f| while line = f.gets do next unless line.start_with?("command=\"#{key_command(@key_id)}\"") f.seek(-line.length, IO::SEEK_CUR) # Overwrite the line with #'s. Because the 'line' variable contains # a terminating '\n', we write line.length - 1 '#' characters. f.write('#' * (line.length - 1)) end end end true end def clear open(auth_file, 'w') { |file| file.puts '# Managed by gitlab-shell' } true end def lock(timeout = 10) File.open(lock_file, "w+") do |f| begin f.flock File::LOCK_EX Timeout::timeout(timeout) { yield } ensure f.flock File::LOCK_UN end end end def lock_file @lock_file ||= auth_file + '.lock' end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/gitlab_logger.rb000066400000000000000000000006121264274401100255630ustar00rootroot00000000000000require 'logger' require_relative 'gitlab_config' def convert_log_level log_level Logger.const_get(log_level.upcase) rescue NameError $stderr.puts "WARNING: Unrecognized log level #{log_level.inspect}." $stderr.puts "WARNING: Falling back to INFO." Logger::INFO end config = GitlabConfig.new $logger = Logger.new(config.log_file) $logger.level = convert_log_level(config.log_level) gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/gitlab_net.rb000066400000000000000000000065241264274401100251020ustar00rootroot00000000000000require 'net/http' require 'openssl' require 'json' require_relative 'gitlab_config' require_relative 'gitlab_logger' require_relative 'gitlab_access' require_relative 'httpunix' class GitlabNet class ApiUnreachableError < StandardError; end def check_access(cmd, repo, actor, changes) project_name = repo.gsub("'", "") project_name = project_name.gsub(/\.git\Z/, "") project_name = project_name.gsub(/\A\//, "") changes = changes.join("\n") unless changes.kind_of?(String) params = { action: cmd, changes: changes, project: project_name, } if actor =~ /\Akey\-\d+\Z/ params.merge!(key_id: actor.gsub("key-", "")) elsif actor =~ /\Auser\-\d+\Z/ params.merge!(user_id: actor.gsub("user-", "")) end url = "#{host}/allowed" resp = post(url, params) if resp.code == '200' GitAccessStatus.create_from_json(resp.body) else GitAccessStatus.new(false, 'API is not accessible') end end def discover(key) key_id = key.gsub("key-", "") resp = get("#{host}/discover?key_id=#{key_id}") JSON.parse(resp.body) rescue nil end def broadcast_message resp = get("#{host}/broadcast_message") JSON.parse(resp.body) rescue {} end def check get("#{host}/check") end protected def config @config ||= GitlabConfig.new end def host "#{config.gitlab_url}/api/v3/internal" end def http_client_for(uri) if uri.is_a?(URI::HTTPUNIX) http = Net::HTTPUNIX.new(uri.hostname) else http = Net::HTTP.new(uri.host, uri.port) end if uri.is_a?(URI::HTTPS) http.use_ssl = true http.cert_store = cert_store http.verify_mode = OpenSSL::SSL::VERIFY_NONE if config.http_settings['self_signed_cert'] end http end def http_request_for(method, uri, params = {}) request_klass = method == :get ? Net::HTTP::Get : Net::HTTP::Post request = request_klass.new(uri.request_uri) user = config.http_settings['user'] password = config.http_settings['password'] request.basic_auth(user, password) if user && password request.set_form_data(params.merge(secret_token: secret_token)) request end def request(method, url, params = {}) $logger.debug "Performing #{method.to_s.upcase} #{url}" uri = URI.parse(url) http = http_client_for(uri) request = http_request_for(method, uri, params) begin response = http.start { http.request(request) } rescue => e $logger.warn "Failed to connect to internal API <#{method.to_s.upcase} #{url}>: #{e.inspect}" raise ApiUnreachableError end if response.code == "200" $logger.debug "Received response #{response.code} => <#{response.body}>." else $logger.error "API call <#{method.to_s.upcase} #{url}> failed: #{response.code} => <#{response.body}>." end response end def get(url) request(:get, url) end def post(url, params) request(:post, url, params) end def cert_store @cert_store ||= begin store = OpenSSL::X509::Store.new store.set_default_paths if ca_file = config.http_settings['ca_file'] store.add_file(ca_file) end if ca_path = config.http_settings['ca_path'] store.add_path(ca_path) end store end end def secret_token @secret_token ||= File.read config.secret_file end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/gitlab_post_receive.rb000066400000000000000000000046001264274401100267740ustar00rootroot00000000000000require_relative 'gitlab_init' require_relative 'gitlab_net' require 'json' require 'base64' class GitlabPostReceive attr_reader :config, :repo_path, :changes def initialize(repo_path, actor, changes) @config = GitlabConfig.new @repo_path, @actor = repo_path.strip, actor @changes = changes end def exec result = update_redis begin broadcast_message = GitlabNet.new.broadcast_message if broadcast_message.has_key?("message") puts print_broadcast_message(broadcast_message["message"]) end rescue GitlabNet::ApiUnreachableError nil end result end protected def print_broadcast_message(message) # A standard terminal window is (at least) 80 characters wide. total_width = 80 # Git prefixes remote messages with "remote: ", so this width is subtracted # from the width available to us. total_width -= "remote: ".length # Our centered text shouldn't start or end right at the edge of the window, # so we add some horizontal padding: 2 chars on either side. text_width = total_width - 2 * 2 # Automatically wrap message at text_width (= 68) characters: # Splits the message up into the longest possible chunks matching # "". # The last result is always an empty string (0 chars and the end-of-line), # so drop that. # message.scan returns a nested array of capture groups, so flatten. lines = message.scan(/(.{,#{text_width}})(?:\s|$)/)[0...-1].flatten puts "=" * total_width puts lines.each do |line| line.strip! # Center the line by calculating the left padding measured in characters. line_padding = [(total_width - line.length) / 2, 0].max puts((" " * line_padding) + line) end puts puts "=" * total_width end def update_redis # Encode changes as base64 so we don't run into trouble with non-UTF-8 input. changes = Base64.encode64(@changes) queue = "#{config.redis_namespace}:queue:post_receive" msg = JSON.dump({ 'class' => 'PostReceive', 'args' => [@repo_path, @actor, changes] }) if system(*config.redis_command, 'rpush', queue, msg, err: '/dev/null', out: '/dev/null') return true else puts "GitLab: An unexpected error occurred (redis-cli returned #{$?.exitstatus})." return false end end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/gitlab_projects.rb000066400000000000000000000177141264274401100261500ustar00rootroot00000000000000require 'fileutils' require 'timeout' require_relative 'gitlab_config' require_relative 'gitlab_logger' class GitlabProjects GLOBAL_HOOKS_DIRECTORY = File.join(ROOT_PATH, 'hooks') # Project name is a directory name for repository with .git at the end # It may be namespaced or not. Like repo.git or gitlab/repo.git attr_reader :project_name # Absolute path to directory where repositories stored # By default it is /home/git/repositories attr_reader :repos_path # Full path is an absolute path to the repository # Ex /home/git/repositories/test.git attr_reader :full_path def self.create_hooks(path) local_hooks_directory = File.join(path, 'hooks') real_local_hooks_directory = :not_found begin real_local_hooks_directory = File.realpath(local_hooks_directory) rescue Errno::ENOENT # real_local_hooks_directory == :not_found end if real_local_hooks_directory != File.realpath(GLOBAL_HOOKS_DIRECTORY) if File.exist?(local_hooks_directory) $logger.info "Moving existing hooks directory and symlinking global hooks directory for #{path}." FileUtils.mv(local_hooks_directory, "#{local_hooks_directory}.old.#{Time.now.to_i}") end FileUtils.ln_sf(GLOBAL_HOOKS_DIRECTORY, local_hooks_directory) else $logger.info "Hooks already exist for #{path}." true end end def initialize @command = ARGV.shift @project_name = ARGV.shift @repos_path = GitlabConfig.new.repos_path @full_path = File.join(@repos_path, @project_name) unless @project_name.nil? end def exec case @command when 'create-branch'; create_branch when 'rm-branch'; rm_branch when 'create-tag'; create_tag when 'rm-tag'; rm_tag when 'add-project'; add_project when 'list-projects'; puts list_projects when 'rm-project'; rm_project when 'mv-project'; mv_project when 'import-project'; import_project when 'fork-project'; fork_project when 'fetch-remote'; fetch_remote when 'update-head'; update_head when 'gc'; gc else $logger.warn "Attempt to execute invalid gitlab-projects command #{@command.inspect}." puts 'not allowed' false end end protected def create_branch branch_name = ARGV.shift ref = ARGV.shift || "HEAD" cmd = %W(git --git-dir=#{full_path} branch -- #{branch_name} #{ref}) system(*cmd) end def rm_branch branch_name = ARGV.shift cmd = %W(git --git-dir=#{full_path} branch -D -- #{branch_name}) system(*cmd) end def create_tag tag_name = ARGV.shift ref = ARGV.shift || "HEAD" cmd = %W(git --git-dir=#{full_path} tag) if ARGV.size > 0 msg = ARGV.shift cmd += %W(-a -m #{msg}) end cmd += %W(-- #{tag_name} #{ref}) system(*cmd) end def rm_tag tag_name = ARGV.shift cmd = %W(git --git-dir=#{full_path} tag -d -- #{tag_name}) system(*cmd) end def add_project $logger.info "Adding project #{@project_name} at <#{full_path}>." FileUtils.mkdir_p(full_path, mode: 0770) cmd = %W(git --git-dir=#{full_path} init --bare) system(*cmd) && self.class.create_hooks(full_path) end def list_projects $logger.info 'Listing projects' Dir.chdir(repos_path) do next Dir.glob('**/*.git') end end def rm_project $logger.info "Removing project #{@project_name} from <#{full_path}>." FileUtils.rm_rf(full_path) end def mask_password_in_url(url) result = URI(url) result.password = "*****" unless result.password.nil? result.user = "*****" unless result.user.nil? #it's needed for oauth access_token result rescue url end def fetch_remote @name = ARGV.shift # timeout for fetch timeout = (ARGV.shift || 120).to_i $logger.info "Fetching remote #{@name} for project #{@project_name}." cmd = %W(git --git-dir=#{full_path} fetch #{@name} --tags) pid = Process.spawn(*cmd) begin Timeout.timeout(timeout) do Process.wait(pid) end $?.exitstatus.zero? rescue Timeout::Error $logger.error "Fetching remote #{@name} for project #{@project_name} failed due to timeout." Process.kill('KILL', pid) Process.wait false end end def remove_origin_in_repo cmd = %W(git --git-dir=#{full_path} remote rm origin) pid = Process.spawn(*cmd) Process.wait(pid) end # Import project via git clone --bare # URL must be publicly cloneable def import_project # Skip import if repo already exists return false if File.exists?(full_path) @source = ARGV.shift masked_source = mask_password_in_url(@source) # timeout for clone timeout = (ARGV.shift || 120).to_i $logger.info "Importing project #{@project_name} from <#{masked_source}> to <#{full_path}>." cmd = %W(git clone --bare -- #{@source} #{full_path}) pid = Process.spawn(*cmd) begin Timeout.timeout(timeout) do Process.wait(pid) end return false unless $?.exitstatus.zero? rescue Timeout::Error $logger.error "Importing project #{@project_name} from <#{masked_source}> failed due to timeout." Process.kill('KILL', pid) Process.wait FileUtils.rm_rf(full_path) return false end self.class.create_hooks(full_path) # The project was imported successfully. # Remove the origin URL since it may contain password. remove_origin_in_repo true end # Move repository from one directory to another # # Ex. # gitlab.git -> gitlabhq.git # gitlab/gitlab-ci.git -> randx/six.git # # Wont work if target namespace directory does not exist # def mv_project new_path = ARGV.shift unless new_path $logger.error "mv-project failed: no destination path provided." return false end new_full_path = File.join(repos_path, new_path) # verify that the source repo exists unless File.exists?(full_path) $logger.error "mv-project failed: source path <#{full_path}> does not exist." return false end # ...and that the target repo does not exist if File.exists?(new_full_path) $logger.error "mv-project failed: destination path <#{new_full_path}> already exists." return false end $logger.info "Moving project #{@project_name} from <#{full_path}> to <#{new_full_path}>." FileUtils.mv(full_path, new_full_path) end def fork_project new_namespace = ARGV.shift # destination namespace must be provided unless new_namespace $logger.error "fork-project failed: no destination namespace provided." return false end # destination namespace must exist namespaced_path = File.join(repos_path, new_namespace) unless File.exists?(namespaced_path) $logger.error "fork-project failed: destination namespace <#{namespaced_path}> does not exist." return false end # a project of the same name cannot already be within the destination namespace full_destination_path = File.join(namespaced_path, project_name.split('/')[-1]) if File.exists?(full_destination_path) $logger.error "fork-project failed: destination repository <#{full_destination_path}> already exists." return false end $logger.info "Forking project from <#{full_path}> to <#{full_destination_path}>." cmd = %W(git clone --bare -- #{full_path} #{full_destination_path}) system(*cmd) && self.class.create_hooks(full_destination_path) end def update_head new_head = ARGV.shift unless new_head $logger.error "update-head failed: no branch provided." return false end File.open(File.join(full_path, 'HEAD'), 'w') do |f| f.write("ref: refs/heads/#{new_head}") end $logger.info "Update head in project #{project_name} to <#{new_head}>." true end def gc $logger.info "Running git gc for <#{full_path}>." unless File.exists?(full_path) $logger.error "gc failed: destination path <#{full_path}> does not exist." return false end cmd = %W(git --git-dir=#{full_path} gc) system(*cmd) end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/gitlab_shell.rb000066400000000000000000000122561264274401100254220ustar00rootroot00000000000000require 'shellwords' require_relative 'gitlab_net' class GitlabShell class AccessDeniedError < StandardError; end class DisallowedCommandError < StandardError; end class InvalidRepositoryPathError < StandardError; end GIT_COMMANDS = %w(git-upload-pack git-receive-pack git-upload-archive git-annex-shell git-lfs-authenticate).freeze attr_accessor :key_id, :repo_name, :git_cmd, :repos_path, :repo_name def initialize(key_id) @key_id = key_id @config = GitlabConfig.new @repos_path = @config.repos_path end # The origin_cmd variable contains UNTRUSTED input. If the user ran # ssh git@gitlab.example.com 'evil command', then origin_cmd contains # 'evil command'. def exec(origin_cmd) unless origin_cmd puts "Welcome to GitLab, #{username}!" return true end args = Shellwords.shellwords(origin_cmd) parse_cmd(args) verify_access process_cmd(args) true rescue GitlabNet::ApiUnreachableError => ex $stderr.puts "GitLab: Failed to authorize your Git request: internal API unreachable" false rescue AccessDeniedError => ex message = "gitlab-shell: Access denied for git command <#{origin_cmd}> by #{log_username}." $logger.warn message $stderr.puts "GitLab: #{ex.message}" false rescue DisallowedCommandError => ex message = "gitlab-shell: Attempt to execute disallowed command <#{origin_cmd}> by #{log_username}." $logger.warn message $stderr.puts "GitLab: Disallowed command" false rescue InvalidRepositoryPathError => ex $stderr.puts "GitLab: Invalid repository path" false end protected def parse_cmd(args) @git_cmd = args.first @git_access = @git_cmd raise DisallowedCommandError unless GIT_COMMANDS.include?(@git_cmd) case @git_cmd when 'git-annex-shell' raise DisallowedCommandError unless @config.git_annex_enabled? @repo_name = escape_path(args[2].sub(/\A\/~\//, '')) # Make sure repository has git-annex enabled init_git_annex(@repo_name) unless gcryptsetup?(args) when 'git-lfs-authenticate' raise DisallowedCommandError unless args.count >= 2 @repo_name = escape_path(args[1]) case args[2] when 'download' @git_access = 'git-upload-pack' when 'upload' @git_access = 'git-receive-pack' else raise DisallowedCommandError end else raise DisallowedCommandError unless args.count == 2 @repo_name = escape_path(args.last) end end def verify_access status = api.check_access(@git_access, @repo_name, @key_id, '_any') raise AccessDeniedError, status.message unless status.allowed? end def process_cmd(args) repo_full_path = File.join(repos_path, repo_name) if @git_cmd == 'git-annex-shell' raise DisallowedCommandError unless @config.git_annex_enabled? parsed_args = args.map do |arg| # Convert /~/group/project.git to group/project.git # to make git annex path compatible with gitlab-shell if arg =~ /\A\/~\/.*\.git\Z/ repo_full_path else arg end end $logger.info "gitlab-shell: executing git-annex command <#{parsed_args.join(' ')}> for #{log_username}." exec_cmd(*parsed_args) else $logger.info "gitlab-shell: executing git command <#{@git_cmd} #{repo_full_path}> for #{log_username}." exec_cmd(@git_cmd, repo_full_path) end end # This method is not covered by Rspec because it ends the current Ruby process. def exec_cmd(*args) # If you want to call a command without arguments, use # exec_cmd(['my_command', 'my_command']) . Otherwise use # exec_cmd('my_command', 'my_argument', ...). if args.count == 1 && !args.first.is_a?(Array) raise DisallowedCommandError end env = { 'HOME' => ENV['HOME'], 'PATH' => ENV['PATH'], 'LD_LIBRARY_PATH' => ENV['LD_LIBRARY_PATH'], 'LANG' => ENV['LANG'], 'GL_ID' => @key_id } if @config.git_annex_enabled? env.merge!({ 'GIT_ANNEX_SHELL_LIMITED' => '1' }) end Kernel::exec(env, *args, unsetenv_others: true) end def api GitlabNet.new end def user return @user if defined?(@user) begin @user = api.discover(@key_id) rescue GitlabNet::ApiUnreachableError @user = nil end end def username user && user['name'] || 'Anonymous' end # User identifier to be used in log messages. def log_username @config.audit_usernames ? username : "user with key #{@key_id}" end def escape_path(path) full_repo_path = File.join(repos_path, path) if File.absolute_path(full_repo_path) == full_repo_path path else raise InvalidRepositoryPathError end end def init_git_annex(path) full_repo_path = File.join(repos_path, path) unless File.exists?(File.join(full_repo_path, 'annex')) cmd = %W(git --git-dir=#{full_repo_path} annex init "GitLab") system(*cmd, err: '/dev/null', out: '/dev/null') $logger.info "Enable git-annex for repository: #{path}." end end def gcryptsetup?(args) non_dashed = args.reject { |a| a.start_with?('-') } non_dashed[0, 2] == %w{git-annex-shell gcryptsetup} end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/httpunix.rb000066400000000000000000000026661264274401100246600ustar00rootroot00000000000000# support for http+unix://... connection scheme # # The URI scheme has the same structure as the similar one for python requests. See: # http://fixall.online/theres-no-need-to-reinvent-the-wheelhttpsgithubcommsabramorequests-unixsocketurl/241810/ # https://github.com/msabramo/requests-unixsocket require 'uri' require 'net/http' module URI class HTTPUNIX < HTTP def hostname # decode %XX from path to file v = self.host URI.decode(v) end # port is not allowed in URI DEFAULT_PORT = nil def set_port(v) return v unless v raise InvalidURIError, "http+unix:// cannot contain port" end end @@schemes['HTTP+UNIX'] = HTTPUNIX end # Based on: # - http://stackoverflow.com/questions/15637226/ruby-1-9-3-simple-get-request-to-unicorn-through-socket # - Net::HTTP::connect module Net class HTTPUNIX < HTTP def initialize(socketpath, port=nil) super(socketpath, port) @port = nil # HTTP will set it to default - override back -> set DEFAULT_PORT end # override to prevent ":" being appended to HTTP_HOST def addr_port address end def connect D "opening connection to #{address} ..." s = UNIXSocket.new(address) D "opened" @socket = BufferedIO.new(s) @socket.read_timeout = @read_timeout @socket.continue_timeout = @continue_timeout @socket.debug_output = @debug_output on_connect end end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/lib/names_helper.rb000066400000000000000000000004271264274401100254300ustar00rootroot00000000000000module NamesHelper def extract_repo_name(path, base) repo_name = path.strip repo_name.gsub!(base, "") repo_name.gsub!(/\.git$/, "") repo_name.gsub!(/^\//, "") repo_name end def extract_ref_name(ref) ref.gsub(/\Arefs\/(tags|heads)\//, '') end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/000077500000000000000000000000001264274401100226225ustar00rootroot00000000000000gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/gitlab_access_spec.rb000066400000000000000000000025411264274401100267460ustar00rootroot00000000000000require 'spec_helper' require 'gitlab_access' describe GitlabAccess do let(:repository_path) { "/home/git/repositories" } let(:repo_name) { 'dzaporozhets/gitlab-ci' } let(:repo_path) { File.join(repository_path, repo_name) + ".git" } let(:api) do double(GitlabNet).tap do |api| api.stub(check_access: GitAccessStatus.new(true)) end end subject do GitlabAccess.new(repo_path, 'key-123', 'wow').tap do |access| access.stub(exec_cmd: :exec_called) access.stub(api: api) end end before do GitlabConfig.any_instance.stub(repos_path: repository_path) end describe :initialize do it { subject.repo_name.should == repo_name } it { subject.repo_path.should == repo_path } it { subject.changes.should == ['wow'] } end describe "#exec" do context "access is granted" do it "returns true" do expect(subject.exec).to be_true end end context "access is denied" do before do api.stub(check_access: GitAccessStatus.new(false)) end it "returns false" do expect(subject.exec).to be_false end end context "API connection fails" do before do api.stub(:check_access).and_raise(GitlabNet::ApiUnreachableError) end it "returns false" do expect(subject.exec).to be_false end end end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/gitlab_config_spec.rb000066400000000000000000000043631264274401100267560ustar00rootroot00000000000000require_relative 'spec_helper' require_relative '../lib/gitlab_config' describe GitlabConfig do let(:config) { GitlabConfig.new } describe :redis do before do config.instance_variable_set(:@config, YAML.load(< 'localhost', 'port' => 1123, 'bin' => '/usr/bin/redis-cli'} } end it { should be_an(Array) } it { should include(config.redis['host']) } it { should include(config.redis['bin']) } it { should include(config.redis['port'].to_s) } end context "with redis socket" do let(:socket) { '/tmp/redis.socket' } before do config.stub(:redis) { {'bin' => '', 'socket' => socket } } end it { should be_an(Array) } it { should include(socket) } it { should_not include('-p') } it { should_not include('-h') } end end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/gitlab_keys_spec.rb000066400000000000000000000165641264274401100264720ustar00rootroot00000000000000require_relative 'spec_helper' require_relative '../lib/gitlab_keys' require 'stringio' describe GitlabKeys do before do $logger = double('logger').as_null_object end describe :initialize do let(:gitlab_keys) { build_gitlab_keys('add-key', 'key-741', 'ssh-rsa AAAAB3NzaDAxx2E') } it { gitlab_keys.key.should == 'ssh-rsa AAAAB3NzaDAxx2E' } it { gitlab_keys.instance_variable_get(:@command).should == 'add-key' } it { gitlab_keys.instance_variable_get(:@key_id).should == 'key-741' } end describe :add_key do let(:gitlab_keys) { build_gitlab_keys('add-key', 'key-741', 'ssh-rsa AAAAB3NzaDAxx2E') } it "adds a line at the end of the file" do create_authorized_keys_fixture gitlab_keys.send :add_key auth_line = "command=\"#{ROOT_PATH}/bin/gitlab-shell key-741\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaDAxx2E" File.read(tmp_authorized_keys_path).should == "existing content\n#{auth_line}\n" end context "without file writing" do before { gitlab_keys.stub(:open) } before { create_authorized_keys_fixture } it "should log an add-key event" do $logger.should_receive(:info).with('Adding key key-741 => "ssh-rsa AAAAB3NzaDAxx2E"') gitlab_keys.send :add_key end it "should return true" do gitlab_keys.send(:add_key).should be_true end end end describe :list_keys do let(:gitlab_keys) do build_gitlab_keys('add-key', 'key-741', 'ssh-rsa AAAAB3NzaDAxx2E') end it 'adds a key and lists it' do create_authorized_keys_fixture gitlab_keys.send :add_key auth_line1 = 'key-741 AAAAB3NzaDAxx2E' gitlab_keys.send(:list_keys).should == "#{auth_line1}\n" end end describe :batch_add_keys do let(:gitlab_keys) { build_gitlab_keys('batch-add-keys') } let(:fake_stdin) { StringIO.new("key-12\tssh-dsa ASDFASGADG\nkey-123\tssh-rsa GFDGDFSGSDFG\n", 'r') } before do create_authorized_keys_fixture gitlab_keys.stub(stdin: fake_stdin) end it "adds lines at the end of the file" do gitlab_keys.send :batch_add_keys auth_line1 = "command=\"#{ROOT_PATH}/bin/gitlab-shell key-12\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-dsa ASDFASGADG" auth_line2 = "command=\"#{ROOT_PATH}/bin/gitlab-shell key-123\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa GFDGDFSGSDFG" File.read(tmp_authorized_keys_path).should == "existing content\n#{auth_line1}\n#{auth_line2}\n" end context "with invalid input" do let(:fake_stdin) { StringIO.new("key-12\tssh-dsa ASDFASGADG\nkey-123\tssh-rsa GFDGDFSGSDFG\nfoo\tbar\tbaz\n", 'r') } it "aborts" do gitlab_keys.should_receive(:abort) gitlab_keys.send :batch_add_keys end end context "without file writing" do before do gitlab_keys.should_receive(:open).and_yield(mock(:file, puts: nil)) end it "should log an add-key event" do $logger.should_receive(:info).with('Adding key key-12 => "ssh-dsa ASDFASGADG"') $logger.should_receive(:info).with('Adding key key-123 => "ssh-rsa GFDGDFSGSDFG"') gitlab_keys.send :batch_add_keys end it "should return true" do gitlab_keys.send(:batch_add_keys).should be_true end end end describe :stdin do let(:gitlab_keys) { build_gitlab_keys } subject { gitlab_keys.send :stdin } before { $stdin = 1 } it { should equal(1) } end describe :rm_key do let(:gitlab_keys) { build_gitlab_keys('rm-key', 'key-741', 'ssh-rsa AAAAB3NzaDAxx2E') } it "removes the right line" do create_authorized_keys_fixture other_line = "command=\"#{ROOT_PATH}/bin/gitlab-shell key-742\",options ssh-rsa AAAAB3NzaDAxx2E" delete_line = "command=\"#{ROOT_PATH}/bin/gitlab-shell key-741\",options ssh-rsa AAAAB3NzaDAxx2E" open(tmp_authorized_keys_path, 'a') do |auth_file| auth_file.puts delete_line auth_file.puts other_line end gitlab_keys.send :rm_key erased_line = delete_line.gsub(/./, '#') File.read(tmp_authorized_keys_path).should == "existing content\n#{erased_line}\n#{other_line}\n" end context "without file writing" do before do gitlab_keys.stub(:open) gitlab_keys.stub(:lock).and_yield end it "should log an rm-key event" do $logger.should_receive(:info).with('Removing key key-741') gitlab_keys.send :rm_key end it "should return true" do gitlab_keys.send(:rm_key).should be_true end end end describe :clear do let(:gitlab_keys) { build_gitlab_keys('clear') } it "should return true" do gitlab_keys.stub(:open) gitlab_keys.send(:clear).should be_true end end describe :exec do it 'add-key arg should execute add_key method' do gitlab_keys = build_gitlab_keys('add-key') gitlab_keys.should_receive(:add_key) gitlab_keys.exec end it 'batch-add-keys arg should execute batch_add_keys method' do gitlab_keys = build_gitlab_keys('batch-add-keys') gitlab_keys.should_receive(:batch_add_keys) gitlab_keys.exec end it 'rm-key arg should execute rm_key method' do gitlab_keys = build_gitlab_keys('rm-key') gitlab_keys.should_receive(:rm_key) gitlab_keys.exec end it 'clear arg should execute clear method' do gitlab_keys = build_gitlab_keys('clear') gitlab_keys.should_receive(:clear) gitlab_keys.exec end it 'should puts message if unknown command arg' do gitlab_keys = build_gitlab_keys('change-key') gitlab_keys.should_receive(:puts).with('not allowed') gitlab_keys.exec end it 'should log a warning on unknown commands' do gitlab_keys = build_gitlab_keys('nooope') gitlab_keys.stub(puts: nil) $logger.should_receive(:warn).with('Attempt to execute invalid gitlab-keys command "nooope".') gitlab_keys.exec end end describe :lock do before do GitlabKeys.any_instance.stub(lock_file: tmp_lock_file_path) end it "should raise exception if operation lasts more then timeout" do key = GitlabKeys.new expect do key.send :lock, 1 do sleep 2 end end.to raise_error end it "should actually lock file" do $global = "" key = GitlabKeys.new thr1 = Thread.new do key.send :lock do # Put bigger sleep here to test if main thread will # wait for lock file released before executing code sleep 1 $global << "foo" end end # make sure main thread start lock command after # thread above sleep 0.5 key.send :lock do $global << "bar" end thr1.join $global.should == "foobar" end end def build_gitlab_keys(*args) argv(*args) GitlabKeys.new end def argv(*args) args.each_with_index do |arg, i| ARGV[i] = arg end end def create_authorized_keys_fixture FileUtils.mkdir_p(File.dirname(tmp_authorized_keys_path)) open(tmp_authorized_keys_path, 'w') { |file| file.puts('existing content') } gitlab_keys.stub(auth_file: tmp_authorized_keys_path) end def tmp_authorized_keys_path File.join(ROOT_PATH, 'tmp', 'authorized_keys') end def tmp_lock_file_path tmp_authorized_keys_path + '.lock' end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/gitlab_logger_spec.rb000066400000000000000000000004361264274401100267650ustar00rootroot00000000000000require_relative 'spec_helper' require_relative '../lib/gitlab_logger' describe :convert_log_level do subject { convert_log_level :extreme } it "converts invalid log level to Logger::INFO" do $stderr.should_receive(:puts).at_least(:once) should eq(Logger::INFO) end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/gitlab_net_spec.rb000066400000000000000000000151711264274401100262760ustar00rootroot00000000000000require_relative 'spec_helper' require_relative '../lib/gitlab_net' require_relative '../lib/gitlab_access_status' describe GitlabNet, vcr: true do let(:gitlab_net) { GitlabNet.new } let(:changes) { ['0000000000000000000000000000000000000000 92d0970eefd7acb6d548878925ce2208cfe2d2ec refs/heads/branch4'] } before do gitlab_net.stub!(:host).and_return('https://dev.gitlab.org/api/v3/internal') gitlab_net.stub!(:secret_token).and_return('a123') end describe :check do it 'should return 200 code for gitlab check' do VCR.use_cassette("check-ok") do result = gitlab_net.check result.code.should == '200' end end it 'adds the secret_token to request' do VCR.use_cassette("check-ok") do Net::HTTP::Get.any_instance.should_receive(:set_form_data).with(hash_including(secret_token: 'a123')) gitlab_net.check end end it "raises an exception if the connection fails" do Net::HTTP.any_instance.stub(:request).and_raise(StandardError) expect { gitlab_net.check }.to raise_error(GitlabNet::ApiUnreachableError) end end describe :discover do it 'should return user has based on key id' do VCR.use_cassette("discover-ok") do user = gitlab_net.discover('key-126') user['name'].should == 'Dmitriy Zaporozhets' end end it 'adds the secret_token to request' do VCR.use_cassette("discover-ok") do Net::HTTP::Get.any_instance.should_receive(:set_form_data).with(hash_including(secret_token: 'a123')) gitlab_net.discover('key-126') end end it "raises an exception if the connection fails" do VCR.use_cassette("discover-ok") do Net::HTTP.any_instance.stub(:request).and_raise(StandardError) expect { gitlab_net.discover('key-126') }.to raise_error(GitlabNet::ApiUnreachableError) end end end describe :broadcast_message do context "broadcast message exists" do it 'should return message' do VCR.use_cassette("broadcast_message-ok") do result = gitlab_net.broadcast_message result["message"].should == "Message" end end end context "broadcast message doesn't exist" do it 'should return nil' do VCR.use_cassette("broadcast_message-none") do result = gitlab_net.broadcast_message result.should == {} end end end end describe :check_access do context 'ssh key with access to project' do it 'should allow pull access for dev.gitlab.org' do VCR.use_cassette("allowed-pull") do access = gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-126', changes) access.allowed?.should be_true end end it 'adds the secret_token to the request' do VCR.use_cassette("allowed-pull") do Net::HTTP::Post.any_instance.should_receive(:set_form_data).with(hash_including(secret_token: 'a123')) gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-126', changes) end end it 'should allow push access for dev.gitlab.org' do VCR.use_cassette("allowed-push") do access = gitlab_net.check_access('git-upload-pack', 'gitlab/gitlabhq.git', 'key-126', changes) access.allowed?.should be_true end end end context 'ssh key without access to project' do it 'should deny pull access for dev.gitlab.org' do VCR.use_cassette("denied-pull") do access = gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-2', changes) access.allowed?.should be_false end end it 'should deny push access for dev.gitlab.org' do VCR.use_cassette("denied-push") do access = gitlab_net.check_access('git-upload-pack', 'gitlab/gitlabhq.git', 'key-2', changes) access.allowed?.should be_false end end it 'should deny push access for dev.gitlab.org (with user)' do VCR.use_cassette("denied-push-with-user") do access = gitlab_net.check_access('git-upload-pack', 'gitlab/gitlabhq.git', 'user-1', changes) access.allowed?.should be_false end end end it "raises an exception if the connection fails" do Net::HTTP.any_instance.stub(:request).and_raise(StandardError) expect { gitlab_net.check_access('git-upload-pack', 'gitlab/gitlabhq.git', 'user-1', changes) }.to raise_error(GitlabNet::ApiUnreachableError) end end describe :host do let(:net) { GitlabNet.new } subject { net.send :host } it { should include(net.send(:config).gitlab_url) } it("uses API version 3") { should include("api/v3") } end describe :http_client_for do subject { gitlab_net.send :http_client_for, URI('https://localhost/') } before do gitlab_net.stub! :cert_store gitlab_net.send(:config).http_settings.stub(:[]).with('self_signed_cert') { true } end its(:verify_mode) { should eq(OpenSSL::SSL::VERIFY_NONE) } end describe :http_request_for do let(:get) do double(Net::HTTP::Get).tap do |get| Net::HTTP::Get.stub(:new) { get } end end let(:user) { 'user' } let(:password) { 'password' } let(:url) { URI 'http://localhost/' } subject { gitlab_net.send :http_request_for, :get, url } before do gitlab_net.send(:config).http_settings.stub(:[]).with('user') { user } gitlab_net.send(:config).http_settings.stub(:[]).with('password') { password } get.should_receive(:basic_auth).with(user, password).once get.should_receive(:set_form_data).with(hash_including(secret_token: 'a123')).once end it { should_not be_nil } end describe :cert_store do let(:store) do double(OpenSSL::X509::Store).tap do |store| OpenSSL::X509::Store.stub(:new) { store } end end before :each do store.should_receive(:set_default_paths).once end after do gitlab_net.send :cert_store end it "calls add_file with http_settings['ca_file']" do gitlab_net.send(:config).http_settings.stub(:[]).with('ca_file') { 'test_file' } gitlab_net.send(:config).http_settings.stub(:[]).with('ca_path') { nil } store.should_receive(:add_file).with('test_file') store.should_not_receive(:add_path) end it "calls add_path with http_settings['ca_path']" do gitlab_net.send(:config).http_settings.stub(:[]).with('ca_file') { nil } gitlab_net.send(:config).http_settings.stub(:[]).with('ca_path') { 'test_path' } store.should_not_receive(:add_file) store.should_receive(:add_path).with('test_path') end end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/gitlab_post_receive_spec.rb000066400000000000000000000055141264274401100301770ustar00rootroot00000000000000require 'spec_helper' require 'gitlab_post_receive' describe GitlabPostReceive do let(:repository_path) { "/home/git/repositories" } let(:repo_name) { 'dzaporozhets/gitlab-ci' } let(:actor) { 'key-123' } let(:changes) { "123456 789012 refs/heads/tést\n654321 210987 refs/tags/tag" } let(:wrongly_encoded_changes) { changes.encode("ISO-8859-1").force_encoding("UTF-8") } let(:base64_changes) { Base64.encode64(wrongly_encoded_changes) } let(:repo_path) { File.join(repository_path, repo_name) + ".git" } let(:gitlab_post_receive) { GitlabPostReceive.new(repo_path, actor, wrongly_encoded_changes) } let(:message) { "test " * 10 + "message " * 10 } before do GitlabConfig.any_instance.stub(repos_path: repository_path) GitlabNet.any_instance.stub(broadcast_message: { "message" => message }) end describe "#exec" do before do GitlabConfig.any_instance.stub(redis_command: %w(env -i redis-cli)) allow(gitlab_post_receive).to receive(:system).and_return(true) end it "prints the broadcast message" do expect(gitlab_post_receive).to receive(:puts).ordered expect(gitlab_post_receive).to receive(:puts).with( "========================================================================" ).ordered expect(gitlab_post_receive).to receive(:puts).ordered expect(gitlab_post_receive).to receive(:puts).with( " test test test test test test test test test test message message" ).ordered expect(gitlab_post_receive).to receive(:puts).with( " message message message message message message message message" ).ordered expect(gitlab_post_receive).to receive(:puts).ordered expect(gitlab_post_receive).to receive(:puts).with( "========================================================================" ).ordered gitlab_post_receive.exec end it "pushes a Sidekiq job onto the queue" do expect(gitlab_post_receive).to receive(:system).with( *[ *%w(env -i redis-cli rpush resque:gitlab:queue:post_receive), %Q/{"class":"PostReceive","args":["#{repo_path}","#{actor}",#{base64_changes.inspect}]}/, { err: "/dev/null", out: "/dev/null" } ] ).and_return(true) gitlab_post_receive.exec end context "when the redis command succeeds" do before do allow(gitlab_post_receive).to receive(:system).and_return(true) end it "returns true" do expect(gitlab_post_receive.exec).to eq(true) end end context "when the redis command fails" do before do allow(gitlab_post_receive).to receive(:system).and_return(false) allow($?).to receive(:exitstatus).and_return(nil) end it "returns false" do expect(gitlab_post_receive.exec).to eq(false) end end end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/gitlab_projects_spec.rb000066400000000000000000000345271264274401100273470ustar00rootroot00000000000000require_relative 'spec_helper' require_relative '../lib/gitlab_projects' describe GitlabProjects do before do GitlabConfig.any_instance.stub(repos_path: tmp_repos_path) FileUtils.mkdir_p(tmp_repos_path) $logger = double('logger').as_null_object end after do FileUtils.rm_rf(tmp_repos_path) end describe :create_hooks do let(:repo_path) { File.join(tmp_repos_path, 'hook-test.git') } let(:hooks_dir) { File.join(repo_path, 'hooks') } let(:target_hooks_dir) { File.join(ROOT_PATH, 'hooks') } let(:existing_target) { File.join(repo_path, 'foobar') } before do FileUtils.rm_rf(repo_path) FileUtils.mkdir_p(repo_path) end context 'hooks is a directory' do let(:existing_file) { File.join(hooks_dir, 'my-file') } before do FileUtils.mkdir_p(hooks_dir) FileUtils.touch(existing_file) GitlabProjects.create_hooks(repo_path) end it { File.readlink(hooks_dir).should == target_hooks_dir } it { Dir[File.join(repo_path, "hooks.old.*/my-file")].count.should == 1 } end context 'hooks is a valid symlink' do before do FileUtils.mkdir_p existing_target File.symlink(existing_target, hooks_dir) GitlabProjects.create_hooks(repo_path) end it { File.readlink(hooks_dir).should == target_hooks_dir } end context 'hooks is a broken symlink' do before do FileUtils.rm_f(existing_target) File.symlink(existing_target, hooks_dir) GitlabProjects.create_hooks(repo_path) end it { File.readlink(hooks_dir).should == target_hooks_dir } end end describe :initialize do before do argv('add-project', repo_name) @gl_projects = GitlabProjects.new end it { @gl_projects.project_name.should == repo_name } it { @gl_projects.instance_variable_get(:@command).should == 'add-project' } it { @gl_projects.instance_variable_get(:@full_path).should == "#{GitlabConfig.new.repos_path}/gitlab-ci.git" } end describe :create_branch do let(:gl_projects_create) { build_gitlab_projects('import-project', repo_name, 'https://github.com/randx/six.git') } let(:gl_projects) { build_gitlab_projects('create-branch', repo_name, 'test_branch', 'master') } it "should create a branch" do gl_projects_create.exec gl_projects.exec branch_ref = capture_in_tmp_repo(%W(git rev-parse test_branch)) master_ref = capture_in_tmp_repo(%W(git rev-parse master)) branch_ref.should == master_ref end end describe :rm_branch do let(:gl_projects_create) { build_gitlab_projects('import-project', repo_name, 'https://github.com/randx/six.git') } let(:gl_projects_create_branch) { build_gitlab_projects('create-branch', repo_name, 'test_branch', 'master') } let(:gl_projects) { build_gitlab_projects('rm-branch', repo_name, 'test_branch') } it "should remove a branch" do gl_projects_create.exec gl_projects_create_branch.exec branch_ref = capture_in_tmp_repo(%W(git rev-parse test_branch)) gl_projects.exec branch_del = capture_in_tmp_repo(%W(git rev-parse test_branch)) branch_del.should_not == branch_ref end end describe :create_tag do let(:gl_projects_create) { build_gitlab_projects('import-project', repo_name, 'https://github.com/randx/six.git') } context "lightweight tag" do let(:gl_projects) { build_gitlab_projects('create-tag', repo_name, 'test_tag', 'master') } it "should create a tag" do gl_projects_create.exec gl_projects.exec tag_ref = capture_in_tmp_repo(%W(git rev-parse test_tag)) master_ref = capture_in_tmp_repo(%W(git rev-parse master)) tag_ref.should == master_ref end end context "annotated tag" do msg = 'some message' tag_name = 'test_annotated_tag' let(:gl_projects) { build_gitlab_projects('create-tag', repo_name, tag_name, 'master', msg) } it "should create an annotated tag" do gl_projects_create.exec system(*%W(git --git-dir=#{tmp_repo_path} config user.name Joe)) system(*%W(git --git-dir=#{tmp_repo_path} config user.email joe@smith.com)) gl_projects.exec tag_ref = capture_in_tmp_repo(%W(git rev-parse #{tag_name}^{})) master_ref = capture_in_tmp_repo(%W(git rev-parse master)) tag_msg = capture_in_tmp_repo(%W(git tag -l -n1 #{tag_name})) tag_ref.should == master_ref tag_msg.should == tag_name + ' ' + msg end end end describe :rm_tag do let(:gl_projects_create) { build_gitlab_projects('import-project', repo_name, 'https://github.com/randx/six.git') } let(:gl_projects_create_tag) { build_gitlab_projects('create-tag', repo_name, 'test_tag', 'master') } let(:gl_projects) { build_gitlab_projects('rm-tag', repo_name, 'test_tag') } it "should remove a branch" do gl_projects_create.exec gl_projects_create_tag.exec branch_ref = capture_in_tmp_repo(%W(git rev-parse test_tag)) gl_projects.exec branch_del = capture_in_tmp_repo(%W(git rev-parse test_tag)) branch_del.should_not == branch_ref end end describe :add_project do let(:gl_projects) { build_gitlab_projects('add-project', repo_name) } it "should create a directory" do gl_projects.stub(system: true) GitlabProjects.stub(create_hooks: true) gl_projects.exec File.exists?(tmp_repo_path).should be_true end it "should receive valid cmd" do valid_cmd = ['git', "--git-dir=#{tmp_repo_path}", 'init', '--bare'] gl_projects.should_receive(:system).with(*valid_cmd).and_return(true) GitlabProjects.should_receive(:create_hooks).with(tmp_repo_path) gl_projects.exec end it "should log an add-project event" do $logger.should_receive(:info).with("Adding project #{repo_name} at <#{tmp_repo_path}>.") gl_projects.exec end end describe :list_projects do let(:gl_projects) do build_gitlab_projects('add-project', "list_test/#{repo_name}") end before do FileUtils.mkdir_p(tmp_repos_path) end it 'should create projects and list them' do GitlabProjects.stub(create_hooks: true) gl_projects.exec gl_projects.send(:list_projects).should == ["list_test/#{repo_name}"] end end describe :mv_project do let(:gl_projects) { build_gitlab_projects('mv-project', repo_name, 'repo.git') } let(:new_repo_path) { File.join(tmp_repos_path, 'repo.git') } before do FileUtils.mkdir_p(tmp_repo_path) end it "should move a repo directory" do File.exists?(tmp_repo_path).should be_true gl_projects.exec File.exists?(tmp_repo_path).should be_false File.exists?(new_repo_path).should be_true end it "should fail if no destination path is provided" do incomplete = build_gitlab_projects('mv-project', repo_name) $logger.should_receive(:error).with("mv-project failed: no destination path provided.") incomplete.exec.should be_false end it "should fail if the source path doesn't exist" do bad_source = build_gitlab_projects('mv-project', 'bad-src.git', 'dest.git') $logger.should_receive(:error).with("mv-project failed: source path <#{tmp_repos_path}/bad-src.git> does not exist.") bad_source.exec.should be_false end it "should fail if the destination path already exists" do FileUtils.mkdir_p(File.join(tmp_repos_path, 'already-exists.git')) bad_dest = build_gitlab_projects('mv-project', repo_name, 'already-exists.git') message = "mv-project failed: destination path <#{tmp_repos_path}/already-exists.git> already exists." $logger.should_receive(:error).with(message) bad_dest.exec.should be_false end it "should log an mv-project event" do message = "Moving project #{repo_name} from <#{tmp_repo_path}> to <#{new_repo_path}>." $logger.should_receive(:info).with(message) gl_projects.exec end end describe :rm_project do let(:gl_projects) { build_gitlab_projects('rm-project', repo_name) } before do FileUtils.mkdir_p(tmp_repo_path) end it "should remove a repo directory" do File.exists?(tmp_repo_path).should be_true gl_projects.exec File.exists?(tmp_repo_path).should be_false end it "should log an rm-project event" do $logger.should_receive(:info).with("Removing project #{repo_name} from <#{tmp_repo_path}>.") gl_projects.exec end end describe :update_head do let(:gl_projects) { build_gitlab_projects('update-head', repo_name, 'stable') } let(:gl_projects_fail) { build_gitlab_projects 'update-head', repo_name } before do FileUtils.mkdir_p(tmp_repo_path) system(*%W(git init --bare #{tmp_repo_path})) FileUtils.touch(File.join(tmp_repo_path, "refs/heads/stable")) File.read(File.join(tmp_repo_path, 'HEAD')).strip.should == 'ref: refs/heads/master' end it "should update head for repo" do gl_projects.exec.should be_true File.read(File.join(tmp_repo_path, 'HEAD')).strip.should == 'ref: refs/heads/stable' end it "should log an update_head event" do $logger.should_receive(:info).with("Update head in project #{repo_name} to .") gl_projects.exec end it "should failed and log an error" do $logger.should_receive(:error).with("update-head failed: no branch provided.") gl_projects_fail.exec.should be_false end end describe :import_project do context 'success import' do let(:gl_projects) { build_gitlab_projects('import-project', repo_name, 'https://github.com/randx/six.git') } it { gl_projects.exec.should be_true } it "should import a repo" do gl_projects.exec File.exists?(File.join(tmp_repo_path, 'HEAD')).should be_true end it "should log an import-project event" do message = "Importing project #{repo_name} from to <#{tmp_repo_path}>." $logger.should_receive(:info).with(message) gl_projects.exec end end context 'already exists' do let(:gl_projects) { build_gitlab_projects('import-project', repo_name, 'https://github.com/randx/six.git') } it 'should import only once' do gl_projects.exec.should be_true gl_projects.exec.should be_false end end context 'timeout' do let(:gl_projects) { build_gitlab_projects('import-project', repo_name, 'https://github.com/gitlabhq/gitlabhq.git', '1') } it { gl_projects.exec.should be_false } it "should not import a repo" do gl_projects.exec File.exists?(File.join(tmp_repo_path, 'HEAD')).should be_false end it "should log an import-project event" do message = "Importing project #{repo_name} from failed due to timeout." $logger.should_receive(:error).with(message) gl_projects.exec end end end describe :fork_project do let(:source_repo_name) { File.join('source-namespace', repo_name) } let(:dest_repo) { File.join(tmp_repos_path, 'forked-to-namespace', repo_name) } let(:gl_projects_fork) { build_gitlab_projects('fork-project', source_repo_name, 'forked-to-namespace') } let(:gl_projects_import) { build_gitlab_projects('import-project', source_repo_name, 'https://github.com/randx/six.git') } before do gl_projects_import.exec end it "should not fork without a destination namespace" do missing_arg = build_gitlab_projects('fork-project', source_repo_name) $logger.should_receive(:error).with("fork-project failed: no destination namespace provided.") missing_arg.exec.should be_false end it "should not fork into a namespace that doesn't exist" do message = "fork-project failed: destination namespace <#{tmp_repos_path}/forked-to-namespace> does not exist." $logger.should_receive(:error).with(message) gl_projects_fork.exec.should be_false end it "should fork the repo" do # create destination namespace FileUtils.mkdir_p(File.join(tmp_repos_path, 'forked-to-namespace')) gl_projects_fork.exec.should be_true File.exists?(dest_repo).should be_true File.exists?(File.join(dest_repo, '/hooks/pre-receive')).should be_true File.exists?(File.join(dest_repo, '/hooks/post-receive')).should be_true end it "should not fork if a project of the same name already exists" do # create a fake project at the intended destination FileUtils.mkdir_p(File.join(tmp_repos_path, 'forked-to-namespace', repo_name)) # trying to fork again should fail as the repo already exists message = "fork-project failed: destination repository <#{tmp_repos_path}/forked-to-namespace/#{repo_name}> " message << "already exists." $logger.should_receive(:error).with(message) gl_projects_fork.exec.should be_false end it "should log a fork-project event" do message = "Forking project from <#{File.join(tmp_repos_path, source_repo_name)}> to <#{dest_repo}>." $logger.should_receive(:info).with(message) # create destination namespace FileUtils.mkdir_p(File.join(tmp_repos_path, 'forked-to-namespace')) gl_projects_fork.exec.should be_true end end describe :exec do it 'should puts message if unknown command arg' do gitlab_projects = build_gitlab_projects('edit-project', repo_name) gitlab_projects.should_receive(:puts).with('not allowed') gitlab_projects.exec end it 'should log a warning for unknown commands' do gitlab_projects = build_gitlab_projects('hurf-durf', repo_name) $logger.should_receive(:warn).with('Attempt to execute invalid gitlab-projects command "hurf-durf".') gitlab_projects.exec end end def build_gitlab_projects(*args) argv(*args) gl_projects = GitlabProjects.new gl_projects.stub(repos_path: tmp_repos_path) gl_projects.stub(full_path: File.join(tmp_repos_path, gl_projects.project_name)) gl_projects end def argv(*args) args.each_with_index do |arg, i| ARGV[i] = arg end end def tmp_repos_path File.join(ROOT_PATH, 'tmp', 'repositories') end def tmp_repo_path File.join(tmp_repos_path, repo_name) end def repo_name 'gitlab-ci.git' end def capture_in_tmp_repo(cmd) IO.popen([*cmd, {chdir: tmp_repo_path}]).read.strip end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/gitlab_shell_spec.rb000066400000000000000000000174271264274401100266250ustar00rootroot00000000000000require_relative 'spec_helper' require_relative '../lib/gitlab_shell' require_relative '../lib/gitlab_access_status' describe GitlabShell do before do FileUtils.mkdir_p(tmp_repos_path) end after do FileUtils.rm_rf(tmp_repos_path) end subject do ARGV[0] = key_id GitlabShell.new(key_id).tap do |shell| shell.stub(exec_cmd: :exec_called) shell.stub(api: api) end end let(:api) do double(GitlabNet).tap do |api| api.stub(discover: { 'name' => 'John Doe' }) api.stub(check_access: GitAccessStatus.new(true)) end end let(:key_id) { "key-#{rand(100) + 100}" } let(:ssh_cmd) { nil } let(:tmp_repos_path) { File.join(ROOT_PATH, 'tmp', 'repositories') } before do GitlabConfig.any_instance.stub(repos_path: tmp_repos_path, audit_usernames: false) end describe :initialize do let(:ssh_cmd) { 'git-receive-pack' } its(:key_id) { should == key_id } its(:repos_path) { should == tmp_repos_path } end describe :parse_cmd do describe 'git' do context 'w/o namespace' do let(:ssh_args) { %W(git-upload-pack gitlab-ci.git) } before do subject.send :parse_cmd, ssh_args end its(:repo_name) { should == 'gitlab-ci.git' } its(:git_cmd) { should == 'git-upload-pack' } end context 'namespace' do let(:ssh_args) { %W(git-upload-pack dmitriy.zaporozhets/gitlab-ci.git) } before do subject.send :parse_cmd, ssh_args end its(:repo_name) { should == 'dmitriy.zaporozhets/gitlab-ci.git' } its(:git_cmd) { should == 'git-upload-pack' } end context 'with an invalid number of arguments' do let(:ssh_args) { %W(foobar) } it "should raise an DisallowedCommandError" do expect { subject.send :parse_cmd, ssh_args }.to raise_error(GitlabShell::DisallowedCommandError) end end end describe 'git-annex' do let(:repo_path) { File.join(tmp_repos_path, 'dzaporozhets/gitlab.git') } let(:ssh_args) { %W(git-annex-shell inannex /~/dzaporozhets/gitlab.git SHA256E) } before do GitlabConfig.any_instance.stub(git_annex_enabled?: true) # Create existing project FileUtils.mkdir_p(repo_path) cmd = %W(git --git-dir=#{repo_path} init --bare) system(*cmd) subject.send :parse_cmd, ssh_args end its(:repo_name) { should == 'dzaporozhets/gitlab.git' } its(:git_cmd) { should == 'git-annex-shell' } it 'should init git-annex' do File.exists?(File.join(tmp_repos_path, 'dzaporozhets/gitlab.git/annex')).should be_true end context 'with git-annex-shell gcryptsetup' do let(:ssh_args) { %W(git-annex-shell gcryptsetup /~/dzaporozhets/gitlab.git) } it 'should not init git-annex' do File.exists?(File.join(tmp_repos_path, 'dzaporozhets/gitlab.git/annex')).should be_false end end end end describe :exec do context 'git-upload-pack' do let(:ssh_cmd) { 'git-upload-pack gitlab-ci.git' } after { subject.exec(ssh_cmd) } it "should process the command" do subject.should_receive(:process_cmd).with(%W(git-upload-pack gitlab-ci.git)) end it "should execute the command" do subject.should_receive(:exec_cmd).with("git-upload-pack", File.join(tmp_repos_path, 'gitlab-ci.git')) end it "should log the command execution" do message = "gitlab-shell: executing git command " message << " " message << "for user with key #{key_id}." $logger.should_receive(:info).with(message) end it "should use usernames if configured to do so" do GitlabConfig.any_instance.stub(audit_usernames: true) $logger.should_receive(:info) { |msg| msg.should =~ /for John Doe/ } end end context 'git-receive-pack' do let(:ssh_cmd) { 'git-receive-pack gitlab-ci.git' } after { subject.exec(ssh_cmd) } it "should process the command" do subject.should_receive(:process_cmd).with(%W(git-receive-pack gitlab-ci.git)) end it "should execute the command" do subject.should_receive(:exec_cmd).with("git-receive-pack", File.join(tmp_repos_path, 'gitlab-ci.git')) end it "should log the command execution" do message = "gitlab-shell: executing git command " message << " " message << "for user with key #{key_id}." $logger.should_receive(:info).with(message) end end context 'arbitrary command' do let(:ssh_cmd) { 'arbitrary command' } after { subject.exec(ssh_cmd) } it "should not process the command" do subject.should_not_receive(:process_cmd) end it "should not execute the command" do subject.should_not_receive(:exec_cmd) end it "should log the attempt" do message = "gitlab-shell: Attempt to execute disallowed command by user with key #{key_id}." $logger.should_receive(:warn).with(message) end end context 'no command' do after { subject.exec(nil) } it "should call api.discover" do api.should_receive(:discover).with(key_id) end end context "failed connection" do let(:ssh_cmd) { 'git-upload-pack gitlab-ci.git' } before { api.stub(:check_access).and_raise(GitlabNet::ApiUnreachableError) } after { subject.exec(ssh_cmd) } it "should not process the command" do subject.should_not_receive(:process_cmd) end it "should not execute the command" do subject.should_not_receive(:exec_cmd) end end describe 'git-annex' do let(:ssh_cmd) { 'git-annex-shell commit /~/gitlab-ci.git SHA256' } before do GitlabConfig.any_instance.stub(git_annex_enabled?: true) end after { subject.exec(ssh_cmd) } it "should execute the command" do subject.should_receive(:exec_cmd).with("git-annex-shell", "commit", File.join(tmp_repos_path, 'gitlab-ci.git'), "SHA256") end end end describe :validate_access do let(:ssh_cmd) { 'git-upload-pack gitlab-ci.git' } after { subject.exec(ssh_cmd) } it "should call api.check_access" do api.should_receive(:check_access). with('git-upload-pack', 'gitlab-ci.git', key_id, '_any') end it "should disallow access and log the attempt if check_access returns false status" do api.stub(check_access: GitAccessStatus.new(false)) message = "gitlab-shell: Access denied for git command " message << "by user with key #{key_id}." $logger.should_receive(:warn).with(message) end end describe :exec_cmd do let(:shell) { GitlabShell.new(key_id) } before { Kernel.stub!(:exec) } it "uses Kernel::exec method" do Kernel.should_receive(:exec).with(kind_of(Hash), 1, 2, unsetenv_others: true).once shell.send :exec_cmd, 1, 2 end it "refuses to execute a lone non-array argument" do expect { shell.send :exec_cmd, 1 }.to raise_error(GitlabShell::DisallowedCommandError) end it "allows one argument if it is an array" do Kernel.should_receive(:exec).with(kind_of(Hash), [1, 2], unsetenv_others: true).once shell.send :exec_cmd, [1, 2] end end describe :api do let(:shell) { GitlabShell.new(key_id) } subject { shell.send :api } it { should be_a(GitlabNet) } end describe :escape_path do let(:shell) { GitlabShell.new(key_id) } before { File.stub(:absolute_path) { 'y' } } subject { -> { shell.send(:escape_path, 'z') } } it { should raise_error(GitlabShell::InvalidRepositoryPathError) } end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/httpunix_spec.rb000066400000000000000000000027051264274401100260500ustar00rootroot00000000000000require_relative 'spec_helper' require_relative '../lib/httpunix' require 'webrick' describe URI::HTTPUNIX do describe :parse do uri = URI::parse('http+unix://%2Fpath%2Fto%2Fsocket/img.jpg') subject { uri } it { should be_an_instance_of(URI::HTTPUNIX) } its(:scheme) { should eq('http+unix') } its(:hostname) { should eq('/path/to/socket') } its(:path) { should eq('/img.jpg') } end end # like WEBrick::HTTPServer, but listens on UNIX socket class HTTPUNIXServer < WEBrick::HTTPServer def listen(address, port) socket = Socket.unix_server_socket(address) socket.autoclose = false server = UNIXServer.for_fd(socket.fileno) socket.close @listeners << server end end def tmp_socket_path File.join(ROOT_PATH, 'tmp', 'socket') end describe Net::HTTPUNIX do # "hello world" over unix socket server in background thread FileUtils.mkdir_p(File.dirname(tmp_socket_path)) server = HTTPUNIXServer.new(:BindAddress => tmp_socket_path) server.mount_proc '/' do |req, resp| resp.body = "Hello World (at #{req.path})" end Thread.start { server.start } it "talks via HTTP ok" do VCR.turned_off do begin WebMock.allow_net_connect! http = Net::HTTPUNIX.new(tmp_socket_path) expect(http.get('/').body).to eq('Hello World (at /)') expect(http.get('/path').body).to eq('Hello World (at /path)') ensure WebMock.disable_net_connect! end end end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/names_helper_spec.rb000066400000000000000000000011521264274401100266220ustar00rootroot00000000000000require 'spec_helper' require 'names_helper' describe NamesHelper do include NamesHelper describe :extract_repo_name do it { extract_repo_name(' /opt/repos/randx/gitlab.git', '/opt/repos').should == 'randx/gitlab' } it { extract_repo_name("/opt/repos/randx/gitlab.git\r\n", '/opt/repos/').should == 'randx/gitlab' } end describe :extract_ref_name do it { extract_ref_name('refs/heads/awesome-feature').should == 'awesome-feature' } it { extract_ref_name('refs/tags/v2.2.1').should == 'v2.2.1' } it { extract_ref_name('refs/tags/releases/v2.2.1').should == 'releases/v2.2.1' } end end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/spec_helper.rb000066400000000000000000000005241264274401100254410ustar00rootroot00000000000000ROOT_PATH = File.expand_path(File.join(File.dirname(__FILE__), "..")) if ENV['COVERALLS'] require 'coveralls' Coveralls.wear! else require 'simplecov' SimpleCov.start end require 'vcr' require 'webmock' VCR.configure do |c| c.cassette_library_dir = 'spec/vcr_cassettes' c.hook_into :webmock c.configure_rspec_metadata! end gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/vcr_cassettes/000077500000000000000000000000001264274401100254725ustar00rootroot00000000000000gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/vcr_cassettes/allowed-pull.yml000066400000000000000000000023211264274401100306140ustar00rootroot00000000000000--- http_interactions: - request: method: post uri: https://dev.gitlab.org/api/v3/internal/allowed body: encoding: US-ASCII string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&project=gitlab%2Fgitlabhq&key_id=126&secret_token=a123 headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: - "*/*" User-Agent: - Ruby Content-Type: - application/x-www-form-urlencoded response: status: code: 200 message: OK headers: Server: - nginx/1.1.19 Date: - Wed, 03 Sep 2014 11:27:36 GMT Content-Type: - application/json Content-Length: - '4' Connection: - keep-alive Status: - 200 OK Etag: - '"b326b5062b2f0e69046810717534cb09"' Cache-Control: - max-age=0, private, must-revalidate X-Request-Id: - 85bfa9b3-fddc-4810-a033-f1eabc04c66c X-Runtime: - '0.089741' body: encoding: UTF-8 string: '{"status": "true"}' http_version: recorded_at: Wed, 03 Sep 2014 11:27:36 GMT recorded_with: VCR 2.4.0 gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/vcr_cassettes/allowed-push.yml000066400000000000000000000023201264274401100306160ustar00rootroot00000000000000--- http_interactions: - request: method: post uri: https://dev.gitlab.org/api/v3/internal/allowed body: encoding: US-ASCII string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&project=gitlab%2Fgitlabhq&key_id=126&secret_token=a123 headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: - "*/*" User-Agent: - Ruby Content-Type: - application/x-www-form-urlencoded response: status: code: 200 message: OK headers: Server: - nginx/1.1.19 Date: - Wed, 03 Sep 2014 11:27:37 GMT Content-Type: - application/json Content-Length: - '4' Connection: - keep-alive Status: - 200 OK Etag: - '"b326b5062b2f0e69046810717534cb09"' Cache-Control: - max-age=0, private, must-revalidate X-Request-Id: - 115e7a72-b3a6-49e1-b531-980c71088c9d X-Runtime: - '0.833195' body: encoding: UTF-8 string: '{"status": "true"}' http_version: recorded_at: Wed, 03 Sep 2014 11:27:37 GMT recorded_with: VCR 2.4.0 broadcast_message-none.yml000066400000000000000000000014071264274401100325430ustar00rootroot00000000000000gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/vcr_cassettes--- http_interactions: - request: method: get uri: https://dev.gitlab.org/api/v3/internal/broadcast_message body: encoding: US-ASCII string: secret_token=a123 headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: - "*/*" User-Agent: - Ruby response: status: code: 200 message: Not Found headers: Server: - nginx/1.1.19 Date: - Sat, 07 Feb 2015 16:45:35 GMT Content-Type: - application/json Content-Length: - '27' Connection: - keep-alive Status: - 200 OK body: encoding: UTF-8 string: '{}' http_version: recorded_at: Sat, 07 Feb 2015 16:45:35 GMT recorded_with: VCR 2.4.0 broadcast_message-ok.yml000066400000000000000000000015651264274401100322220ustar00rootroot00000000000000gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/vcr_cassettes--- http_interactions: - request: method: get uri: https://dev.gitlab.org/api/v3/internal/broadcast_message body: encoding: US-ASCII string: secret_token=a123 headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: - "*/*" User-Agent: - Ruby response: status: code: 200 message: OK headers: Server: - nginx/1.1.19 Date: - Sat, 07 Feb 2015 16:44:35 GMT Content-Type: - application/json Content-Length: - '118' Connection: - keep-alive Status: - 200 OK body: encoding: UTF-8 string: '{"message":"Message","starts_at":"2015-02-07T15:35:00.000Z","ends_at":"2015-02-07T16:35:00.000Z","color":"","font":""}' http_version: recorded_at: Sat, 07 Feb 2015 16:44:35 GMT recorded_with: VCR 2.4.0 gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/vcr_cassettes/check-ok.yml000066400000000000000000000020311264274401100276750ustar00rootroot00000000000000--- http_interactions: - request: method: get uri: https://dev.gitlab.org/api/v3/internal/check body: encoding: US-ASCII string: secret_token=a123 headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: - "*/*" User-Agent: - Ruby response: status: code: 200 message: OK headers: Server: - nginx/1.1.19 Date: - Wed, 03 Sep 2014 11:27:35 GMT Content-Type: - application/json Content-Length: - '72' Connection: - keep-alive Status: - 200 OK Etag: - '"23ee74bf6981b57b9d0e0d8a91f3ec1b"' Cache-Control: - max-age=0, private, must-revalidate X-Request-Id: - c389e282-4d7b-4041-9aed-336e5b007424 X-Runtime: - '0.958718' body: encoding: UTF-8 string: '{"api_version":"v3","gitlab_version":"7.3.0.pre","gitlab_rev":"e8f1331"}' http_version: recorded_at: Wed, 03 Sep 2014 11:27:35 GMT recorded_with: VCR 2.4.0 gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/vcr_cassettes/denied-pull.yml000066400000000000000000000022441264274401100304210ustar00rootroot00000000000000--- http_interactions: - request: method: post uri: https://dev.gitlab.org/api/v3/internal/allowed body: encoding: US-ASCII string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&project=gitlab%2Fgitlabhq&key_id=2&secret_token=a123 headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: - "*/*" User-Agent: - Ruby Content-Type: - application/x-www-form-urlencoded response: status: code: 404 message: Not Found headers: Server: - nginx/1.1.19 Date: - Wed, 03 Sep 2014 11:27:38 GMT Content-Type: - application/json Content-Length: - '27' Connection: - keep-alive Status: - 404 Not Found Cache-Control: - no-cache X-Request-Id: - f9c23a82-be65-41c8-958a-5f3e1b9aa58b X-Runtime: - '0.028027' body: encoding: UTF-8 string: '{"status": false, "message":"404 Not found"}' http_version: recorded_at: Wed, 03 Sep 2014 11:27:38 GMT recorded_with: VCR 2.4.0 denied-push-with-user.yml000066400000000000000000000023161264274401100322720ustar00rootroot00000000000000gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/vcr_cassettes--- http_interactions: - request: method: post uri: https://dev.gitlab.org/api/v3/internal/allowed body: encoding: US-ASCII string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&project=gitlab%2Fgitlabhq&user_id=1&secret_token=a123 headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: - "*/*" User-Agent: - Ruby Content-Type: - application/x-www-form-urlencoded response: status: code: 200 message: OK headers: Server: - nginx/1.1.19 Date: - Wed, 03 Sep 2014 11:27:39 GMT Content-Type: - application/json Content-Length: - '4' Connection: - keep-alive Status: - 200 OK Etag: - '"b326b5062b2f0e69046810717534cb09"' Cache-Control: - max-age=0, private, must-revalidate X-Request-Id: - 8bd01f76-6029-4921-ba97-12323a7d5750 X-Runtime: - '0.019640' body: encoding: UTF-8 string: '{"status": false}' http_version: recorded_at: Wed, 03 Sep 2014 11:27:39 GMT recorded_with: VCR 2.4.0 gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/vcr_cassettes/denied-push.yml000066400000000000000000000022431264274401100304230ustar00rootroot00000000000000--- http_interactions: - request: method: post uri: https://dev.gitlab.org/api/v3/internal/allowed body: encoding: US-ASCII string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&project=gitlab%2Fgitlabhq&key_id=2&secret_token=a123 headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: - "*/*" User-Agent: - Ruby Content-Type: - application/x-www-form-urlencoded response: status: code: 404 message: Not Found headers: Server: - nginx/1.1.19 Date: - Wed, 03 Sep 2014 11:27:38 GMT Content-Type: - application/json Content-Length: - '27' Connection: - keep-alive Status: - 404 Not Found Cache-Control: - no-cache X-Request-Id: - 8b0210d2-4db5-4325-8120-740e22eaf7d5 X-Runtime: - '0.015198' body: encoding: UTF-8 string: '{"status": false, "message":"404 Not found"}' http_version: recorded_at: Wed, 03 Sep 2014 11:27:38 GMT recorded_with: VCR 2.4.0 gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/spec/vcr_cassettes/discover-ok.yml000066400000000000000000000020271264274401100304430ustar00rootroot00000000000000--- http_interactions: - request: method: get uri: https://dev.gitlab.org/api/v3/internal/discover?key_id=126 body: encoding: US-ASCII string: secret_token=a123 headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: - "*/*" User-Agent: - Ruby response: status: code: 200 message: OK headers: Server: - nginx/1.1.19 Date: - Wed, 03 Sep 2014 11:27:35 GMT Content-Type: - application/json Content-Length: - '56' Connection: - keep-alive Status: - 200 OK Etag: - '"1d75c1cf3d4bfa4d2b7bb6a0bcfd7f55"' Cache-Control: - max-age=0, private, must-revalidate X-Request-Id: - ef4513ae-0424-4941-8be0-b5a3a7b4bf12 X-Runtime: - '0.016934' body: encoding: UTF-8 string: '{"name":"Dmitriy Zaporozhets","username":"dzaporozhets"}' http_version: recorded_at: Wed, 03 Sep 2014 11:27:35 GMT recorded_with: VCR 2.4.0 gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/support/000077500000000000000000000000001264274401100234045ustar00rootroot00000000000000gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/support/rewrite-hooks.sh000077500000000000000000000002431264274401100265440ustar00rootroot00000000000000#!/bin/bash # This script is deprecated. Use bin/create-hooks instead. gitlab_shell_dir="$(cd $(dirname $0) && pwd)/.." exec ${gitlab_shell_dir}/bin/create-hooks gitlab-shell-v2.6.10-82b3a4e8f70692ec679d880628fdb0f5844d42b9/support/truncate_repositories.sh000077500000000000000000000007721264274401100304050ustar00rootroot00000000000000#!/bin/bash # $1 is an optional argument specifying the location of the repositories directory. # Defaults to /home/git/repositories if not provided home_dir="/home/git/repositories" src=${1:-"$home_dir"} echo "Danger!!! Data Loss" while true; do read -p "Do you wish to delete all directories from $home_dir/ (y/n) ?: " yn case $yn in [Yy]* ) sh -c "find $home_dir/. -maxdepth 1 -not -name '.' | xargs rm -rf"; break;; [Nn]* ) exit;; * ) echo "Please answer yes or no.";; esac done