[
  {
    "path": ".deepsource.toml",
    "content": "version = 1\n\n[[analyzers]]\nname = \"ruby\"\nenabled = true\n\n[[analyzers]]\nname = \"javascript\"\nenabled = true\n"
  },
  {
    "path": ".dockerignore",
    "content": "/log/*\n!/log/.keep\n/tmp\n.idea\n.env\n*.swp\nconfig/application.yml\ndump.rdb\ncoverage\n.git\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files for more about ignoring files.\n#\n# If you find yourself ignoring temporary files generated by your text editor\n# or operating system, you probably want to add a global ignore instead:\n#   git config --global core.excludesfile '~/.gitignore_global'\n\n# Ignore bundler config.\n/.bundle\n.local\nvendor/*\n\n# Ignore the default SQLite database.\n/db/*.sqlite3\n/db/*.sqlite3-journal\n\n# Ignore all logfiles and tempfiles.\n/log/*\n!/log/.keep\n/tmp\n.idea\n.env\n.ruby-gemset\n*.swp\nconfig/application.yml\npublic/assets/*\ndump.rdb\ncoverage\ndata/\n"
  },
  {
    "path": ".rspec",
    "content": "--color\n--format documentation\n--require spec_helper\n"
  },
  {
    "path": ".rubocop.yml",
    "content": "---\nrequire: \n  - rubocop-rails\n  - rubocop-performance\n  - rubocop-faker\n\nAllCops:\n  NewCops: enable\n  Exclude:\n  - config/initializers/*.rb\n  - config/environments/*.rb\n  - db/*.rb\n  - app/workers/*.rb\n  - app/services/**/*.rb\n  - spec/services/*.rb\n  - bin/*\n  - Rakefile\n  - lib/tasks/*.rake\n  - spec/workers/*.rb\n  - chef-repo/**/*\nNaming/AccessorMethodName:\n  Description: Check the naming of accessor methods for get_/set_.\n  Enabled: false\nStyle/Alias:\n  Description: Use alias_method instead of alias.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#alias-method\n  Enabled: false\nStyle/ArrayJoin:\n  Description: Use Array#join instead of Array#*.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#array-join\n  Enabled: false\nStyle/AsciiComments:\n  Description: Use only ascii symbols in comments.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#english-comments\n  Enabled: false\nNaming/AsciiIdentifiers:\n  Description: Use only ascii symbols in identifiers.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#english-identifiers\n  Enabled: false\nStyle/Attr:\n  Description: Checks for uses of Module#attr.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#attr\n  Enabled: false\nMetrics/BlockNesting:\n  Description: Avoid excessive block nesting\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count\n  Enabled: false\nStyle/CaseEquality:\n  Description: Avoid explicit use of the case equality operator(===).\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-case-equality\n  Enabled: false\nStyle/CharacterLiteral:\n  Description: Checks for uses of character literals.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-character-literals\n  Enabled: false\nStyle/ClassAndModuleChildren:\n  Description: Checks style of children classes and modules.\n  Enabled: true\n  EnforcedStyle: compact\n  Exclude:\n  - config/application.rb\nMetrics/ClassLength:\n  Description: Avoid classes longer than 100 lines of code.\n  Enabled: false\nMetrics/ModuleLength:\n  Description: Avoid modules longer than 100 lines of code.\n  Enabled: false\nStyle/ClassVars:\n  Description: Avoid the use of class variables.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-class-vars\n  Enabled: false\nStyle/CollectionMethods:\n  Enabled: true\n  PreferredMethods:\n    find: detect\n    inject: reduce\n    collect: map\n    find_all: select\nStyle/ColonMethodCall:\n  Description: 'Do not use :: for method call.'\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#double-colons\n  Enabled: false\nStyle/CommentAnnotation:\n  Description: Checks formatting of special comments (TODO, FIXME, OPTIMIZE, HACK,\n    REVIEW).\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#annotate-keywords\n  Enabled: false\nMetrics/AbcSize:\n  Description: A calculated magnitude based on number of assignments, branches, and\n    conditions.\n  Enabled: false\nMetrics/BlockLength:\n  CountComments: true\n  Max: 25\n  AllowedMethods: []\n  Exclude:\n  - spec/**/*\n  - config/environments/*.rb\nMetrics/CyclomaticComplexity:\n  Description: A complexity metric that is strongly correlated to the number of test\n    cases needed to validate a method.\n  Enabled: false\nRails/Delegate:\n  Description: Prefer delegate method for delegations.\n  Enabled: false\nStyle/PreferredHashMethods:\n  Description: Checks use of `has_key?` and `has_value?` Hash methods.\n  StyleGuide: \"#hash-key\"\n  Enabled: false\nStyle/Documentation:\n  Description: Document classes and non-namespace modules.\n  Enabled: false\nStyle/DoubleNegation:\n  Description: Checks for uses of double negation (!!).\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-bang-bang\n  Enabled: false\nStyle/EachWithObject:\n  Description: Prefer `each_with_object` over `inject` or `reduce`.\n  Enabled: false\nStyle/EmptyLiteral:\n  Description: Prefer literals to Array.new/Hash.new/String.new.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#literal-array-hash\n  Enabled: false\nStyle/Encoding:\n  Enabled: false\nStyle/EvenOdd:\n  Description: Favor the use of Fixnum#even? && Fixnum#odd?\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#predicate-methods\n  Enabled: false\nNaming/FileName:\n  Description: Use snake_case for source file names.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-files\n  Enabled: false\nStyle/FrozenStringLiteralComment:\n  Description: Add the frozen_string_literal comment to the top of files to help transition\n    from Ruby 2.3.0 to Ruby 3.0.\n  Enabled: false\nStyle/FormatString:\n  Description: Enforce the use of Kernel#sprintf, Kernel#format or String#%.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#sprintf\n  Enabled: false\nStyle/GlobalVars:\n  Description: Do not introduce global variables.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#instance-vars\n  Reference: http://www.zenspider.com/Languages/Ruby/QuickRef.html\n  Enabled: false\nStyle/GuardClause:\n  Description: Check for conditionals that can be replaced with guard clauses\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals\n  Enabled: false\nStyle/IfUnlessModifier:\n  Description: Favor modifier if/unless usage when you have a single-line body.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier\n  Enabled: false\nStyle/IfWithSemicolon:\n  Description: Do not use if x; .... Use the ternary operator instead.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs\n  Enabled: false\nStyle/InlineComment:\n  Description: Avoid inline comments.\n  Enabled: false\nStyle/Lambda:\n  Description: Use the new lambda literal syntax for single-line blocks.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#lambda-multi-line\n  Enabled: false\nStyle/LambdaCall:\n  Description: Use lambda.call(...) instead of lambda.(...).\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#proc-call\n  Enabled: false\nStyle/LineEndConcatenation:\n  Description: Use \\ instead of + or << to concatenate two string literals at line\n    end.\n  Enabled: false\nMetrics/MethodLength:\n  Description: Avoid methods longer than 10 lines of code.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#short-methods\n  Enabled: false\nStyle/ModuleFunction:\n  Description: Checks for usage of `extend self` in modules.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#module-function\n  Enabled: false\nStyle/MultilineBlockChain:\n  Description: Avoid multi-line chains of blocks.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks\n  Enabled: false\nStyle/NegatedIf:\n  Description: Favor unless over if for negative conditions (or control flow or).\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#unless-for-negatives\n  Enabled: false\nStyle/NegatedWhile:\n  Description: Favor until over while for negative conditions.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#until-for-negatives\n  Enabled: false\nStyle/Next:\n  Description: Use `next` to skip iteration instead of a condition at the end.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals\n  Enabled: false\nStyle/NilComparison:\n  Description: Prefer x.nil? to x == nil.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#predicate-methods\n  Enabled: false\nStyle/Not:\n  Description: Use ! instead of not.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#bang-not-not\n  Enabled: false\nStyle/NumericLiterals:\n  Description: Add underscores to large numeric literals to improve their readability.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics\n  Enabled: false\nStyle/OneLineConditional:\n  Description: Favor the ternary operator(?:) over if/then/else/end constructs.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#ternary-operator\n  Enabled: false\nNaming/BinaryOperatorParameterName:\n  Description: When defining binary operators, name the argument other.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#other-arg\n  Enabled: false\nMetrics/ParameterLists:\n  Description: Avoid parameter lists longer than three or four parameters.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#too-many-params\n  Enabled: false\nStyle/PercentLiteralDelimiters:\n  Description: Use `%`-literal delimiters consistently\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-literal-braces\n  Enabled: false\nStyle/PerlBackrefs:\n  Description: Avoid Perl-style regex back references.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers\n  Enabled: false\nNaming/PredicateName:\n  Description: Check the names of predicate methods.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark\n  ForbiddenPrefixes:\n  - is_\n  Exclude:\n  - spec/**/*\nStyle/Proc:\n  Description: Use proc instead of Proc.new.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#proc\n  Enabled: false\nStyle/RaiseArgs:\n  Description: Checks the arguments passed to raise/fail.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#exception-class-messages\n  Enabled: false\nStyle/RegexpLiteral:\n  Description: Use / or %r around regular expressions.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-r\n  Enabled: false\nStyle/SelfAssignment:\n  Description: Checks for places where self-assignment shorthand should have been\n    used.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#self-assignment\n  Enabled: false\nStyle/SingleLineBlockParams:\n  Description: Enforces the names of some block params.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#reduce-blocks\n  Enabled: false\nStyle/SingleLineMethods:\n  Description: Avoid single-line methods.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-single-line-methods\n  Enabled: false\nStyle/SignalException:\n  Description: Checks for proper usage of fail and raise.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#fail-method\n  Enabled: false\nStyle/SpecialGlobalVars:\n  Description: Avoid Perl-style global variables.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms\n  Enabled: false\nStyle/StringLiterals:\n  Description: Checks if uses of quotes match the configured preference.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-string-literals\n  EnforcedStyle: single_quotes\n  Enabled: true\nStyle/TrailingCommaInArguments:\n  Description: Checks for trailing comma in argument lists.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas\n  EnforcedStyleForMultiline: no_comma\n  SupportedStylesForMultiline:\n  - comma\n  - consistent_comma\n  - no_comma\n  Enabled: true\nStyle/TrailingCommaInArrayLiteral:\n  Description: Checks for trailing comma in array literals.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas\n  EnforcedStyleForMultiline: comma\n  SupportedStylesForMultiline:\n  - comma\n  - consistent_comma\n  - no_comma\n  Enabled: true\nStyle/TrailingCommaInHashLiteral:\n  Description: Checks for trailing comma in hash literals.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas\n  EnforcedStyleForMultiline: comma\n  SupportedStylesForMultiline:\n  - comma\n  - consistent_comma\n  - no_comma\n  Enabled: true\nStyle/TrivialAccessors:\n  Description: Prefer attr_* methods to trivial readers/writers.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#attr_family\n  Enabled: false\nStyle/VariableInterpolation:\n  Description: Don't interpolate global, instance and class variables directly in\n    strings.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#curlies-interpolate\n  Enabled: false\nStyle/WhenThen:\n  Description: Use when x then ... for one-line cases.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#one-line-cases\n  Enabled: false\nStyle/WhileUntilModifier:\n  Description: Favor modifier while/until usage when you have a single-line body.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier\n  Enabled: false\nStyle/WordArray:\n  Description: Use %w or %W for arrays of words.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-w\n  Enabled: false\nLayout/ConditionPosition:\n  Description: Checks for condition placed in a confusing position relative to the\n    keyword.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#same-line-condition\n  Enabled: false\nLayout/DotPosition:\n  Description: Checks the position of the dot in multi-line method calls.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains\n  EnforcedStyle: trailing\nLayout/ExtraSpacing:\n  Description: Do not use unnecessary spacing.\n  Enabled: true\nLayout/MultilineOperationIndentation:\n  Description: Checks indentation of binary operations that span more than one line.\n  Enabled: true\n  EnforcedStyle: indented\nLayout/MultilineMethodCallIndentation:\n  Description: Checks indentation of method calls with the dot operator that span\n    more than one line.\n  Enabled: true\n  EnforcedStyle: indented\nLayout/InitialIndentation:\n  Description: Checks the indentation of the first non-blank non-comment line in a\n    file.\n  Enabled: false\nLint/AmbiguousOperator:\n  Description: Checks for ambiguous operators in the first argument of a method invocation\n    without parentheses.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-as-args\n  Enabled: false\nLint/AmbiguousRegexpLiteral:\n  Description: Checks for ambiguous regexp literals in the first argument of a method\n    invocation without parenthesis.\n  Enabled: false\nLint/AssignmentInCondition:\n  Description: Don't use assignment in conditions.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition\n  Enabled: false\nLint/CircularArgumentReference:\n  Description: Don't refer to the keyword argument in the default value.\n  Enabled: false\nLint/DeprecatedClassMethods:\n  Description: Check for deprecated class method calls.\n  Enabled: false\nLint/EachWithObjectArgument:\n  Description: Check for immutable argument given to each_with_object.\n  Enabled: false\nLint/ElseLayout:\n  Description: Check for odd code arrangement in an else block.\n  Enabled: false\nLint/FormatParameterMismatch:\n  Description: The number of parameters to format/sprint must match the fields.\n  Enabled: false\nLint/LiteralAsCondition:\n  Description: Checks of literals used in conditions.\n  Enabled: false\nLint/LiteralInInterpolation:\n  Description: Checks for literals used in interpolation.\n  Enabled: false\nLint/Loop:\n  Description: Use Kernel#loop with break rather than begin/end/until or begin/end/while\n    for post-loop tests.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#loop-with-break\n  Enabled: false\nLint/NestedMethodDefinition:\n  Description: Do not use nested method definitions.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-methods\n  Enabled: false\nLint/NonLocalExitFromIterator:\n  Description: Do not use return in iterator to cause non-local exit.\n  Enabled: false\nLint/ParenthesesAsGroupedExpression:\n  Description: Checks for method calls with a space before the opening parenthesis.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-no-spaces\n  Enabled: false\nLint/RequireParentheses:\n  Description: Use parentheses in the method call to avoid confusion about precedence.\n  Enabled: false\nLint/UnderscorePrefixedVariableName:\n  Description: Do not use prefix `_` for a variable that is used.\n  Enabled: false\nLint/Void:\n  Description: Possible use of operator/literal/variable in void context.\n  Enabled: false\nPerformance/CaseWhenSplat:\n  Description: Place `when` conditions that use splat at the end of the list of `when`\n    branches.\n  Enabled: false\nPerformance/Count:\n  Description: Use `count` instead of `select...size`, `reject...size`, `select...count`,\n    `reject...count`, `select...length`, and `reject...length`.\n  Enabled: false\nPerformance/Detect:\n  Description: Use `detect` instead of `select.first`, `find_all.first`, `select.last`,\n    and `find_all.last`.\n  Reference: https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code\n  Enabled: false\nPerformance/FlatMap:\n  Description: Use `Enumerable#flat_map` instead of `Enumerable#map...Array#flatten(1)`\n    or `Enumberable#collect..Array#flatten(1)`\n  Reference: https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code\n  Enabled: false\nPerformance/ReverseEach:\n  Description: Use `reverse_each` instead of `reverse.each`.\n  Reference: https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code\n  Enabled: false\nPerformance/Size:\n  Description: Use `size` instead of `count` for counting the number of elements in\n    `Array` and `Hash`.\n  Reference: https://github.com/JuanitoFatas/fast-ruby#arraycount-vs-arraysize-code\n  Enabled: false\nPerformance/StringReplacement:\n  Description: Use `tr` instead of `gsub` when you are replacing the same number of\n    characters. Use `delete` instead of `gsub` when you are deleting characters.\n  Reference: https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code\n  Enabled: false\nRails/ActionFilter:\n  Description: Enforces consistent use of action filter methods.\n  Enabled: false\nRails/Date:\n  Description: Checks the correct usage of date aware methods, such as Date.today,\n    Date.current etc.\n  Enabled: false\nRails/FindBy:\n  Description: Prefer find_by over where.first.\n  Enabled: false\nRails/FindEach:\n  Description: Prefer all.find_each over all.find.\n  Enabled: false\nRails/HasAndBelongsToMany:\n  Description: Prefer has_many :through to has_and_belongs_to_many.\n  Enabled: false\nRails/Output:\n  Description: Checks for calls to puts, print, etc.\n  Enabled: false\nRails/ReadWriteAttribute:\n  Description: Checks for read_attribute(:attr) and write_attribute(:attr, val).\n  Enabled: false\nRails/ScopeArgs:\n  Description: Checks the arguments of ActiveRecord scopes.\n  Enabled: false\nRails/TimeZone:\n  Description: Checks the correct usage of time zone aware methods.\n  StyleGuide: https://github.com/bbatsov/rails-style-guide#time\n  Reference: http://danilenko.org/2012/7/6/rails_timezones\n  Enabled: false\nRails/Validation:\n  Description: Use validates :attribute, hash of validations.\n  Enabled: false\nLint/FlipFlop:\n  Description: Checks for flip flops\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-flip-flops\n  Enabled: false\nLayout/LineLength:\n  Description: Limit lines to 80 characters.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#80-character-limits\n  Max: 100\nStyle/Sample:\n  Description: Use `sample` instead of `shuffle.first`, `shuffle.last`, and `shuffle[Fixnum]`.\n  Reference: https://github.com/JuanitoFatas/fast-ruby#arrayshufflefirst-vs-arraysample-code\n  Enabled: false\nLayout/ParameterAlignment:\n  Description: Here we check if the parameters on a multi-line method call or definition\n    are aligned.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-double-indent\n  Enabled: false\nLint/DuplicateHashKey:\n  Description: Check for duplicate keys in hash literals.\n  Enabled: false\nLint/SuppressedException:\n  Description: Don't suppress exception.\n  StyleGuide: https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions\n  Enabled: false\nLint/RedundantCopDisableDirective:\n  Description: 'Checks for rubocop:disable comments that can be removed. Note: this\n    cop is not disabled when disabling all cops. It must be explicitly disabled.'\n  Enabled: false\n"
  },
  {
    "path": ".ruby-version",
    "content": "ruby-3.1.2\n"
  },
  {
    "path": ".travis.yml",
    "content": "dist: xenial\nsudo: required\nlanguage: ruby\nrvm:\n  - ruby-2.4.4\nservices:\n  - redis-server\n  - mysql\n\nenv:\n  global:\n  - GATE_SERVER_URL=gate.server/url\n  - SIGN_IN_TYPE=\n  - GATE_OAUTH_CLIENT_ID=totally_secret_client_id\n  - GATE_OAUTH_CLIENT_SECRET=totally_secret\n  - GATE_HOSTED_DOMAIN=gate.domain\n  - GATE_HOSTED_DOMAINS=test1.com,test2.com\n  - GATE_DB_HOST=localhost\n  - GATE_DB_PORT=3306\n  - GATE_DB_NAME=\n  - GATE_DB_USER=root\n  - GATE_DB_PASSWORD=\n  - CACHE_HOST=localhost\n  - CACHE_PORT=6379\n  - SECRET_KEY_BASE=\n  - SECRET_API_KEY=\n  - GATE_CONFIG_SECRET=gate_pw_secret\n  - USER_ROLES=employee,consultant\n  - UID_BUFFER=5000\n  - DEFAULT_HOST_PATTERN=s*\n  - PRODUCT_LIST=pr1,pr2\n  - SAML_APPS=datadog\n  - secure: Q2/gUeT/Y6IUKsRzgiiE6HGxLcHAzTtVCv6+6TcQnpJL9+HEO06+ISwekNXHRe/2BNMiWzDG5ufqo2YpA1j9h8899KAJZRYDULbbV0o8nrpT2kZSac1ZejBVYb/SrhDk8wrnQxQ061o6SdKpFEEwbIiJDzaqeQkv4tkyw3dEOq6+FRGS/QXj3mPIIArPbNCOtwcvnmg0mGIuPAAxuRZmz4Tuk2F6JdR4LrZstf33mvUYqbTw02Kl7UoErFjGSx2Ti+JPYIVDJ6D8pNCjv5va3awJskcXE5bj0evR5+OiP69GK6T2/UbP+QKwN2J36Pb9RxBKGuEkBTNuduch+h+RMiYk0A8MDO3Sy47i2v8Oj1xTEYqsNm0scmD2utZ93/VqPipYTrxuos7gbC4mdiW3dcMzyrhgaULEJlvalKaSQV41Xex8h/V7NCRvpoYjnyuWVUMQFgGSnE25Xpt1LvP3epcxlEBA9LcndskbEHaWup8mCD2bp+2Zq10iWi/hNnR2IBrj61fApTWBSoNplJMsMZNb3bverQVFFG79Gfr320HWk+eJVahZNFwUYp9VYn17r38nMRdfP9B85hm2ZUEiokzOvEXEKoSa2QVlCNQHk4HGJmfykPn4L8kDnS6LhYV3Yxugxwsl//1iUOwv4+ARMGn/rs13BDOixWXc+GVZIvk=\n  - secure: hTSsv54GMEfKuZfk5UL3cekNK4ZgEEWwTp2cbfN39ViWBbq4xtXok1jREx4QyhwkNak47ewHuY0rifxPEYkJVJfj+tA/xiGs9BOG73gNEtQYhXXN/ZodXF0Kq3pYOfcVWDDo7oMAUoohkohHK2TA+KY3mfk5sFbxbXHOlNdSn0vXDSa8MgzALoN6+w1oWkC41perODfNS4EYD3gdhwDg4x6QHrmakrt0oOP4PPMCpOWinvkzWq7lBDdhkPSze47BwGilyUDFSJnunSKip88s6DlP4gNj6YYBiT6xGTPLuR76tgEPma9rp25zGvLxH9LsluMXTNuN0L3/6FzVyv0kJgXIwQrpgreQ+B45q3mT6iNom0alyNk3fYDKU59azpH9G0rC0zflmOhW+jssgwyJN08rJN8rFkhpQreD4dgey7+UDjf7qF3rOd0kXmqBvA/3z/aF8Mla/2+EIb1DjyHwnjAcPF2E0/97Gv2eGpZR/PPrQIVN2m8vEe3ce/39c4S/l9OU0G4Dqz9HMqIBG+0qFdS24+egbjaIoOQzO/eJwG/eew4ncvaz/ES8qCDtvFNeKR+QnfxcbJysybrxDNTqt3cBGTeotIdZxIB3rYK63+s+dvzggm09vBMafoP7zyWzD0xjSES7m+Gv9V8Jlvc7h5JSWrkmAgLmgnqt7FpI7hs=\n  - secure: KtmItMzRHfZEPS54hTnYXNAWsqxCZZMtmxamJPcr2SVP+pzM/25eEF3M2b0JZECFRtW3xPBGita+4BjH8IY4NljH0IDVhPPKQsMHaVKtaqEeiTCRXCqkJd0DMUDeqyRcccwPDJSy9HeZns6E+zQDCGaC/tmFwDUWy5BYpairWd7sdw436iOeTmQ2fJ8SQ9lBr9LGpk8NwDmkYqg14fnOraIhw70tiWv9Wn82mEv+OwY4iKphOv2ZXEBeJYitoT1LQmhnaVvNvax16TYIY3Mxh89Oy2gVkKCgvbR9q1NmAIiWmMTyCLWxF0bd6A6Q+dHDDaQZF0pDsQqe83rnoA0xuaJUF9dh+ahvW0bn0aVFIa8tWgiBEMXWxHEDuEyfCWFis9vf3uj1xJl0SV0ktE5oIRzaF/0cTQorAwRy0UpN1HxK1yB9aITCad/BJ9ERK8MXYnfzC65jn2zHuQMjBz9Aeig4sgLc4IgtGM2DzabsIkhYG3ygL1TYaXXSj5Mo2KDwOuaZ46C7B6qxlzcesPmSnM3KF1CBatRZQK5tjsMm5GnxXKKtqg/udGtOA2fELlNolFsojtpmO4A79TAnebRUpEsU2vP0da1x7yTD5+2NWg6dnvq7tA3c5b8qBK2cAMnCHS0gmjaN3RYeFhLYO55dHDdmx/DNyQQ1V/YEkuisy80=\n  - COMMIT=${TRAVIS_COMMIT::8}\n  - DOCKER_COMPOSE_VERSION=1.11.2\n\nbefore_install:\n  - gem update --system\n  - gem install bundler\n\nstages:\n  - build, test and releases\n  - name: build and publish docker\n    if: (branch = master OR tag IS present) and type != pull_request\n\njobs:\n  include:\n    - stage: build, test and releases\n      script:\n        - sudo mysql_upgrade -u root\n        - sudo service mysql restart\n        - mysql -e 'CREATE DATABASE IF NOT EXISTS gate_test;'\n        - gem install bundler\n        - bundle package --all\n        - bundle install --binstubs --local\n        - bundle exec rake db:migrate\n        - bundle exec rake spec\n        - bundle exec rake assets:precompile RAILS_ENV=production\n      before_deploy:\n        - \"rm -rf $TRAVIS_BUILD_DIR/*.tar.gz\"\n        - \"tar zcf gate-$TRAVIS_TAG.tar.gz -C $TRAVIS_BUILD_DIR ./*\"\n      deploy:\n        provider: releases\n        skip_cleanup: true\n        api_key:\n          secure: VWDGnrtrECoeJBATfe71HdpvnD15IKTkHWq/cQDfo9FTrm005ngTusQpW0eim76tSTMcJ1OBLoE9aAkd4Szi00TWenNPaAWHtgodcvNXxjqyZEtznRmtZW/vSk2H+iwJxJZoPDMZqStjmmSoD0dA1t2GLGltFzeJ5Y409YeroLUsVdRcTJY0mvQYbuLt1aZCu76gY5nB01yH7ib4ykZllJQbkyX57TKBYSLoQVUQ3hqo9BLUoROknHSfFGBHjSN9D3zIqKoh0XDOuTnyMHB+zMGmjjJbHMx5Z2eW1NW1HvpqDUOPl8uRnUEoCCfujA7OUlGgdadRBeUJObzpNkpQUNLEh5zIIr9NAZzjunzYkMiMZIseTNflJ6HOajld2Bcgz3PcbzM8hcIFog5loE7CBLWEzDoOIY/B75NYuHPIzRzTbgw7jf8XdfypCNMm0RLWiCXiDmuDMcNPZT9NrrheNGFz1CDmWkwpJmUTIv8J13J2Ux9ex1NY9iSRD8rgJ+bLOfp/u1nJWjACki8zq2/vffm2oUWz/R7q23Keq5Itddpe3mJUyFjokTUZPNSAlC45rF+rgK4ka7yJb+ZZAOaq/N8iIfVkgnfviWtaPQzAWRkO8J8Wzt1GrfQpNlo2BMM6Cpy0iV9ANYqAVYerImAopMWlzQmjbtt8P+xmmYA0FIw=\n        file_glob: true\n        file: $TRAVIS_BUILD_DIR/*.tar.gz\n        on:\n          tags: true\n          repo: gate-sso/gate\n    - stage: build and publish docker\n      install: skip\n      script:\n        - gem install bundler\n        - bundle install\n        - bundle exec rake assets:precompile RAILS_ENV=production\n        - echo \"$DOCKER_PASSWORD\" | docker login -u \"$DOCKER_USERNAME\" --password-stdin\n        - docker build -t gatesso/gate:latest .\n        - if [ -n \"$TRAVIS_TAG\" ]; then\n            docker tag gatesso/gate:latest gatesso/gate:$TRAVIS_TAG;\n          fi\n        - docker images\n        - docker push gatesso/gate\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [1.1.8] - 2020-02-17\n- Securing organisations configuration page\n\n## [1.1.7] - 2020-02-24\n### Fixed\n- Fix audit trail when user removed from group\n\n## [1.1.6] - 2020-01-14\n### Changed\n- Store session to db using active record instead of using cookies\n\n## [1.1.5] - 2019-10-20\n### Changed\n- User API now also return `id`\n\n## [1.1.4] - 2019-10-05\n### Added\n- API to add user to group (POST /api/v1/groups/:group_id/users)\n\n## [1.1.3] - 2019-09-17\n### Changed\n- Update devise to version `4.7.1`.\n\n## [1.1.2] - 2019-09-17\n### Added\n- Show email of group admin on group detail\n- Show users join date to group on group detail\n\n## [1.1.1] - 2019-09-02\n### Changed\n- Update nokogiri to version `1.10.4`.\n\n## [1.1.0] - 2019-09-02\n### Fixed\n- Ensure that 'deactivated_at' on user is automatically set when we make user inactive\n### Changed\n- Improve access policy for actions on resources including profile, user, api resource, host machine, organisation\n### Added\n- Add endpoint entity to represent gate endpoint. Group will own endpoints, this mechanism will be used as an authorization for gate.\n- Add api to deactivate user in gate (PATCH /api/v1/users/:id/deactivate).\n\n## [1.0.5] - 2019-08-06\n### Fixed\n- Improve loading time when opening group and user show page\n\n## [1.0.4] - 2019-07-17\n### Fixed\n- If a user don't have any VPNs, they should still be able to click download VPN without incurring exception\n- Create missing tests for user model\n- Optimize queries when fetching sysadmins\n\n## [1.0.3] - 2019-07-16\n### Fixed\n- Fix nil pointer exception when group members response is nil\n\n## [1.0.2] - 2019-07-15\n### Fixed\n- Optimize slow queries on vpn model\n\n## [1.0.1] - 2019-07-15\n### Added\n- Add the ability to only fetch active user for `/api/v1/users/profile` API\n\n## [1.0.0] - 2019-07-15\n### Changed\n- Use dotenv instead of figaro. This is a breaking change and warrant a major version release.\n- All spiders are banned by default now in `robots.txt`\n- When admin account become inactive, the admin status will automatically revoked.\n- Admin can set expiration date on group assignment. This expiration date is optional, when not specified then it's a permanent assignment.\n\n## [0.1.0] - 2019-05-20\n### Changed\n- Gate now uses semantic versioning.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing Guidelines\n\n## Releasing Gate\n\n> Gate uses semantic versioning, check [this page](https://semver.org/) for more details on how to bump the version number.\n\nSteps on releasing Gate.\n\n1. Bump version on [VERSION](VERSION) file.\n2. Add appropriate changelogs on [CHANGELOG.md](CHANGELOG.md) file. Please follow existing format.\n3. Tag the commit by the new version number and push it, travis will automatically build and release Gate.\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM ruby:2.4\n\nRUN apt-get update\nRUN curl -sL https://deb.nodesource.com/setup_10.x | bash\nRUN apt-get update -qq && apt-get install -y build-essential nodejs git\n\nRUN mkdir /app\nWORKDIR /app\n\nCOPY Gemfile /app\nCOPY Gemfile.lock /app\n\nRUN gem install bundler -v '>= 2.0'\nRUN bundle install --without development\nCOPY . /app\n\nCMD [ \"bundle\", \"exec\", \"rails\", \"s\" ]\n"
  },
  {
    "path": "Gemfile",
    "content": "source 'https://rubygems.org'\n\ngem 'rails', '7.1.3.2'\n\ngem 'activerecord-session_store'\ngem 'ansi'\ngem 'bootstrap'\ngem 'countries', require: 'countries/global'\ngem 'devise'\ngem 'dotenv-rails'\ngem 'font-awesome-rails'\ngem 'httparty'\ngem 'jbuilder'\ngem 'mysql2'\ngem 'omniauth'\ngem 'omniauth-google-oauth2'\ngem 'paranoia'\ngem 'puma'\ngem 'redis'\ngem 'rotp'\ngem 'rqrcode'\n\ngem 'sassc-rails'\n\n\n#Disabling SAML Feature during upgrade\n# #TODO ENable this feature later\n#gem 'ruby-saml', '1.8.0'\n#gem 'saml_idp', git: 'https://github.com/gate-sso/saml_idp'\ngem 'sdoc', group: :doc\n# #TODO ENable this feature later\n\ngem 'slim-rails'\ngem 'turbolinks'\ngem 'uglifier'\ngem 'whenever', require: false\n\ngroup :development, :test do\n  gem 'capybara'\n  gem 'coveralls'\n  gem 'database_cleaner'\n  gem 'dredd_hooks'\n  gem 'factory_bot_rails'\n  gem 'faker'\n  gem 'mock_redis'\n  gem 'pry'\n  gem 'rails-controller-testing'\n  gem 'rspec-rails'\n  gem 'rubocop'\n  gem 'rubocop-faker'\n  gem 'rubocop-performance'\n  gem 'rubocop-rails'\n  gem 'rubocop-rspec'\n  gem 'shoulda-matchers'\n  gem 'simplecov'\n  gem 'simplecov-console'\n  gem 'timecop'\n  gem 'webmock'\nend\n\ngroup :development do\n  gem 'web-console', platform: :ruby\nend\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 gate-sso\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Gate is now Gate-WireGuard \n\n## DEPRECATED\n\n#### Please use [Gate-WireGuard](https://github.com/gate-sso/gate-wireguard)\n\nGateWireguard is based on WireGuard VPN Backend, also, it provides better security, faster speeds; it's much easier to install and run.\n\n\n## *MASTER is broken* we are migrating to Rails 7.\n\n## Ubuntu dependencies\nApart from devtools you need to install the following packages as well.\n```\n    apt-get install libmysqlclient-dev\n    apt-get install libyaml-dev\n```\n\n#### Please note that we are upgrading gate RAILS version, and it will have breaking changes New RAILS 7 version will not be backward compatible and will not have many features We are removing many features, just to support API TOKENS and VPN functionality\n\n#### Please use [RAILS 5 Branch](https://github.com/gate-sso/gate/tree/RAILS_5_RELEASE) for backward compatibilty\n\n> Gate now uses semantic versioning to add more visibility on breaking changes. For users, you might want to check [CHANGELOG.md](CHANGELOG.md). For contributors, check [CONTRIBUTING.md](CONTRIBUTING.md).\n\nGate is a single sign-on (SSO) platform for centralised authentication across Linux, OpenVPN and CAS.\n\nGate works by automating OpenVPN profile creation for you and also providing you with google multi-factor authentication (MFA) integration. Gate provides single MFA Token authorisation across your organisation. Following scenarios can be handled by Gate:\n\n1. Setup OpenVPN with Gate authentication.\n2. Automatically create VPN profiles for each users.\n3. Provide you with JaSig CAS Custom Authentication Handler to authenticate with Gate SSO and in turn enabling MFA for JaSig CAS.\n4. Enable Linux authentication with [pam_gate](https://github.com/gate-sso/pam_gate), which sits like a small module with Linux and allow authentication.\n5. Enable Name Service Switch (NSS) on Linux, so that Gate users can be discovered and authenticated on Linux.\n6. **Access Control on Linux** Gate also allows you to control access to specific machines, like which hosts a user can login. And that can be controlled by reg-ex pattern on host name or IP addresses. (Note: pattern * matches everything).\n\n> The entry point for self sign-in is Google mail authentication. If you don't use Google mail authentication, you can point gate to any OAuth provider and it should work.\n\n> Gate provides you with single sign-on solution plus centralised user management across your applications and services. Not only it helps in controlling users access but it also helps in making most of it automated.\n\n### Modules\n\n* [pam_gate](https://github.com/gate-sso/pam_gate) - Gate module for Linux PAM\n* [nss_gate](https://github.com/gate-sso/nss_gate) - Gate module for Linux Name Server Switch (NSS)\n* [cas_gate](https://github.com/gate-sso/cas_gate) - CAS Customer MFA authentication handler for Gate\n* open_vpn_gate - for OpenVPN setup, it is not extracted yet.\n\n## Development Setup\n\n> We are in the process of improving Gate setup process, please check back for updated instructions.\n\n### Manual Setup\n\n#### Initializing Your Application\n\n* Ensure that ruby is installed (>= 2.4) and `bundler` gem is installed.\n* Clone [Gate repository](https://github.com/gate-sso/gate)\n* Run `bundle install`\n* Run `rake app:init` to create environment file based on sample (we use dotenv to manage environment variables).\n\n#### Setting up OAuth (Optional)\n\n> If you setup Gate for development purpose and you want to avoid setting up OAuth, you can fill `SIGN_IN_TYPE` environment variable with `form`. This option will provide you with sign-in form in Gate homepage that you can fill with e-mail and name to sign-in.\n\n> Note that you still need to update `GATE_HOSTED_DOMAINS` to serve your e-mail domain.\n\nCheck [this guide](docs/oauth_setup.md) For detailed information on how to setup OAuth.\n\n#### Setting up Database and Cache\n\n* Install and setup database (mysql) and update the following values (`GATE_DB_HOST`, `GATE_DB_PORT`, `GATE_DB_USER`, `GATE_DB_PASSWORD`) on `.env`.\n* Install and setup cache (redis) and update the following values (`CACHE_DB`, `CACHE_HOST`).\n\n#### Finishing Steps\n\nTo finalize your setup you just need to run `rake app:setup`. This command will setup your database and also run inital set of tests to make sure you have a successful setup.\n\nOnce Gate is setup, sign-in with your user and you should see welcome page with VPN profile download and VPN MFA Scanning.\n\nIf you want Gate to setup VPN for you then just install OpenVPN with easy rsa. Gate should just work fine with it.\n\n> **NOTE** We will be putting more effort to automate VPN setup using Gate as well. Or you can send a pull request to help us with this.\n\n### Docker Setup\n\n* Build docker image using `docker build -t gate .`\n* Create and update `.env` file according to `.env.example` with appropriate values\n* Run the image using `docker run -p 3000:3000 --env-file=.env -it gate`\n* If you want use docker-compose run using `docker-compose up`\n\n## Additional Topics\n\n* [API Blueprint Test](docs/dredd_setup.md)\n* [Additional Setup](docs/additional_setup.md)\n* [Administration](docs/administration.md)\n* [Newrelic Integration](docs/newrelic.md)\n\n### Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md)\n\n### Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md)\n\n### License\n\nMIT License, See [LICENSE](LICENSE).\n"
  },
  {
    "path": "Rakefile",
    "content": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire File.expand_path('../config/application', __FILE__)\n\nRails.application.load_tasks\n"
  },
  {
    "path": "VERSION",
    "content": "1.1.8\n"
  },
  {
    "path": "api_blueprint/bin/dredd_server.sh",
    "content": "#!/bin/bash\n# dredd_server.sh\nkill -9 $(lsof -i tcp:9865 -t)\nexport RAILS_ENV=test\nexport LOG_LEVEL=info\nrake db:drop\nrake db:setup\nbundle exec rails server --port=9865\n\n"
  },
  {
    "path": "api_blueprint/group.apib",
    "content": "FORMAT: 1A\n\n# API Group\n\n# Group [/api/v1/groups]\n\n## Create Groups [POST]\nCreate new group\n\n+ Request(application/json)\n\n    + Body\n\n            {\n                \"access_token\": \"token\",\n                \"name\" : \"foo\"\n            }\n    \n    + Schema\n    \n            {\n                \"type\": \"object\",\n                \"properties\" : {\n                    \"name\" : {\n                        \"type\" : \"string\"\n                    },\n                    \"access_token\" : {\n                        \"type\" : \"string\"\n                    }\n                }\n            }\n    \n+ Response 200\n\n    + Headers\n\n            x-frame-options: SAMEORIGIN\n            x-xss-protection: 1; mode=block\n            x-content-type-options: nosniff\n            content-type: application/json; charset=utf-8\n            etag: W/\"5c15461069e69109955c72671ffc465d\"\n            cache-control: max-age=0, private, must-revalidate\n            x-request-id: 0613ec11-afd7-4dc0-9096-fee607d71c12\n            x-runtime: 0.073641\n            connection: close\n            transfer-encoding: chunked\n\n    + Body\n    \n            {\n                \"id\": 1,\n                \"name\": \"foo\"\n            }\n    + Schema\n    \n            {\n                \"type\": \"object\",\n                \"properties\" : {\n                    \"id\" : {\n                        \"type\" : \"int\"\n                    },\n                    \"name\" : {\n                        \"type\" : \"string\"\n                    }\n                }\n            }\n\n+ Request(application/json)\n\n    + Body\n\n            {\n                \"access_token\": \"token\",\n                \"name\" : \"foo\"\n            }\n    \n    + Schema\n    \n            {\n                \"type\": \"object\",\n                \"properties\" : {\n                    \"name\" : {\n                        \"type\" : \"string\"\n                    },\n                    \"access_token\" : {\n                        \"type\" : \"string\"\n                    }\n                }\n            }\n\n+ Response 422\n\n    + Headers\n\n            x-frame-options: SAMEORIGIN\n            x-xss-protection: 1; mode=block\n            x-content-type-options: nosniff\n            content-type: application/json; charset=utf-8\n            cache-control: no-cache\n            x-request-id: 30ec7a3a-4794-435b-a5b9-8ff480cd648c\n            x-runtime: 0.007976\n            connection: close\n            transfer-encoding: chunked\n\n    + Body\n\n            {\n                \"status\": \"group already exist\",\n                \"id\": 1,\n                \"name\": \"foo\"\n            }\n    + Schema\n    \n            {\n                \"type\": \"object\",\n                \"properties\" : {\n                    \"status\" : {\n                        \"type\" : \"string\"\n                    },\n                    \"id\" : {\n                        \"type\" : \"int\"\n                    },\n                    \"name\" : {\n                        \"type\" : \"string\"\n                    }\n                }\n            }\n\n+ Request(application/json)\n\n    + Body\n\n            {\n                \"access_token\": \"wrong token\",\n                \"name\" : \"foo\"\n            }\n    \n    + Schema\n    \n            {\n                \"type\": \"object\",\n                \"properties\" : {\n                    \"name\" : {\n                        \"type\" : \"string\"\n                    },\n                    \"access_token\" : {\n                        \"type\" : \"string\"\n                    }\n                }\n            }\n\n+ Response 401\n\n    + Headers\n\n            x-frame-options: SAMEORIGIN\n            x-xss-protection: 1; mode=block\n            x-content-type-options: nosniff\n            content-type: text/html\n            cache-control: no-cache\n            x-request-id: 30276bf6-0ce6-4ba6-b87d-7528b4f2c85d\n            x-runtime: 0.002650\n            connection: close\n            transfer-encoding: chunked\n\n    + Body\n\n# Group Users [/api/v1/groups/{group_id}/users]\n\n+ Parameters\n    + group_id: 1 (required, number) - ID of the Group in form of an integer\n\n## Add user [POST]\n\n+ user_id (required, number) - ID of the User in form of an integer\n+ expiration_date (optional, date) - Membership expiration date in format (YYYY-MM-DD)\n\n+ Request (application/json)\n\n    + Header\n\n            Authorization: <user-token>\n\n    + Body\n\n            {\n                \"user_id\": 7,\n                \"expiration_date\": \"2019-11-20\"\n            }\n\n+ Response 204\n"
  },
  {
    "path": "api_blueprint/hooks/dredd_hooks.rb",
    "content": "ENV['RAILS_ENV'] ||= 'test'\n\nrequire File.expand_path('../../config/environment', __dir__)\nrequire 'dredd_hooks/methods'\n\ninclude DreddHooks::Methods\n\nbefore_all do |_|\n  user = User.create(name: 'foo', email: 'bar@test.com', admin: 1)\n  access_token = AccessToken.new\n  access_token.token = 'token'\n  access_token.user = user\n  user.access_token = access_token\n  access_token.save!\n  user.save!\n  inactive_user = User.create(name: 'foo', email: 'foo@test2.com', active: 0, user_login_id: 'foo')\n  inactive_user.save!\n  active_user = User.create(name: 'foo', email: 'foo@test.com', active: 1, user_login_id: 'foo')\n  active_user.save!\nend\n"
  },
  {
    "path": "api_blueprint/user.apib",
    "content": "FORMAT: 1A\n\n# API User\n\n# User [/api/v1/users]\n\n## Create Users [POST]\nCreate new users gate\n\n+ Request(application/json)\n\n    + Body\n    \n            {\n                \"access_token\": \"token\",\n                \"name\" : \"foo\",\n                \"email\" : \"foo@test.com\"\n            }\n\n    + Schema\n    \n            {\n                \"type\": \"object\",\n                \"properties\" : {\n                    \"name\" : {\n                        \"type\" : \"string\"\n                    },\n                    \"email\" : {\n                        \"type\" : \"string\"\n                    },\n                    \"access_token\" : {\n                        \"type\" : \"string\"\n                    }\n                }\n            }\n\n+ Response 200\n\n    + Headers\n\n            x-frame-options: SAMEORIGIN\n            x-xss-protection: 1; mode=block\n            x-content-type-options: nosniff\n            content-type: application/json; charset=utf-8\n            etag: W/\"5c15461069e69109955c72671ffc465d\"\n            cache-control: max-age=0, private, must-revalidate\n            x-request-id: 0613ec11-afd7-4dc0-9096-fee607d71c12\n            x-runtime: 0.073641\n            connection: close\n            transfer-encoding: chunked\n\n    + Body\n    \n            {\n                \"status\" : \"created\"\n            }\n\n    + Schema\n    \n            {\n                \"type\": \"object\",\n                \"properties\" : {\n                    \"status\" : {\n                        \"type\" : \"string\"\n                    }\n                }\n            }\n\n\n+ Request(application/json)\n\n    + Body\n    \n            {\n                \"access_token\": \"wrong token\",\n                \"name\" : \"foo\",\n                \"email\" : \"foo@test.com\"\n            }\n\n    + Schema\n    \n            {\n                \"type\": \"object\",\n                \"properties\" : {\n                    \"name\" : {\n                        \"type\" : \"string\"\n                    },\n                    \"email\" : {\n                        \"type\" : \"string\"\n                    },\n                    \"access_token\" : {\n                        \"type\" : \"string\"\n                    }\n                }\n            }\n\n+ Response 401\n\n    + Headers\n\n            x-frame-options: SAMEORIGIN\n            x-xss-protection: 1; mode=block\n            x-content-type-options: nosniff\n            content-type: text/html\n            cache-control: no-cache\n            x-request-id: 30276bf6-0ce6-4ba6-b87d-7528b4f2c85d\n            x-runtime: 0.002650\n            connection: close\n            transfer-encoding: chunked\n\n    + Body\n\n# User [/api/v1/users/profile/{?email,uid,username,active}]\n\n## Get Users [GET]\nGet user data with email, uid or username\n\n+ Request\n\n    + Headers\n\n            Authorization: token\n\n    + Parameters\n\n        + email (string, optional)\n            + Default: bar@test.com\n        + uid (string, optional)\n        + username (string, optional)\n        + active ((string, boolean, int), optional)\n\n+ Response 200\n\n    + Body\n\n            {\n                \"email\": \"bar@test.com\",\n                \"id\": null,\n                \"uid\": null,\n                \"name\": \"foo\",\n                \"active\": true,\n                \"admin\": true,\n                \"home_dir\": null,\n                \"shell\": null,\n                \"public_key\": null,\n                \"user_login_id\": null,\n                \"product_name\": null,\n                \"groups\": []\n            }\n\n+ Request\n\n    + Headers\n\n            Authorization: token\n\n    + Parameters\n\n        + email (string, optional)\n        + uid (string, optional)\n        + username (string, optional)\n            + Default: foo\n        + active ((string, boolean, int), optional)\n            + Default: 1\n\n+ Response 200\n\n    + Body\n\n            {\n                \"email\": \"foo@test.com\",\n                \"uid\": null,\n                \"name\": \"foo\",\n                \"active\": true,\n                \"admin\": true,\n                \"home_dir\": null,\n                \"shell\": null,\n                \"public_key\": null,\n                \"user_login_id\": \"foo\",\n                \"product_name\": null,\n                \"groups\": []\n            }\n            \n+ Request\n\n    + Headers\n\n            Authorization: token\n\n    + Parameters\n\n        + email (string, optional)\n            + Default: notfound@test.com\n        + uid (string, optional)\n        + username (string, optional)\n        + active ((string, boolean, int), optional)\n\n+ Response 404\n\n    + Body\n\n# User [/api/v1/users/profile]\n\n## Update Users [POST]\nUpdate users data with email as key \n\n+ Request(application/json)\n\n    + Headers\n\n            Authorization: token\n\n    + Body\n    \n            {\n                \"name\" : \"foo bar\",\n                \"email\" : \"bar@test.com\"\n            }\n\n    + Schema\n    \n            {\n                \"type\": \"object\",\n                \"properties\" : {\n                    \"name\" : {\n                        \"type\" : \"string\"\n                    },\n                    \"email\" : {\n                        \"type\" : \"string\"\n                    }\n                }\n            }\n\n+ Response 200\n\n    + Headers\n\n            x-frame-options: SAMEORIGIN\n            x-xss-protection: 1; mode=block\n            x-content-type-options: nosniff\n            content-type: application/json; charset=utf-8\n            etag: W/\"c955e57777ec0d73639dca6748560d00\"\n            cache-control: max-age=0, private, must-revalidate\n            x-request-id: 136874b1-39fb-4704-b548-d7670773a4bf\n            x-runtime: 0.014707\n            connection: close\n            transfer-encoding: chunked\n\n    + Body\n\n            {\n                \"success\": true\n            }    \n\n\n+ Request(application/json)\n\n    + Headers\n\n            Authorization: wrong-token\n\n    + Body\n    \n            {\n                \"name\" : \"foo bar\",\n                \"email\" : \"bar@test.com\"\n            }\n\n    + Schema\n    \n            {\n                \"type\": \"object\",\n                \"properties\" : {\n                    \"name\" : {\n                        \"type\" : \"string\"\n                    },\n                    \"email\" : {\n                        \"type\" : \"string\"\n                    }\n                }\n            }\n\n+ Response 401\n\n    + Headers\n\n            x-frame-options: SAMEORIGIN\n            x-xss-protection: 1; mode=block\n            x-content-type-options: nosniff\n            content-type: text/html\n            cache-control: no-cache\n            x-request-id: 85cb1679-58d4-41c4-9479-ce5f9c75ca7a\n            x-runtime: 0.002542\n            connection: close\n            transfer-encoding: chunked\n"
  },
  {
    "path": "api_blueprint/vpns.apib",
    "content": "FORMAT: 1A\n\n# API VPNS\n\n# VPNS [/api/v1/vpns]\n\n## Create VPNS [POST]\nCreate new vpns\n\n+ Request(application/json)\n\n    + Headers\n    \n            Authorization: token\n\n    + Body\n    \n            {\n                \"id\": 1,\n                \"name\": \"test-vpn\",\n                \"host_name\": \"test-vpn.example.com\",\n                \"ip_address\" : \"10.10.10.10\"\n            }\n\n    + Schema\n\n            {\n                \"type\": \"object\",\n                \"properties\" : {\n                    \"id\" : {\n                        \"type\" : int\n                    },\n                    \"name\" : {\n                        \"type\" : \"string\"\n                    },\n                    \"host_name\" : {\n                        \"type\" : \"string\"\n                    },\n                    \"ip_address\" : {\n                        \"type\" : \"string\"\n                    }\n                }\n            }\n\n+ Response 200\n\n    + Body\n\n            {\n                \"id\": 1,\n                \"name\": \"test-vpn\",\n                \"host_name\": \"test-vpn.example.com\",\n                \"ip_address\": \"10.10.10.10\"\n            }\n\n+ Request(application/json)\n\n    + Headers\n    \n            Authorization: wrong-token\n    \n    + Body\n\n+ Response 401\n\n    + Headers\n\n            x-frame-options: SAMEORIGIN\n            x-xss-protection: 1; mode=block\n            x-content-type-options: nosniff\n            content-type: text/html\n            cache-control: no-cache\n            x-request-id: 73b10aa8-6f68-400b-b723-2e992ac4d000\n            x-runtime: 0.002242\n            connection: close\n            transfer-encoding: chunked\n"
  },
  {
    "path": "app/assets/config/manifest.js",
    "content": "//= link application.css\n//= link apple-touch-icon-144x144-precomposed.png\n//= link apple-touch-icon-72x72-precomposed.png\n//= link apple-touch-icon-precomposed.png\n//= link favicon.ico\n//= link application.js\n"
  },
  {
    "path": "app/assets/images/.keep",
    "content": ""
  },
  {
    "path": "app/assets/javascripts/admin.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://coffeescript.org/\n"
  },
  {
    "path": "app/assets/javascripts/api_resources.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://coffeescript.org/\n\nAPI_RESOURCE_NAME_FORMAT_ERROR_MSG = \"Please enter valid name containing only alphanumeric (a-z, A-Z, 0-9), underscore (_) and dash (-)\"\nAPI_RESOURCE_NAME_UNIQUENESS_ERROR_MSG = \"API name already taken\"\nAPI_RESOURCE_DESC_REQUIRED_ERROR_MSG = \"Please enter description\"\n\napi_resources_ready = ->\n  $('#api_resource_name').on 'blur', ->\n    valid = validate_pattern($('#api_resource_name'), '^[a-zA-Z0-9_-]+$', API_RESOURCE_NAME_FORMAT_ERROR_MSG)\n    if valid \n      validate_uniqueness($('#api_resource_name'), '/api_resources/search', API_RESOURCE_NAME_UNIQUENESS_ERROR_MSG)\n\n  $('#api_resource_description').on 'blur', ->\n    validate_required($('#api_resource_description'), API_RESOURCE_DESC_REQUIRED_ERROR_MSG)\n\n  $('#new_api_resource').submit (event) ->\n    if $('.is-invalid').length > 0\n      event.preventDefault()\n      event.stopPropagation()\n    return\n\n$(document).on('turbolinks:load', api_resources_ready)\n"
  },
  {
    "path": "app/assets/javascripts/application.js",
    "content": "// This is a manifest file that'll be compiled into application.js, which will include all the files\n// listed below.\n//\n// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,\n// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.\n//\n// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the\n// compiled file.\n//\n// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details\n// about supported directives.\n//\n// require jquery3\n// require popper\n// require bootstrap-sprockets\n// require jquery_ujs\n// require turbolinks\n// require_tree .\n"
  },
  {
    "path": "app/assets/javascripts/bootstrap.js.coffee",
    "content": "jQuery ->\n  $(\"a[rel~=popover], .has-popover\").popover()\n  $(\"a[rel~=tooltip], .has-tooltip\").tooltip()\n"
  },
  {
    "path": "app/assets/javascripts/group.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://coffeescript.org/\n\ngroup_ready = ->\n  $('#assign_admin_include_inactive_user').prop('checked', false);\n  $('#assign_admin_user_id').selectize\n    maxItems: 1\n    valueField: 'id'\n    labelField: 'name'\n    searchField: 'name_email'\n    create: false\n    render: option: (item, escape) ->\n      '<div>' + escape(item.name) + '<br /><span class=\\'small\\'>' + escape(item.email) + '</span></div>'\n    load: (query, callback) ->\n      if !query.length\n        return callback()\n\n      # Clear the cache, because user list may change\n      this.clearOptions()\n      include_inactive = $('#assign_admin_include_inactive_user').is(':checked')\n\n      # Use remote as source\n      $.ajax\n        url: '/users/search?q=' + encodeURIComponent(query) + '&include_inactive=' + include_inactive\n        type: 'GET'\n        error: ->\n          callback()\n          return\n        success: (res) ->\n          callback res\n          return\n      return\n\n  $('#assign_admin_user_id').on 'change', ->\n    set_allow_submit($(this).val(), $(this))\n\n  $('#add_user_include_inactive_user').prop('checked', false);\n  $('#add_user_user_id').selectize\n    maxItems: 1\n    valueField: 'id'\n    labelField: 'name'\n    searchField: 'name_email'\n    create: false\n    render: option: (item, escape) ->\n      '<div>' + escape(item.name) + '<br /><span class=\\'small\\'>' + escape(item.email) + '</span></div>'\n    load: (query, callback) ->\n      if !query.length\n        return callback()\n\n      # Clear the cache, because user list may change\n      this.clearOptions()\n      include_inactive = $('#add_user_include_inactive_user').is(':checked')\n\n      # Use remote as source\n      $.ajax\n        url: '/users/search?q=' + encodeURIComponent(query) + '&include_inactive=' + include_inactive\n        type: 'GET'\n        error: ->\n          callback()\n          return\n        success: (res) ->\n          callback res\n          return\n      return\n\n  $('#add_user_user_id').on 'change', ->\n    set_allow_submit($(this).val(), $(this))\n\n  $('#add_vpn_vpn_id').selectize\n    maxItems: 1\n    valueField: 'id'\n    labelField: 'name'\n    searchField: 'name'\n    create: false\n    render: option: (item, escape) ->\n      '<div>' + escape(item.name) + '</div>'\n    load: (query, callback) ->\n      if !query.length\n        return callback()\n\n      # Use remote as source\n      $.ajax\n        url: '/vpns/search?q=' + encodeURIComponent(query) \n        type: 'GET'\n        error: ->\n          callback()\n          return\n        success: (res) ->\n          callback res\n          return\n      return\n\n  $('#add_vpn_vpn_id').on 'change', ->\n    set_allow_submit($(this).val(), $(this))\n\n  $('#add_machine_machine_id').selectize\n    maxItems: 1\n    valueField: 'id'\n    labelField: 'name'\n    searchField: 'name'\n    create: false\n    render: option: (item, escape) ->\n      '<div>' + escape(item.name) + '</div>'\n    load: (query, callback) ->\n      if !query.length\n        return callback()\n\n      # Use remote as source\n      $.ajax\n        url: '/host_machines/search?q=' + encodeURIComponent(query) \n        type: 'GET'\n        error: ->\n          callback()\n          return\n        success: (res) ->\n          callback res\n          return\n      return\n\n  $('#add_machine_machine_id').on 'change', ->\n    set_allow_submit($(this).val(), $(this))\n\n$(document).on('turbolinks:load', group_ready)\n"
  },
  {
    "path": "app/assets/javascripts/groups.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://coffeescript.org/\n"
  },
  {
    "path": "app/assets/javascripts/home.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://coffeescript.org/\n"
  },
  {
    "path": "app/assets/javascripts/host_access_groups.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://coffeescript.org/\n"
  },
  {
    "path": "app/assets/javascripts/host_machine_groups.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://coffeescript.org/\n"
  },
  {
    "path": "app/assets/javascripts/host_machines.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://coffeescript.org/\n\nhost_machines_ready = ->\n  $('#group_id').selectize\n    maxItems: 1\n    valueField: 'id'\n    labelField: 'name'\n    searchField: 'name'\n    create: false\n    render: option: (item, escape) ->\n      '<div>' + escape(item.name) + '</div>'\n    load: (query, callback) ->\n      if !query.length\n        return callback()\n\n      # Use remote as source\n      $.ajax\n        url: '/groups/search?q=' + encodeURIComponent(query) \n        type: 'GET'\n        error: ->\n          callback()\n          return\n        success: (res) ->\n          callback res\n          return\n      return\n\n  $('#group_id').on 'change', ->\n    set_allow_submit($(this).val(), $(this))\n\n$(document).on('turbolinks:load', host_machines_ready)\n"
  },
  {
    "path": "app/assets/javascripts/nss.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://coffeescript.org/\n"
  },
  {
    "path": "app/assets/javascripts/omniauth_callbacks.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://coffeescript.org/\n"
  },
  {
    "path": "app/assets/javascripts/profile.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://coffeescript.org/\n"
  },
  {
    "path": "app/assets/javascripts/users.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://coffeescript.org/\n\nusers_ready = ->\n  $('#group_id').selectize\n    maxItems: 1\n    valueField: 'id'\n    labelField: 'name'\n    searchField: 'name'\n    create: false\n    render: option: (item, escape) ->\n      '<div>' + escape(item.name) + '</div>'\n    load: (query, callback) ->\n      if !query.length\n        return callback()\n\n      # Use remote as source\n      $.ajax\n        url: '/groups/search?q=' + encodeURIComponent(query) \n        type: 'GET'\n        error: ->\n          callback()\n          return\n        success: (res) ->\n          callback res\n          return\n      return\n\n  $('#group_id').on 'change', ->\n    set_allow_submit($(this).val(), $(this))\n\n$(document).on('turbolinks:load', users_ready)\n"
  },
  {
    "path": "app/assets/javascripts/utilities.coffee",
    "content": "# Utility functions\n\n@append_error_msg = (elem, res, msg) ->\n  if !res\n    elem.addClass('is-invalid')\n    elem.next('.invalid-feedback').html(msg)\n  else\n    elem.removeClass('is-invalid')\n    elem.next('.invalid-feedback').html('')\n\n@validate_required = (elem, msg) ->\n  input = elem.val()\n  append_error_msg(elem, input, msg)\n  return !!input\n\n@validate_pattern = (elem, pattern, msg) ->\n  input = elem.val()\n  regex = new RegExp(pattern)\n  validate_regex = regex.test(input)\n  append_error_msg(elem, validate_regex, msg)\n  return !!validate_regex\n\n@validate_uniqueness = (elem, check_url, msg) ->\n  input = elem.val()\n  $.ajax\n    url: check_url + '?q=' + encodeURIComponent(input) + '&exact=true'\n    type: 'GET'\n    error: ->\n      return\n    success: (res) ->\n      append_error_msg(elem, $.isEmptyObject(res), msg)\n      return\n\n@set_allow_submit = (cond, elem) ->\n  curSubmit = $(\"input[type=submit]\", $(elem).parents('form'))\n  if !!cond\n    curSubmit.prop(\"disabled\", false)\n  else\n    curSubmit.prop('disabled', true)\n  return\n"
  },
  {
    "path": "app/assets/javascripts/viewport.js",
    "content": "/*!\n * IE10 viewport hack for Surface/desktop Windows 8 bug\n * Copyright 2014-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n// See the Getting Started docs for more information:\n// http://getbootstrap.com/getting-started/#support-ie10-width\n\n(function () {\n  'use strict';\n\n  if (navigator.userAgent.match(/IEMobile\\/10\\.0/)) {\n    var msViewportStyle = document.createElement('style')\n    msViewportStyle.appendChild(\n      document.createTextNode(\n        '@-ms-viewport{width:auto!important}'\n      )\n    )\n    document.querySelector('head').appendChild(msViewportStyle)\n  }\n\n})();\n"
  },
  {
    "path": "app/assets/stylesheets/application.scss",
    "content": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below.\n *\n * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,\n * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.\n *\n * You're free to add application-wide styles to this file and they'll appear at the bottom of the\n * compiled file so the styles you add here take precedence over styles defined in any styles\n * defined in the other CSS/SCSS files in this directory. It is generally better to create a new\n * file per style scope.\n *\n *= require_tree .\n *= require_self\n *= stub 'profile'\n *= stub 'general'\n */\n\n\n@import url(https://fonts.googleapis.com/css?family=Roboto:300);\n@import url(https://fonts.googleapis.com/css?family=Lato:300);\n@import \"bootstrap\";\n@import \"font-awesome\";\n\n/*\n * https://github.com/ladjs/bootstrap-social\n */\n@import \"bootstrap-social\";\na.btn { color: white!important;  }\n.form-group .field_with_errors { display: block; background: none;  }\n.form-group .field_with_errors label::after { content: ' *'; color: red;  }\nbody {\n  font-family: 'Roboto', 'Lato', sans-serif;\n}\n\n.container-profile {\n  max-width: 768px;\n  min-width: 768px;\n}\n\na:visited {\n    color: blue;\n}\n/* mouse over link */\na:hover {\n    color: blue;\n    background: none;\n}\n.table-responsive { overflow-x: inherit;  }\n\n#configAppTabsContent .tab-pane { padding-top: 20px; }\n#configAppTabsContent ol li { margin-top: 10px; }\n#configAppTabsContent li > ol { margin-bottom: 10px; }\n"
  },
  {
    "path": "app/assets/stylesheets/bootstrap-social.scss",
    "content": "/*\n * Social Buttons for Bootstrap\n *\n * Copyright 2013-2016 Panayiotis Lipiridis\n * Licensed under the MIT License\n *\n * https://github.com/lipis/bootstrap-social\n */\n.btn-social, .btn-social-icon {\n  position: relative;\n  padding-left: 3.5rem;\n  text-align: left;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis; }\n  .btn-social > :first-child, .btn-social-icon > :first-child {\n    position: absolute;\n    left: 0;\n    top: 0;\n    bottom: 0;\n    width: 3rem;\n    line-height: 5rem;\n    font-size: 1.6em;\n    text-align: center;\n    border-right: 1px solid rgba(0, 0, 0, 0.2); }\n  .btn-social.btn-lg, .btn-lg.btn-social-icon {\n    padding-left: 4.75rem; }\n    .btn-social.btn-lg > :first-child, .btn-lg.btn-social-icon > :first-child {\n      line-height: 4rem;\n      width: 4rem;\n      font-size: 1.8em; }\n  .btn-social.btn-sm, .btn-sm.btn-social-icon {\n    padding-left: 2.25rem; }\n    .btn-social.btn-sm > :first-child, .btn-sm.btn-social-icon > :first-child {\n      line-height: 2rem;\n      width: 2rem;\n      font-size: 1.4em; }\n  .btn-social.btn-xs, .btn-xs.btn-social-icon {\n    padding-left: 2.25rem; }\n    .btn-social.btn-xs > :first-child, .btn-xs.btn-social-icon > :first-child {\n      line-height: 2rem;\n      width: 2rem;\n      font-size: 1.2em; }\n\n.btn-social > :first-child, .btn-social-icon > :first-child {\n  line-height: 1.25 !important;\n  padding-top: 0.5rem !important;\n  padding-bottom: 0.5rem !important;\n  font-size: inherit !important; }\n.btn-social.btn-lg > :first-child, .btn-lg.btn-social-icon > :first-child {\n  line-height: 1.25 !important;\n  padding-top: 0.75rem !important;\n  padding-bottom: 0.75rem !important;\n  font-size: inherit !important; }\n\n.btn-social-icon {\n  height: 3rem;\n  width: 3rem;\n  padding: 0; }\n  .btn-social-icon > :first-child {\n    border: none;\n    text-align: center;\n    width: 100% !important; }\n  .btn-social-icon.btn-lg {\n    height: 4rem;\n    width: 4rem;\n    padding-left: 0;\n    padding-right: 0; }\n  .btn-social-icon.btn-sm {\n    height: 2rem;\n    width: 2rem;\n    padding-left: 0;\n    padding-right: 0; }\n  .btn-social-icon.btn-xs {\n    height: 2rem;\n    width: 2rem;\n    padding-left: 0;\n    padding-right: 0; }\n\n.btn-adn {\n  color: #fff;\n  background-color: #d87a68;\n  border-color: #d87a68; }\n  .btn-adn:hover {\n    color: #fff;\n    background-color: #d05f4a;\n    border-color: #ce563f; }\n  .btn-adn:focus, .btn-adn.focus {\n    box-shadow: 0 0 0 3px rgba(216, 122, 104, 0.5); }\n  .btn-adn.disabled, .btn-adn:disabled {\n    background-color: #d87a68;\n    border-color: #d87a68; }\n  .btn-adn:active, .btn-adn.active, .show > .btn-adn.dropdown-toggle {\n    background-color: #d05f4a;\n    background-image: none;\n    border-color: #ce563f; }\n\n.btn-bitbucket {\n  color: #fff;\n  background-color: #205081;\n  border-color: #205081; }\n  .btn-bitbucket:hover {\n    color: #fff;\n    background-color: #183d62;\n    border-color: #163758; }\n  .btn-bitbucket:focus, .btn-bitbucket.focus {\n    box-shadow: 0 0 0 3px rgba(32, 80, 129, 0.5); }\n  .btn-bitbucket.disabled, .btn-bitbucket:disabled {\n    background-color: #205081;\n    border-color: #205081; }\n  .btn-bitbucket:active, .btn-bitbucket.active, .show > .btn-bitbucket.dropdown-toggle {\n    background-color: #183d62;\n    background-image: none;\n    border-color: #163758; }\n\n.btn-dropbox {\n  color: #fff;\n  background-color: #1087dd;\n  border-color: #1087dd; }\n  .btn-dropbox:hover {\n    color: #fff;\n    background-color: #0d71b9;\n    border-color: #0d6aad; }\n  .btn-dropbox:focus, .btn-dropbox.focus {\n    box-shadow: 0 0 0 3px rgba(16, 135, 221, 0.5); }\n  .btn-dropbox.disabled, .btn-dropbox:disabled {\n    background-color: #1087dd;\n    border-color: #1087dd; }\n  .btn-dropbox:active, .btn-dropbox.active, .show > .btn-dropbox.dropdown-toggle {\n    background-color: #0d71b9;\n    background-image: none;\n    border-color: #0d6aad; }\n\n.btn-facebook {\n  color: #fff;\n  background-color: #3b5998;\n  border-color: #3b5998; }\n  .btn-facebook:hover {\n    color: #fff;\n    background-color: #30497c;\n    border-color: #2d4373; }\n  .btn-facebook:focus, .btn-facebook.focus {\n    box-shadow: 0 0 0 3px rgba(59, 89, 152, 0.5); }\n  .btn-facebook.disabled, .btn-facebook:disabled {\n    background-color: #3b5998;\n    border-color: #3b5998; }\n  .btn-facebook:active, .btn-facebook.active, .show > .btn-facebook.dropdown-toggle {\n    background-color: #30497c;\n    background-image: none;\n    border-color: #2d4373; }\n\n.btn-flickr {\n  color: #fff;\n  background-color: #ff0084;\n  border-color: #ff0084; }\n  .btn-flickr:hover {\n    color: #fff;\n    background-color: #d90070;\n    border-color: #cc006a; }\n  .btn-flickr:focus, .btn-flickr.focus {\n    box-shadow: 0 0 0 3px rgba(255, 0, 132, 0.5); }\n  .btn-flickr.disabled, .btn-flickr:disabled {\n    background-color: #ff0084;\n    border-color: #ff0084; }\n  .btn-flickr:active, .btn-flickr.active, .show > .btn-flickr.dropdown-toggle {\n    background-color: #d90070;\n    background-image: none;\n    border-color: #cc006a; }\n\n.btn-foursquare {\n  color: #fff;\n  background-color: #f94877;\n  border-color: #f94877; }\n  .btn-foursquare:hover {\n    color: #fff;\n    background-color: #f8235b;\n    border-color: #f71752; }\n  .btn-foursquare:focus, .btn-foursquare.focus {\n    box-shadow: 0 0 0 3px rgba(249, 72, 119, 0.5); }\n  .btn-foursquare.disabled, .btn-foursquare:disabled {\n    background-color: #f94877;\n    border-color: #f94877; }\n  .btn-foursquare:active, .btn-foursquare.active, .show > .btn-foursquare.dropdown-toggle {\n    background-color: #f8235b;\n    background-image: none;\n    border-color: #f71752; }\n\n.btn-github {\n  color: #fff;\n  background-color: #444444;\n  border-color: #444444; }\n  .btn-github:hover {\n    color: #fff;\n    background-color: #313131;\n    border-color: #2b2b2b; }\n  .btn-github:focus, .btn-github.focus {\n    box-shadow: 0 0 0 3px rgba(68, 68, 68, 0.5); }\n  .btn-github.disabled, .btn-github:disabled {\n    background-color: #444444;\n    border-color: #444444; }\n  .btn-github:active, .btn-github.active, .show > .btn-github.dropdown-toggle {\n    background-color: #313131;\n    background-image: none;\n    border-color: #2b2b2b; }\n\n.btn-google {\n  color: #fff;\n  background-color: #dd4b39;\n  border-color: #dd4b39; }\n  .btn-google:hover {\n    color: #fff;\n    background-color: #cd3623;\n    border-color: #c23321; }\n  .btn-google:focus, .btn-google.focus {\n    box-shadow: 0 0 0 3px rgba(221, 75, 57, 0.5); }\n  .btn-google.disabled, .btn-google:disabled {\n    background-color: #dd4b39;\n    border-color: #dd4b39; }\n  .btn-google:active, .btn-google.active, .show > .btn-google.dropdown-toggle {\n    background-color: #cd3623;\n    background-image: none;\n    border-color: #c23321; }\n\n.btn-instagram {\n  color: #fff;\n  background-color: #3f729b;\n  border-color: #3f729b; }\n  .btn-instagram:hover {\n    color: #fff;\n    background-color: #345e80;\n    border-color: #305777; }\n  .btn-instagram:focus, .btn-instagram.focus {\n    box-shadow: 0 0 0 3px rgba(63, 114, 155, 0.5); }\n  .btn-instagram.disabled, .btn-instagram:disabled {\n    background-color: #3f729b;\n    border-color: #3f729b; }\n  .btn-instagram:active, .btn-instagram.active, .show > .btn-instagram.dropdown-toggle {\n    background-color: #345e80;\n    background-image: none;\n    border-color: #305777; }\n\n.btn-linkedin {\n  color: #fff;\n  background-color: #007bb6;\n  border-color: #007bb6; }\n  .btn-linkedin:hover {\n    color: #fff;\n    background-color: #006190;\n    border-color: #005983; }\n  .btn-linkedin:focus, .btn-linkedin.focus {\n    box-shadow: 0 0 0 3px rgba(0, 123, 182, 0.5); }\n  .btn-linkedin.disabled, .btn-linkedin:disabled {\n    background-color: #007bb6;\n    border-color: #007bb6; }\n  .btn-linkedin:active, .btn-linkedin.active, .show > .btn-linkedin.dropdown-toggle {\n    background-color: #006190;\n    background-image: none;\n    border-color: #005983; }\n\n.btn-microsoft {\n  color: #fff;\n  background-color: #2672ec;\n  border-color: #2672ec; }\n  .btn-microsoft:hover {\n    color: #fff;\n    background-color: #135fd9;\n    border-color: #125acd; }\n  .btn-microsoft:focus, .btn-microsoft.focus {\n    box-shadow: 0 0 0 3px rgba(38, 114, 236, 0.5); }\n  .btn-microsoft.disabled, .btn-microsoft:disabled {\n    background-color: #2672ec;\n    border-color: #2672ec; }\n  .btn-microsoft:active, .btn-microsoft.active, .show > .btn-microsoft.dropdown-toggle {\n    background-color: #135fd9;\n    background-image: none;\n    border-color: #125acd; }\n\n.btn-odnoklassniki {\n  color: #fff;\n  background-color: #f4731c;\n  border-color: #f4731c; }\n  .btn-odnoklassniki:hover {\n    color: #fff;\n    background-color: #df600b;\n    border-color: #d35b0a; }\n  .btn-odnoklassniki:focus, .btn-odnoklassniki.focus {\n    box-shadow: 0 0 0 3px rgba(244, 115, 28, 0.5); }\n  .btn-odnoklassniki.disabled, .btn-odnoklassniki:disabled {\n    background-color: #f4731c;\n    border-color: #f4731c; }\n  .btn-odnoklassniki:active, .btn-odnoklassniki.active, .show > .btn-odnoklassniki.dropdown-toggle {\n    background-color: #df600b;\n    background-image: none;\n    border-color: #d35b0a; }\n\n.btn-openid {\n  color: #111;\n  background-color: #f7931e;\n  border-color: #f7931e; }\n  .btn-openid:hover {\n    color: #111;\n    background-color: #e78008;\n    border-color: #da7908; }\n  .btn-openid:focus, .btn-openid.focus {\n    box-shadow: 0 0 0 3px rgba(247, 147, 30, 0.5); }\n  .btn-openid.disabled, .btn-openid:disabled {\n    background-color: #f7931e;\n    border-color: #f7931e; }\n  .btn-openid:active, .btn-openid.active, .show > .btn-openid.dropdown-toggle {\n    background-color: #e78008;\n    background-image: none;\n    border-color: #da7908; }\n\n.btn-pinterest {\n  color: #fff;\n  background-color: #cb2027;\n  border-color: #cb2027; }\n  .btn-pinterest:hover {\n    color: #fff;\n    background-color: #aa1b21;\n    border-color: #9f191f; }\n  .btn-pinterest:focus, .btn-pinterest.focus {\n    box-shadow: 0 0 0 3px rgba(203, 32, 39, 0.5); }\n  .btn-pinterest.disabled, .btn-pinterest:disabled {\n    background-color: #cb2027;\n    border-color: #cb2027; }\n  .btn-pinterest:active, .btn-pinterest.active, .show > .btn-pinterest.dropdown-toggle {\n    background-color: #aa1b21;\n    background-image: none;\n    border-color: #9f191f; }\n\n.btn-reddit {\n  color: #111;\n  background-color: #eff7ff;\n  border-color: #eff7ff; }\n  .btn-reddit:hover {\n    color: #111;\n    background-color: #c9e4ff;\n    border-color: #bcdeff; }\n  .btn-reddit:focus, .btn-reddit.focus {\n    box-shadow: 0 0 0 3px rgba(239, 247, 255, 0.5); }\n  .btn-reddit.disabled, .btn-reddit:disabled {\n    background-color: #eff7ff;\n    border-color: #eff7ff; }\n  .btn-reddit:active, .btn-reddit.active, .show > .btn-reddit.dropdown-toggle {\n    background-color: #c9e4ff;\n    background-image: none;\n    border-color: #bcdeff; }\n\n.btn-soundcloud {\n  color: #fff;\n  background-color: #ff5500;\n  border-color: #ff5500; }\n  .btn-soundcloud:hover {\n    color: #fff;\n    background-color: #d94800;\n    border-color: #cc4400; }\n  .btn-soundcloud:focus, .btn-soundcloud.focus {\n    box-shadow: 0 0 0 3px rgba(255, 85, 0, 0.5); }\n  .btn-soundcloud.disabled, .btn-soundcloud:disabled {\n    background-color: #ff5500;\n    border-color: #ff5500; }\n  .btn-soundcloud:active, .btn-soundcloud.active, .show > .btn-soundcloud.dropdown-toggle {\n    background-color: #d94800;\n    background-image: none;\n    border-color: #cc4400; }\n\n.btn-tumblr {\n  color: #fff;\n  background-color: #2c4762;\n  border-color: #2c4762; }\n  .btn-tumblr:hover {\n    color: #fff;\n    background-color: #203448;\n    border-color: #1c2e3f; }\n  .btn-tumblr:focus, .btn-tumblr.focus {\n    box-shadow: 0 0 0 3px rgba(44, 71, 98, 0.5); }\n  .btn-tumblr.disabled, .btn-tumblr:disabled {\n    background-color: #2c4762;\n    border-color: #2c4762; }\n  .btn-tumblr:active, .btn-tumblr.active, .show > .btn-tumblr.dropdown-toggle {\n    background-color: #203448;\n    background-image: none;\n    border-color: #1c2e3f; }\n\n.btn-twitter {\n  color: #fff;\n  color: #fff;\n  background-color: #1DA1F2;\n  border-color: #1DA1F2; }\n  .btn-twitter:hover {\n    color: #fff;\n    background-color: #0d8ddc;\n    border-color: #0c85d0; }\n  .btn-twitter:focus, .btn-twitter.focus {\n    box-shadow: 0 0 0 3px rgba(29, 161, 242, 0.5); }\n  .btn-twitter.disabled, .btn-twitter:disabled {\n    background-color: #1DA1F2;\n    border-color: #1DA1F2; }\n  .btn-twitter:active, .btn-twitter.active, .show > .btn-twitter.dropdown-toggle {\n    background-color: #0d8ddc;\n    background-image: none;\n    border-color: #0c85d0; }\n\n.btn-vimeo {\n  color: #fff;\n  background-color: #1ab7ea;\n  border-color: #1ab7ea; }\n  .btn-vimeo:hover {\n    color: #fff;\n    background-color: #139ecb;\n    border-color: #1295bf; }\n  .btn-vimeo:focus, .btn-vimeo.focus {\n    box-shadow: 0 0 0 3px rgba(26, 183, 234, 0.5); }\n  .btn-vimeo.disabled, .btn-vimeo:disabled {\n    background-color: #1ab7ea;\n    border-color: #1ab7ea; }\n  .btn-vimeo:active, .btn-vimeo.active, .show > .btn-vimeo.dropdown-toggle {\n    background-color: #139ecb;\n    background-image: none;\n    border-color: #1295bf; }\n\n.btn-vk {\n  color: #fff;\n  background-color: #587ea3;\n  border-color: #587ea3; }\n  .btn-vk:hover {\n    color: #fff;\n    background-color: #4b6b8a;\n    border-color: #466482; }\n  .btn-vk:focus, .btn-vk.focus {\n    box-shadow: 0 0 0 3px rgba(88, 126, 163, 0.5); }\n  .btn-vk.disabled, .btn-vk:disabled {\n    background-color: #587ea3;\n    border-color: #587ea3; }\n  .btn-vk:active, .btn-vk.active, .show > .btn-vk.dropdown-toggle {\n    background-color: #4b6b8a;\n    background-image: none;\n    border-color: #466482; }\n\n.btn-yahoo {\n  color: #fff;\n  background-color: #720e9e;\n  border-color: #720e9e; }\n  .btn-yahoo:hover {\n    color: #fff;\n    background-color: #590b7b;\n    border-color: #500a6f; }\n  .btn-yahoo:focus, .btn-yahoo.focus {\n    box-shadow: 0 0 0 3px rgba(114, 14, 158, 0.5); }\n  .btn-yahoo.disabled, .btn-yahoo:disabled {\n    background-color: #720e9e;\n    border-color: #720e9e; }\n  .btn-yahoo:active, .btn-yahoo.active, .show > .btn-yahoo.dropdown-toggle {\n    background-color: #590b7b;\n    background-image: none;\n    border-color: #500a6f; }\n\n.btn-stripe {\n  color: #fff;\n  background-color: #1275FF;\n  border-color: #1275FF; }\n  .btn-stripe:hover {\n    color: #fff;\n    background-color: #0062eb;\n    border-color: #005dde; }\n  .btn-stripe:focus, .btn-stripe.focus {\n    box-shadow: 0 0 0 3px rgba(18, 117, 255, 0.5); }\n  .btn-stripe.disabled, .btn-stripe:disabled {\n    background-color: #1275FF;\n    border-color: #1275FF; }\n  .btn-stripe:active, .btn-stripe.active, .show > .btn-stripe.dropdown-toggle {\n    background-color: #0062eb;\n    background-image: none;\n    border-color: #005dde; }\n\n.btn-amazon {\n  color: #232F3E;\n  color: #111;\n  background-color: #FF9900;\n  border-color: #FF9900; }\n  .btn-amazon:hover {\n    color: #111;\n    background-color: #d98200;\n    border-color: #cc7a00; }\n  .btn-amazon:focus, .btn-amazon.focus {\n    box-shadow: 0 0 0 3px rgba(255, 153, 0, 0.5); }\n  .btn-amazon.disabled, .btn-amazon:disabled {\n    background-color: #FF9900;\n    border-color: #FF9900; }\n  .btn-amazon:active, .btn-amazon.active, .show > .btn-amazon.dropdown-toggle {\n    background-color: #d98200;\n    background-image: none;\n    border-color: #cc7a00; }\n\n.btn-patreon {\n  color: #052D49;\n  color: #fff;\n  background-color: #F96854;\n  border-color: #F96854; }\n  .btn-patreon:hover {\n    color: #fff;\n    background-color: #f8472f;\n    border-color: #f73c23; }\n  .btn-patreon:focus, .btn-patreon.focus {\n    box-shadow: 0 0 0 3px rgba(249, 104, 84, 0.5); }\n  .btn-patreon.disabled, .btn-patreon:disabled {\n    background-color: #F96854;\n    border-color: #F96854; }\n  .btn-patreon:active, .btn-patreon.active, .show > .btn-patreon.dropdown-toggle {\n    background-color: #f8472f;\n    background-image: none;\n    border-color: #f73c23; }\n\n.btn-untappd {\n  color: #111;\n  background-color: #ffc000;\n  border-color: #ffc000; }\n  .btn-untappd:hover {\n    color: #111;\n    background-color: #d9a300;\n    border-color: #cc9a00; }\n  .btn-untappd:focus, .btn-untappd.focus {\n    box-shadow: 0 0 0 3px rgba(255, 192, 0, 0.5); }\n  .btn-untappd.disabled, .btn-untappd:disabled {\n    background-color: #ffc000;\n    border-color: #ffc000; }\n  .btn-untappd:active, .btn-untappd.active, .show > .btn-untappd.dropdown-toggle {\n    background-color: #d9a300;\n    background-image: none;\n    border-color: #cc9a00; }\n\n.btn-gitlab {\n  color: #fff;\n  background-color: #de7e00;\n  border-color: #de7e00; }\n  .btn-gitlab:hover {\n    color: #fff;\n    background-color: #b86800;\n    border-color: #ab6100; }\n  .btn-gitlab:focus, .btn-gitlab.focus {\n    box-shadow: 0 0 0 3px rgba(222, 126, 0, 0.5); }\n  .btn-gitlab.disabled, .btn-gitlab:disabled {\n    background-color: #de7e00;\n    border-color: #de7e00; }\n  .btn-gitlab:active, .btn-gitlab.active, .show > .btn-gitlab.dropdown-toggle {\n    background-color: #b86800;\n    background-image: none;\n    border-color: #ab6100; }\n\n.btn-whatsapp {\n  color: #fff;\n  color: #fff;\n  background-color: #25D366;\n  border-color: #25D366; }\n  .btn-whatsapp:hover {\n    color: #fff;\n    background-color: #1fb256;\n    border-color: #1da851; }\n  .btn-whatsapp:focus, .btn-whatsapp.focus {\n    box-shadow: 0 0 0 3px rgba(37, 211, 102, 0.5); }\n  .btn-whatsapp.disabled, .btn-whatsapp:disabled {\n    background-color: #25D366;\n    border-color: #25D366; }\n  .btn-whatsapp:active, .btn-whatsapp.active, .show > .btn-whatsapp.dropdown-toggle {\n    background-color: #1fb256;\n    background-image: none;\n    border-color: #1da851; }\n\n/*# sourceMappingURL=bootstrap-social.css.map */\n"
  },
  {
    "path": "app/assets/stylesheets/general.css",
    "content": "body {\n  padding-top: 70px;\n}\n"
  },
  {
    "path": "app/assets/stylesheets/home.scss.erb",
    "content": "// Place all the styles related to the Home controller here.\n// They will automatically be included in application.css.\n// You can use Sass (SCSS) here: http://sass-lang.com/\n\n:root {\n  --input-padding-x: .75rem;\n  --input-padding-y: .75rem;\n}\n\nhtml,\nbody {\n  height: 100%;\n}\n@import url(https://fonts.googleapis.com/css?family=Roboto:300);\n@import url(https://fonts.googleapis.com/css?family=Lato:300);\n\n.home {\n  display: -ms-flexbox;\n  display: -webkit-box;\n  display: flex;\n  -ms-flex-align: center;\n  -ms-flex-pack: center;\n  -webkit-box-align: center;\n  align-items: center;\n  -webkit-box-pack: center;\n  justify-content: center;\n  padding-top: 40px;\n  padding-bottom: 40px;\n  font-family: 'Roboto', sans-serif;\n  font-size: 16px;\n  font-weight: 300;\n  color: white;\n  line-height: 30px;\n  text-align: center;\n  background-image: image-url('bg.jpeg');\n  background-size: cover;\n  background-repeat: no-repeat;\n  background-color: #444444;\n  background-position: 50%;\n}\n\n.form-signin {\n  width: 100%;\n  max-width: 420px;\n  padding: 15px;\n  margin: 0 auto;\n}\n\n.token-qr table {\n  border-width: 0;\n  border-style: none;\n  border-color: #0000ff;\n  border-collapse: collapse;\n}\n\n.token-qr td {\n  border-left: solid 5px #000;\n  padding: 0; \n  margin: 0; \n  width: 0px; \n  height: 5px; \n}\n\n.token-qr td.black { border-color: #000; }\n.token-qr td.white { border-color: #fff; }\n\n"
  },
  {
    "path": "app/assets/stylesheets/profile.css",
    "content": "/* Everything but the jumbotron gets side spacing for mobile first views */\n\n.container-profile {\n  max-width: 730px;\n  min-width: 768px;\n}\n"
  },
  {
    "path": "app/assets/stylesheets/scaffolds.scss",
    "content": "body {\n  background-color: #fff;\n  color: #333;\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 13px;\n  line-height: 18px;\n}\n\np, ol, ul, td {\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 13px;\n  line-height: 18px;\n}\n\npre {\n  background-color: #eee;\n  padding: 10px;\n  font-size: 11px;\n}\n\na {\n  color: #000;\n\n  &:visited {\n    color: #666;\n  }\n\n  &:hover {\n    color: #fff;\n    background-color: #000;\n  }\n}\n\ndiv {\n  &.field, &.actions {\n    margin-bottom: 10px;\n  }\n}\n\n#notice {\n  color: green;\n}\n\n.field_with_errors {\n  padding: 2px;\n  background-color: red;\n  display: table;\n}\n\n#error_explanation {\n  width: 450px;\n  border: 2px solid red;\n  padding: 7px;\n  padding-bottom: 0;\n  margin-bottom: 20px;\n  background-color: #f0f0f0;\n\n  h2 {\n    text-align: left;\n    font-weight: bold;\n    padding: 5px 5px 5px 15px;\n    font-size: 12px;\n    margin: -7px;\n    margin-bottom: 0px;\n    background-color: #c00;\n    color: #fff;\n  }\n\n  ul li {\n    font-size: 12px;\n    list-style: square;\n  }\n}\n"
  },
  {
    "path": "app/clients/data_dog_client.rb",
    "content": "class DataDogClient\n  include HTTParty\n  base_uri 'https://api.datadoghq.com/api/v1'\n\n  def initialize(app_key, api_key)\n    @base_path = 'https://api.datadoghq.com/api/v1'\n    @app_key = app_key\n    @api_key = api_key\n    @headers = {\n      'Content-Type' => 'application/json',\n      'Accept' => 'application/json',\n    }\n  end\n\n  def get_user(email)\n    url = append_auth(\"/user/#{email}\")\n    response = self.class.get(url, headers: @headers)\n    if response.success?\n      JSON.parse(response.body)['user']\n    else\n      {}\n    end\n  end\n\n  def new_user(email)\n    url = append_auth('/user')\n    response = self.class.post(url, body: { handle: email }.to_json, headers: @headers)\n    if response.success?\n      JSON.parse(response.body)['user']\n    else\n      {}\n    end\n  end\n\n  def activate_user(email)\n    url = append_auth(\"/user/#{email}\")\n    response = self.class.put(url, body: { email: email, disabled: false }.to_json, headers: @headers)\n    if response.success?\n      JSON.parse(response.body)['user']\n    else\n      {}\n    end\n  end\n\n  def deactivate_user(email)\n    url = append_auth(\"/user/#{email}\")\n    response = self.class.put(url, body: { email: email, disabled: true }.to_json, headers: @headers)\n    if response.success?\n      JSON.parse(response.body)['user']\n    else\n      {}\n    end\n  end\n\n  private\n\n  def append_auth(str)\n    auth_str = \"api_key=#{@api_key}&application_key=#{@app_key}\"\n    str += str.include?('?') ? \"&#{auth_str}\" : \"?#{auth_str}\"\n    str\n  end\nend\n"
  },
  {
    "path": "app/controllers/admin_controller.rb",
    "content": "class AdminController < ApplicationController\n  def index; end\nend\n"
  },
  {
    "path": "app/controllers/api/v1/base_controller.rb",
    "content": "class ::Api::V1::BaseController < ActionController::Base\n  protect_from_forgery with: :null_session\n  before_action :authenticate_user_from_token!\n\n  def authenticate_user_from_token!\n    if get_token.nil? || !AccessToken.valid_token(get_token)\n      raise_unauthorized\n    end\n  end\n\n  protected\n\n  def current_user\n    access_token = AccessToken.find_token(get_token)\n    access_token.user\n  end\n\n  private\n\n  def get_token\n    if params.key?(:access_token)\n      params[:access_token]\n    elsif params.key?(:token)\n      params[:token]\n    elsif request.headers.key?(:Authorization)\n      request.headers[:Authorization].split(' ').last\n    end\n  end\n\n  def raise_unauthorized\n    head :unauthorized\n  end\nend\n"
  },
  {
    "path": "app/controllers/api/v1/endpoints_controller.rb",
    "content": "class ::Api::V1::EndpointsController < ::Api::V1::BaseController\n  before_action :authorize_user\n\n  def create\n    endpoint = Endpoint.new(endpoint_param)\n    if endpoint.save\n      render json: {\n        id: endpoint.id,\n        path: endpoint.path,\n        method: endpoint.method,\n      }\n    else\n      render json: { status: endpoint.errors }, status: :unprocessable_entity\n    end\n  end\n\n  def add_group\n    endpoint = Endpoint.find_by(id: params[:id])\n    if endpoint.nil?\n      return head :not_found\n    end\n\n    group = Group.find_by(group_param)\n    group_endpoint = GroupEndpoint.new(group: group, endpoint: endpoint)\n\n    if group_endpoint.save\n      render json: {}\n    else\n      head :unprocessable_entity\n    end\n  end\n\n  private\n\n  def authorize_user\n    unless current_user.admin?\n      head :forbidden\n    end\n  end\n\n  def group_param\n    params.require(:group).permit(:id)\n  end\n\n  def endpoint_param\n    params.require(:endpoint).permit(:path, :method)\n  end\nend\n"
  },
  {
    "path": "app/controllers/api/v1/groups_controller.rb",
    "content": "class ::Api::V1::GroupsController < ::Api::V1::BaseController\n  def create\n    if current_user.admin?\n      @group = Group.new(group_params)\n      if @group.save\n        render json: {\n          id: @group.id,\n          name: @group.name,\n        }, status: :ok\n      else\n        is_taken = @group.errors.details[:name].select { |x| x[:error] == :taken }\n\n        if !is_taken.blank?\n          existing_group = Group.find_by(name: @group.name)\n          render json: {\n            status: 'group already exist',\n            id: existing_group.id,\n            name: existing_group.name,\n          }, status: :unprocessable_entity\n        else\n          render json: {\n            status: 'error',\n          }, status: :unprocessable_entity\n        end\n      end\n    end\n  end\n\n  def add_user\n    @group = Group.find_by(id: params[:id])\n    return head :not_found unless @group.present?\n\n    return raise_unauthorized unless current_user.admin? || @group.admin?(current_user)\n\n    user = User.find_by(id: params[:user_id])\n    return head :unprocessable_entity unless user.present?\n\n    expiration_date = params[:expiration_date]\n    @group.add_user_with_expiration(params[:user_id], expiration_date)\n    head :no_content\n  end\n\n  private\n\n  def group_params\n    params.require(:group).permit(:name)\n  end\nend\n"
  },
  {
    "path": "app/controllers/api/v1/users_controller.rb",
    "content": "class ::Api::V1::UsersController < ::Api::V1::BaseController\n  before_action :set_user, only: %i[show update]\n\n  def create\n    return head :forbidden unless current_user.admin?\n\n    user = user_params\n    if User.add_temp_user user[:name], user[:email]\n      render json: { status: 'created' }, status: :ok\n    else\n      render json: { status: 'error' }, status: :unprocessable_entity\n    end\n  end\n\n  def show\n    if @user.present?\n      user_attrs = %w(\n        email id uid name active admin home_dir shell public_key user_login_id\n        product_name\n      )\n      data = @user.attributes.select { |k, _v| user_attrs.include?(k) }\n      data['groups'] = @user.groups.map { |g| { 'id' => g.gid, 'name' => g.name } }\n      render json: data\n    else\n      head :not_found\n    end\n  end\n\n  def deactivate\n    endpoint = Endpoint.find_by(path: api_v1_deactivate_user_path(':id'), method: 'PATCH')\n    if !current_user.admin? && !current_user.permitted_endpoint?(endpoint)\n      return head :forbidden\n    end\n\n    user = User.find_by(id: params[:id])\n    return head :not_found if user.nil?\n\n    user.active = false\n    if user.save\n      head :no_content\n    else\n      head :unprocessable_entity\n    end\n  end\n\n  def update\n    return raise_unauthorized unless current_user.admin? || current_user == @user\n\n    render json: { success: @user.update_profile(user_params) }\n  end\n\n  private\n\n  def set_user\n    @user = if params.key?('email')\n              User.find_by_email(params['email'])\n            elsif params.key?('uid')\n              User.find_by_uid(params['uid'])\n            elsif params.key?('username')\n              if params.key?('active')\n                is_active = [1, true, '1', 'true'].include?(params['active'])\n                User.where(user_login_id: params['username'], active: is_active).take\n              else\n                User.find_by_user_login_id(params['username'])\n              end\n            end\n  end\n\n  def user_params\n    if params.key?(:user)\n      params.require(:user).permit(:name, :email, :public_key, :product_name)\n    else\n      params.permit(:name, :email, :public_key, :product_name)\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/api/v1/vpns_controller.rb",
    "content": "class ::Api::V1::VpnsController < ::Api::V1::BaseController\n  before_action :set_vpn, only: [:assign_group]\n\n  def create\n    if current_user.admin?\n      @vpn = Vpn.new(vpn_params)\n      @vpn.uuid = SecureRandom.uuid\n      if @vpn.save\n        render json: {\n          id: @vpn.id,\n          name: @vpn.name,\n          host_name: @vpn.host_name,\n          ip_address: @vpn.ip_address,\n        }, status: :ok\n      else\n        render json: { status: 'error' }, status: :unprocessable_entity\n      end\n    end\n  end\n\n  def assign_group\n    if current_user.admin?\n      @vpn.groups.delete_all\n      @vpn.groups << Group.where(id: params[:group_id]).first\n      render json: { status: 'group assigned' }, status: :ok\n    end\n  end\n\n  private\n\n  def set_vpn\n    @vpn = Vpn.find(params[:id])\n  end\n\n  def vpn_params\n    params.require(:vpn).permit(:name, :host_name, :ip_address)\n  end\nend\n"
  },
  {
    "path": "app/controllers/api_resources_controller.rb",
    "content": "class ApiResourcesController < ApplicationController\n  before_action :set_api_resource, only: %i[show edit update destroy regenerate_access_key]\n  before_action :authenticate_user!, except: [:authenticate]\n  before_action :authorize_user, only: %i[regenerate_access_key destroy update]\n\n  # GET /api_resources\n  # GET /api_resources.json\n  def index\n    @api_resources = ApiResource.where(user: current_user) if !current_user.admin\n    @api_resources = ApiResource.all if current_user.admin\n  end\n\n  # GET /api_resources/1\n  # GET /api_resources/1.json\n  def show; end\n\n  def authenticate\n    # this authenticates and tells whether users is able to access this api or not\n    if ApiResource.authenticate(params[:access_key], params[:access_token])\n      render  json: { result: 0 }, status: :ok\n    else\n      render  json: { result: 1 }, status: 401\n    end\n  end\n\n  # GET /api_resources/new\n  def new\n    @api_resource = ApiResource.new\n  end\n\n  # GET /api_resources/1/edit\n  def edit; end\n\n  # POST /api_resources\n  # POST /api_resources.json\n  def create\n    @api_resource = ApiResource.new(api_resource_params)\n    @api_resource.access_key = ROTP::Base32.random_base32\n    @api_resource.user = current_user\n    group = Group.create name: \"#{@api_resource.name}_api_group\"\n    @api_resource.group = group\n    group.add_admin current_user\n    group.save!\n    respond_to do |format|\n      if @api_resource.save\n        format.html { redirect_to api_resource_path(@api_resource.id), notice: 'Api resource was successfully created.', flash: { access_key: @api_resource.access_key } }\n        format.json { render :show, status: :created, location: @api_resource }\n      else\n        format.html { render :new }\n        format.json { render json: @api_resource.errors, status: :unprocessable_entity }\n      end\n    end\n  end\n\n  # PATCH/PUT /api_resources/1\n  # PATCH/PUT /api_resources/1.json\n  def update\n    respond_to do |format|\n      if @api_resource.update(api_resource_params)\n        format.html { redirect_to api_resources_path, notice: 'Api resource was successfully updated.' }\n        format.json { render :show, status: :ok, location: @api_resource }\n      else\n        format.html { render :edit }\n        format.json { render json: @api_resource.errors, status: :unprocessable_entity }\n      end\n    end\n  end\n\n  # DELETE /api_resources/1\n  # DELETE /api_resources/1.json\n  def destroy\n    @api_resource.group.destroy if @api_resource.group.present?\n    @api_resource.destroy\n    respond_to do |format|\n      format.html { redirect_to api_resources_url, notice: 'Api resource was successfully destroyed.' }\n      format.json { head :no_content }\n    end\n  end\n\n  # GET /api_resources/q=.json\n  def search\n    if params[:exact]\n      @api_resources = ApiResource.where('name LIKE ?', params[:q].to_s)\n    else\n      @api_resources = ApiResource.where('name LIKE ?', \"%#{params[:q]}%\")\n    end\n    @api_resources = @api_resources.order('name ASC').limit(20)\n    data = @api_resources.map { |group| { id: group.id, name: group.name } }\n    render json: data\n  end\n\n  # GET /api_resources/:id/regenerate_access_key\n  def regenerate_access_key\n    @api_resource.access_key = ROTP::Base32.random_base32\n    respond_to do |format|\n      if @api_resource.save\n        format.html { redirect_to api_resource_path(@api_resource.id), notice: 'Access key regenerated.', flash: { access_key: @api_resource.access_key } }\n        format.json { render :show, status: :ok, location: @api_resource }\n      else\n        format.html { redirect_to api_resource_path(@api_resource.id), notice: 'Access key failed to regenerate.' }\n        format.json { render json: @api_resource.errors, status: :unprocessable_entity }\n      end\n    end\n  end\n\n  private\n\n  # Use callbacks to share common setup or constraints between actions.\n  def set_api_resource\n    @api_resource = ApiResource.find(params[:id])\n  end\n\n  # Never trust parameters from the scary internet, only allow the white list through.\n  def api_resource_params\n    params.require(:api_resource).permit(:name, :access_key, :description, :user_id, :group_id)\n  end\n\n  def authorize_user\n    unless current_user.admin? || current_user == @api_resource.user\n      respond_to do |format|\n        format.html { redirect_to api_resources_url, notice: 'Unauthorized access' }\n        format.json { render json: {}, status: :unauthorized }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/application_controller.rb",
    "content": "class ApplicationController < ActionController::Base\n  # Prevent CSRF attacks by raising an exception.\n  # For APIs, you may want to use :null_session instead.\n  protect_from_forgery with: :exception\n\n  def render_404\n    respond_to do |format|\n      format.html { render file: \"#{Rails.root}/public/404\", layout: false, status: :not_found }\n      format.xml  { head :not_found }\n      format.any  { head :not_found }\n    end\n  end\n\n  def authenticate_access_token!\n    unless AccessToken.valid_token(params[:token])\n      render_error(['Unauthorized'], :unauthorized)\n    end\n  end\n\n  def render_error(errors, status = 400)\n    render 'common/errors', locals: { errors: errors }, status: status\n  end\nend\n"
  },
  {
    "path": "app/controllers/concerns/.keep",
    "content": ""
  },
  {
    "path": "app/controllers/groups_controller.rb",
    "content": "class GroupsController < ApplicationController\n  before_action :set_group, only: %i[show edit update destroy\n                                     add_user add_machine add_vpn add_admin\n                                     remove_admin delete_user delete_vpn delete_machine]\n  before_action :authenticate_user!\n\n  def index\n    @groups = []\n    @group_search = params[:group_search]\n    if current_user.admin && @group_search.present?\n      @groups = Group.where('name LIKE ?', \"%#{@group_search}%\")\n    elsif current_user.group_admin? && !current_user.admin\n      @groups = GroupAdmin.where(user_id: current_user.id).map(&:group)\n    end\n  end\n\n  def create\n    if current_user.admin?\n      @group = Group.new(group_params)\n      respond_to do |format|\n        if @group.save\n          format.html { redirect_to group_path(@group), notice: 'Group was successfully created.' }\n          format.json { render status: :created, json: \"#{@group.name}host created\" }\n        else\n          format.html { redirect_to groups_path, notice: \"Can't save '#{group_params[:name]}'\" }\n          format.json { render status: :error, json: \"#{@group.name} not created\" }\n        end\n      end\n    else\n      format.html { redirect_to groups_path, notice: \"Can't save '#{group_params[:name]}'\" }\n      format.json { render status: :error, json: \"#{@group.name} not created\" }\n    end\n  end\n\n  def new\n    @group = Group.new\n  end\n\n  def show\n    @group_users = User.\n      select(%Q{\n        users.id AS id,\n        name,\n        email,\n        active,\n        group_associations.created_at AS join_date,\n        group_associations.expiration_date AS group_expiration_date\n      }).\n      joins('LEFT OUTER JOIN group_associations ON users.id = group_associations.user_id').\n      where('group_associations.group_id = ?', @group.id)\n  end\n\n  def delete_machine\n    @machine = HostMachine.find(params[:host_machine_id])\n    if current_user.admin?\n      @machine.groups.delete(@group)\n    end\n\n    redirect_to group_path @group\n  end\n\n  def delete_user\n    if current_user.admin? || @group.admin?(current_user)\n      @user = User.find(params[:user_id])\n\n      if @user.email.split('@').first != @group.name\n        @group.remove_user @user\n      end\n\n    end\n    redirect_to group_path(@group, anchor: 'group_members')\n  end\n\n  def add_user\n    if current_user.admin? || @group.admin?(current_user)\n      user = User.find(params[:user_id])\n      begin\n        expiration_date = expiration_date_param\n      rescue ArgumentError\n        return respond_to do |format|\n          format.html { redirect_to group_path(@group), notice: 'Expiration date is wrong' }\n        end\n      end\n      @group.add_user_with_expiration(user.id, expiration_date) if user.present?\n    end\n\n    respond_to do |format|\n      format.html do\n        redirect_to group_path(@group, anchor: 'group_members')\n      end\n    end\n  end\n\n  def add_machine\n    if current_user.admin?\n      machine = HostMachine.find(params[:machine_id])\n      machine.groups << @group if machine.present? && machine.groups.find_by_id(@group.id).blank?\n      machine.save!\n    end\n\n    respond_to do |format|\n      format.html do\n        redirect_to group_path @group\n      end\n    end\n  end\n\n  def add_admin\n    if current_user.admin?\n      GroupAdmin.find_or_create_by(group_id: @group.id, user_id: params[:user_id])\n    end\n\n    respond_to do |format|\n      format.html do\n        redirect_to group_path @group\n      end\n    end\n  end\n\n  def remove_admin\n    if current_user.admin?\n      GroupAdmin.delete(params[:group_admin_id])\n    end\n\n    respond_to do |format|\n      format.html do\n        redirect_to group_path @group\n      end\n    end\n  end\n\n  def add_vpn\n    if current_user.admin?\n      VpnGroupAssociation.find_or_create_by(group_id: @group.id, vpn_id: params[:vpn_id])\n    end\n\n    respond_to do |format|\n      format.html do\n        redirect_to group_path @group\n      end\n    end\n  end\n\n  def delete_vpn\n    return unless current_user.admin? || @group.group_admin.user == current_user\n\n    VpnGroupAssociation.where(group_id: @group.id, vpn_id: params[:vpn_id]).destroy_all\n    VpnGroupUserAssociation.where(group_id: @group.id, vpn_id: params[:vpn_id]).destroy_all\n\n    respond_to do |format|\n      format.html do\n        redirect_to group_path @group\n      end\n    end\n  end\n\n  def add_group\n    user_id = params[:id]\n    if current_user.admin?\n      begin\n        expiration_date = expiration_date_param\n      rescue ArgumentError\n        response_message = 'Expiration date is wrong'\n        return redirect_to user_path, notice: response_message\n      end\n      group = Group.find(params[:group_id])\n      group.add_user_with_expiration(user_id, expiration_date)\n    end\n    redirect_to user_path\n  end\n\n  def delete_group\n    @user = User.find(params[:user_id])\n    if current_user.admin?\n      group = Group.find(params[:id])\n\n      if @user.email.split('@').first != group.name\n        @user.groups.delete(group)\n      end\n\n    end\n\n    redirect_to user_path(@user)\n  end\n\n  def list\n    @groups = []\n    @group_search = params[:group_search]\n    return unless @group_search.present?\n\n    if current_user.admin?\n      @groups = Group.where('name LIKE ?', \"%#{@group_search}%\")\n    elsif current_user.group_admin?\n      @groups = GroupAdmin.where(user_id: current_user.id).map(&:group)\n    end\n  end\n\n  def search\n    @groups = Group.\n      where('name LIKE ?', \"%#{params[:q]}%\").\n      order('name ASC').\n      limit(20)\n    data = @groups.map { |group| { id: group.id, name: group.name } }\n    render json: data\n  end\n\n  private\n\n  # Use callbacks to share common setup or constraints between actions.\n  def set_group\n    @group = Group.find(params[:id])\n  end\n\n  # Never trust parameters from the scary internet, only allow the white list through.\n  def group_params\n    params.require(:group).permit(:name)\n  end\n\n  def expiration_date_param\n    expiration_date = params[:expiration_date]\n    return nil if expiration_date.nil? || expiration_date.empty?\n\n    Date.parse(expiration_date, '%Y-%m-%d')\n  end\nend\n"
  },
  {
    "path": "app/controllers/home_controller.rb",
    "content": "class HomeController < ApplicationController\n\n  before_action :check_signed_in\n\n  def check_signed_in\n    redirect_to profile_path if signed_in?\n  end\n\n  def index; end\nend\n"
  },
  {
    "path": "app/controllers/host_controller.rb",
    "content": "class HostController < ApplicationController\n  before_action :authenticate_user!\n  def add_host\n    @user = User.find(params[:id])\n    if current_user.admin?\n      host = Host.new\n      host.user = @user\n      host.host_pattern = params[:host_pattern]\n      host.save!\n\n    end\n    redirect_to user_path\n  end\n\n  def delete_host\n    @user = User.find(params[:user_id])\n    if current_user.admin?\n      @host = Host.find(params[:id])\n      @host.deleted_by = current_user.id\n      @host.save!\n      @host.destroy\n    end\n\n    redirect_to user_path(@user)\n  end\nend\n"
  },
  {
    "path": "app/controllers/host_machine_groups_controller.rb",
    "content": "class HostMachineGroupsController < ApplicationController\n  def show\n    @host_machines = HostMachine.all\n  end\n\n  def create\n    @host_machine = HostMachine.new(host_machine_params)\n    respond_to do |format|\n      @host_machine.save\n      format.html { redirect_to :show, notice: 'host_machine was successfully created.' }\n      format.json { render status: :created, json: \"#{@host_machine.name}host created\" }\n    end\n  end\n\n  private\n\n  # Use callbacks to share common setup or constraints between actions.\n  def set_host_machine\n    @host_machine = host_machine.find(params[:id])\n  end\n\n  # Never trust parameters from the scary internet, only allow the white list through.\n  def host_machine_params\n    params.require(:host_machine).permit(:name)\n  end\nend\n"
  },
  {
    "path": "app/controllers/host_machines_controller.rb",
    "content": "class HostMachinesController < ApplicationController\n  before_action :set_host_machine, only: %i[add_group show edit update destroy delete_group]\n  before_action :authenticate_user!\n  before_action :authorize_user, only: %i[delete_group update]\n\n  def index\n    @title = 'Host'\n    @host_machines = HostMachine.all\n    @host_machines = []\n    @host_machine_search = params[:host_machine_search]\n    if @host_machine_search.present?\n      if current_user.admin?\n        @host_machines = HostMachine.where('name LIKE ?', \"%#{@host_machine_search}%\")\n      end\n    end\n  end\n\n  def create\n    @host_machine = HostMachine.new(host_machine_params)\n    respond_to do |format|\n      if @host_machine.save\n        format.html { redirect_to host_machines_path, notice: 'Host was successfully created.' }\n        format.json { render status: :created, json: \"#{@host_machine.name}host created\" }\n      else\n        format.html { redirect_to host_machines_path, notice: \"Can't save '#{host_machine_params[:name]}'\" }\n        format.json { render status: :error, json: \"#{@host_machine.name} not created\" }\n      end\n    end\n  end\n\n  def show\n    @machine = @host_machine\n    @groups = Group.all\n  end\n\n  def update\n    @host_machine.update(default_admins: params[:host_machine][:default_admins])\n    redirect_to host_machine_path @host_machine\n  end\n\n  def add_group\n    @machine = @host_machine\n    if current_user.admin?\n      group = Group.find(params[:group_id])\n      @machine.groups << group if @machine.present? && @machine.groups.find_by_id(group.id).blank?\n      @machine.save!\n    end\n\n    respond_to do |format|\n      format.html do\n        redirect_to host_machine_path @host_machine\n      end\n    end\n  end\n\n  def delete_group\n    group = Group.find(params[:group_id])\n    @host_machine.groups.delete(group)\n    @host_machine.save!\n    redirect_to host_machine_path @host_machine\n  end\n\n  def search\n    @host_machines = HostMachine.\n      where('name LIKE ?', \"%#{params[:q]}%\").\n      order('name ASC').\n      limit(20)\n    data = @host_machines.map { |host_machine| { id: host_machine.id, name: host_machine.name } }\n    render json: data\n  end\n\n  private\n\n  # Use callbacks to share common setup or constraints between actions.\n  def set_host_machine\n    @host_machine = HostMachine.find(params[:id])\n  end\n\n  # Never trust parameters from the scary internet, only allow the white list through.\n  def host_machine_params\n    params.require(:host_machine).permit(:name)\n  end\n\n  def authorize_user\n    unless current_user.admin?\n      redirect_to host_machines_path, notice: 'Unauthorized access'\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/nss_controller.rb",
    "content": "class NssController < ApplicationController\n  skip_before_action :verify_authenticity_token, only: %i[add_host add_user_to_group]\n  before_action :authenticate_access_token!, only: %i[add_host]\n\n  def host\n    token = AccessToken.valid_token params[:token]\n    @response = nil\n    if token\n      @response = HostMachine.get_group_response(params[:name]) if params[:name].present?\n      render json: @response\n      return\n    end\n\n    host_machine = HostMachine.find_by(access_key: params[:token])\n    sysadmins = host_machine.sysadmins if host_machine.present?\n    if sysadmins.present? && sysadmins.count.positive?\n      @response = Group.get_sysadmins_and_groups sysadmins, host_machine.default_admins\n    end\n    render json: @response\n    nil\n  end\n\n  def add_host\n    if params[:name].present?\n      host = HostMachine.find_or_create_by(name: params[:name])\n      host.default_admins = params[:default_admins]\n      host.save\n      host.add_host_group(params[:name])\n      host.add_group(params[:group_name])\n      render 'add_host', locals: { host: host }, format: :json\n    else\n      errors = ['Name can\\'t be blank']\n      if params.key?(:group_name) && params[:group_name].blank?\n        errors << 'Group Name can\\'t be blank'\n      end\n      render_error(errors)\n    end\n  end\n\n  def group\n    @response = REDIS_CACHE.get(\"#{GROUP_RESPONSE}:#{params[:token]}\")\n    @response = JSON.parse(@response) if @response.present?\n    if @response.blank?\n      host_machine = HostMachine.find_by(access_key: params[:token])\n      sysadmins = host_machine.sysadmins if host_machine.present?\n      if sysadmins.present? && sysadmins.count.positive?\n        @response = Group.get_sysadmins_and_groups sysadmins, host_machine.default_admins\n        REDIS_CACHE.set(\"#{GROUP_RESPONSE}:#{params[:token]}\", @response.to_json)\n        REDIS_CACHE.expire(\"#{GROUP_RESPONSE}:#{params[:token]}\", REDIS_KEY_EXPIRY)\n      end\n    end\n\n    render json: @response\n  end\n\n  def passwd\n    @response = REDIS_CACHE.get(\"#{PASSWD_RESPONSE}:#{params[:token]}\")\n    @response = JSON.parse(@response) if @response.present?\n    if @response.blank?\n      host_machine = HostMachine.find_by(access_key: params[:token])\n      sysadmins = host_machine.sysadmins if host_machine.present?\n      if sysadmins.present? && sysadmins.count.positive?\n        @response = User.get_sysadmins sysadmins\n        REDIS_CACHE.set(\"#{PASSWD_RESPONSE}:#{params[:token]}\", @response.to_json)\n        REDIS_CACHE.expire(\"#{PASSWD_RESPONSE}:#{params[:token]}\", REDIS_KEY_EXPIRY)\n      end\n    end\n    render json: @response\n  end\n\n  def shadow\n    token = AccessToken.valid_token params[:token]\n    @response = nil\n\n    if token\n      name = params[:name]\n\n      if name.present?\n        @response = REDIS_CACHE.get(SHADOW_NAME_RESPONSE + name)\n        if @response.blank?\n          @response = User.get_shadow_name_response(name).to_json\n          REDIS_CACHE.set(SHADOW_NAME_RESPONSE + name, @response)\n          REDIS_CACHE.expire(SHADOW_NAME_RESPONSE + name, REDIS_KEY_EXPIRY)\n        end\n      else\n        @response = REDIS_CACHE.get(SHADOW_ALL_RESPONSE)\n        if @response.blank?\n          @response = User.get_all_shadow_response.to_json\n          REDIS_CACHE.set(SHADOW_ALL_RESPONSE, @response)\n          REDIS_CACHE.expire(SHADOW_ALL_RESPONSE, REDIS_KEY_EXPIRY)\n        end\n      end\n    end\n    render json: @response\n  end\n\n  def groups_list\n    token = AccessToken.valid_token params[:token]\n    if token\n      user = User.get_user(params[:email].split('@').first)\n      if user.blank?\n        render json: { success: false }\n      else\n        groups = user.blank? ? [] : user.group_names_list\n        render json: { success: true, groups: groups }\n      end\n    else\n      render json: { success: false }\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/organisations_controller.rb",
    "content": "class OrganisationsController < ApplicationController\n  before_action :authorize_user, except: [:index]\n  before_action :load_org, only: %i(\n    config_saml_app update show setup_saml save_config_saml_app\n    remove_user_saml_app add_user_saml_app\n  )\n  before_action :validate_app_name, only: %i(\n    config_saml_app save_config_saml_app\n    remove_user_saml_app add_user_saml_app\n  )\n\n  def index\n    render :index, locals: { org_list: Organisation.all }\n  end\n\n  def new\n    render :new, locals: { org: Organisation.new }\n  end\n\n  def config_saml_app\n    app_name = params[:app_name]\n    saml_app = app_name.titleize.constantize.new(@org.id)\n    config = saml_app.config\n    users = config.persisted? ? config.group.users : []\n    render :config_saml_app, locals: {\n      org: @org,\n      saml_config: config,\n      app_name: app_name,\n      users: users,\n    }\n  end\n\n  def save_config_saml_app\n    app_name = params[:app_name]\n    saml_app_config = params[:saml_app_config]\n    saml_app = app_name.titleize.constantize.new(@org.id)\n    saml_app.save_config(saml_app_config[:sso_url], params[:config])\n    flash[:success] = 'Configuration saved successfully'\n    redirect_to organisation_config_saml_app_path(\n      app_name: app_name,\n      organisation_id: @org.id\n    )\n  end\n\n  def remove_user_saml_app\n    app_name = params[:app_name]\n    saml_app = app_name.titleize.constantize.new(@org.id)\n    if saml_app.remove_user(params[:email])\n      flash[:success] = 'User removed successfullly'\n    else\n      flash[:error] = 'Issue removing the user'\n    end\n    redirect_to organisation_config_saml_app_path\n  end\n\n  def add_user_saml_app\n    app_name = params[:app_name]\n    saml_app = app_name.titleize.constantize.new(@org.id)\n    if saml_app.add_user(params[:email])\n      flash[:success] = 'User added successfullly'\n    else\n      flash[:error] = 'Issue adding the user'\n    end\n    redirect_to organisation_config_saml_app_path\n  end\n\n  def create\n    org = Organisation.setup(organisation_params.to_h || {})\n    if org.errors.blank?\n      flash[:success] = 'Successfully created organisation'\n      redirect_to organisations_path\n    else\n      flash[:errors] = org.errors.full_messages\n      render :new, locals: { org: org }\n    end\n  end\n\n  def update\n    @org.update_profile(organisation_params.to_h || {})\n    if @org.errors.blank?\n      flash[:success] = 'Successfully updated organisation'\n      redirect_to organisations_path\n    else\n      flash[:errors] = @org.errors.full_messages\n      render :show, locals: { org: @org }\n    end\n  end\n\n  def show\n    render :show, locals: { org: @org }\n  end\n\n  def setup_saml\n    if @org.saml_setup?\n      flash[:errors] = 'SAML Certificates Already Setup'\n    else\n      @org.setup_saml_certs\n      flash[:success] = 'Successfully setup SAML Certificates'\n    end\n    redirect_to organisations_path\n  end\n\n  private\n\n  def authorize_user\n    unless current_user.admin?\n      respond_to do |format|\n        format.html { redirect_to organisations_path, notice: 'Unauthorized access' }\n        format.json { render json: {}, status: :unauthorized }\n      end\n    end\n  end\n\n  def load_org\n    id = params[:id] || params[:organisation_id]\n    @org = Organisation.where(id: id).first\n    if @org.blank?\n      redirect_to organisations_path\n    end\n  end\n\n  def validate_app_name\n    saml_apps = ENV['SAML_APPS'].split(',').map(&:downcase)\n    unless saml_apps.include?(params[:app_name].downcase)\n      redirect_to organisation_path(id: params[:organisation_id])\n    end\n  end\n\n  def organisation_params\n    params.require(:organisation).permit(\n      :name, :website, :domain, :country, :state, :address, :admin_email_address,\n      :slug, :unit_name\n    )\n  end\nend\n"
  },
  {
    "path": "app/controllers/pings_controller.rb",
    "content": "class PingsController < ApplicationController\n  def show\n    render plain: 'pong'\n  end\nend\n"
  },
  {
    "path": "app/controllers/profile_controller.rb",
    "content": "class ProfileController < ApplicationController\n  require 'vpn/mobileconfig'\n\n  skip_before_action :verify_authenticity_token, if: Proc.new { |c| c.request.format == 'application/json' }\n  before_action :authenticate_user!, except: %i[user_id verify authenticate authenticate_cas authenticate_ms_chap authenticate_pam public_key]\n\n  def regen_auth\n    current_user.generate_two_factor_auth(true)\n    redirect_to profile_path\n  end\n\n  def show\n    @token_qr = nil\n    unless current_user.provisioning_uri.blank?\n      @token_qr = RQRCode::QRCode.new(current_user.provisioning_uri, size: 10, level: :h)\n    end\n  end\n\n  def user_admin\n    @users = []\n    @groups = []\n    if !current_user.admin?\n      redirect_to profile_path\n    end\n\n    @user_search = params[:user_search]\n    if @user_search.present?\n      @users = User.where('name LIKE ? OR email LIKE ?', \"%#{@user_search}%\", \"%#{@user_search}%\").\n        take(5)\n      redirect_to profile_list_path(user_search: params[:user_search]) if @users.count.positive?\n    end\n\n    @group_search = params[:group_search]\n    if @group_search.present?\n      @groups = Group.where('name LIKE ?', \"%#{@group_search}%\").take(5)\n      redirect_to group_list_path(group_search: params[:group_search]) if @groups.count.positive?\n    end\n  end\n\n  def group_admin\n    @users = []\n    @groups = []\n    if !current_user.admin? && !current_user.group_admin?\n      redirect_to profile_path\n    end\n\n    @user_search = params[:user_search]\n    if @user_search.present?\n      @users = User.where('name LIKE ? OR email LIKE ?', \"%#{@user_search}%\", \"%#{@user_search}%\").\n        take(5)\n      redirect_to profile_list_path(user_search: params[:user_search]) if @users.count.positive?\n    end\n\n    @group_search = params[:group_search]\n    if @group_search.present?\n      @groups = Group.where('name LIKE ?', \"%#{@group_search}%\").take(5)\n      redirect_to group_list_path(group_search: params[:group_search]) if @groups.count >= 0\n    end\n  end\n\n  def user_id\n    token = AccessToken.valid_token params[:token]\n    response = 0\n    if token\n      user = User.get_user(params[:name])\n      response = user.uid if user.present?\n    end\n    render plain: response\n  end\n\n  def download_vpn\n    if !Pathname.new(\"/opt/vpnkeys/#{current_user.email}.tar.gz\").exist?\n      `cd /etc/openvpn/easy-rsa/ && bash /etc/openvpn/easy-rsa/gen-client-keys #{current_user.email}`\n    else\n      `cd /etc/openvpn/easy-rsa/ && bash /etc/openvpn/easy-rsa/gen-client-conf #{current_user.email}`\n    end\n    send_file(\n      \"/opt/vpnkeys/#{current_user.email}.tar.gz\",\n      type: 'application/zip',\n      disposition: \"attachment; filename=#{current_user.email}.tar.gz\"\n    )\n  end\n\n  def download_vpn_for_ios_and_mac\n    mobileconfig = Mobileconfig.new\n    vpns = Vpn.user_vpns current_user\n\n    return render plain: \"you don't have access to any vpns\" unless vpns.present?\n\n    mobileconfig_data = mobileconfig.generate(vpns, current_user)\n\n    send_data(\n      mobileconfig_data,\n      filename: \"#{current_user.email}.mobileconfig\",\n      type: 'application/x-apple-aspen-config'\n    )\n  end\n\n  def download_vpn_for_user\n    render plain: 'Please download vpn config from your homepage'\n  end\n\n  def authenticate\n    response = User.authenticate params\n    if response\n      render plain: 0\n    else\n      render plain: 1\n    end\n  end\n\n  def authenticate_ms_chap\n    response = User.ms_chap_auth params\n    render plain: response\n  end\n\n  def authenticate_cas\n    username = User.authenticate_cas request.env['HTTP_AUTHORIZATION']\n\n    ## cas-5.2.x expects {\"@c\":\".SimplePrincipal\",\"id\":\"casuser\",\"attributes\":{}}\n    response_map = {\n      '@class': 'org.apereo.cas.authentication.principal.SimplePrincipal',\n      'id': username,\n      'attributes': { 'backend': 'gate-sso' },\n    }\n\n    if username.present?\n      render json: response_map, status: :ok\n    else\n      response_map['attributes'] = nil\n      render json: response_map, status: 401\n    end\n  end\n\n  def authenticate_pam\n    response = User.authenticate_pam params\n    if response\n      render plain: 0\n    else\n      render plain: 1\n    end\n  end\n\n  def verify\n    token = AccessToken.valid_token params[:token]\n    if token\n      response = User.verify params\n      if response\n        render plain: 0\n      else\n        render plain: 1\n      end\n    else\n      render plain: 1\n    end\n  end\n\n  def list\n    @users = []\n    @user_search = params[:user_search]\n    if @user_search.present?\n      @users = User.where('name LIKE ? OR email LIKE ?', \"%#{@user_search}%\", \"%#{@user_search}%\").\n        take(5)\n    end\n  end\n\n  def admin\n    @users = []\n    @groups = []\n    if !current_user.admin?\n      redirect_to profile_path\n    end\n\n    @user_search = params[:user_search]\n    if @user_search.present?\n      @users = User.where('name LIKE ? OR email LIKE ?', \"%#{@user_search}%\", \"%#{@user_search}%\").\n        take(5)\n      redirect_to profile_list_path(user_search: params[:user_search]) if @users.count.positive?\n    end\n\n    @group_search = params[:group_search]\n    if @group_search.present?\n      @groups = Group.where('name LIKE ?', \"%#{@group_search}%\").take(5)\n      redirect_to group_list_path(group_search: params[:group_search]) if @groups.count.positive?\n    end\n  end\n\n  def update\n    if current_user.admin?\n      @user = User.find(params[:id])\n      @user.update(admin_active)\n    end\n    redirect_to user_path\n  end\n\n  def user_edit; end\n\n  def public_key_update\n    @user = User.where(id: params[:id]).first\n    if current_user.admin? || current_user.id == @user.id\n      @user.public_key = params[:public_key]\n      @user.save!\n    end\n    redirect_to user_path\n  end\n\n  def public_key\n    public_key = ''\n    @user = User.get_user(params[:name])\n    public_key = @user.public_key if @user.present?\n    render plain: public_key\n  end\n\n  def user\n    @group = Group.all\n    @user = User.where(id: params[:id]).first\n\n    if !current_user.admin? && current_user.id != @user.id\n      redirect_to profile_path\n    end\n    render_404 if @user.blank?\n    if @user.present?\n      # hack add blank text to public_key\n      @user.public_key = 'Add public key' if @user.public_key.blank?\n      respond_to do |format|\n        format.html\n      end\n    end\n  end\n\n  protected\n\n  def admin_active\n    params.require(:user).permit(:active, :admin)\n  end\nend\n"
  },
  {
    "path": "app/controllers/saml_idp_controller.rb",
    "content": "#TODO rename back to SAMLController\n\n#class SamlIdpController < SamlIdp::IdpController\nclass SamlIdpController < ApplicationController\n  layout false\n  before_action :setup_saml_configuration\n\n  def show\n    xml_content = SamlIdp.metadata.signed\n    if params.key?(:download)\n      send_data xml_content,\n        type: 'text/xml',\n        filename: 'metadata.xml'\n    else\n      render xml: xml_content\n    end\n  end\n\n  private\n\n  def idp_authenticate(email, password)\n    user = User.find_and_validate_saml_user(email, password, params[:app])\n    user.present? ? user : nil\n  end\n\n  def idp_make_saml_response(found_user)\n    encode_response found_user\n  end\n\n  def idp_logout\n    # user = User.by_email(saml_request.name_id)\n    # user.logout\n  end\n\n  def setup_saml_configuration\n    slug = params[:slug]\n    app = params[:app]\n    org = Organisation.find_by_slug(slug)\n    saml_url = \"#{ENV['GATE_SERVER_URL']}/#{slug}/#{app}/saml\"\n    SamlIdp.configure do |config|\n      config.x509_certificate = org.cert_key\n      config.secret_key = org.cert_private_key\n      config.organization_name = org.name\n      config.organization_url = org.website\n      config.base_saml_location = saml_url\n      config.session_expiry = 86400\n      config.name_id.formats = {\n        email_address: ->(principal) { principal.email },\n        transient: ->(principal) { principal.user_login_id },\n        persistent: ->(principal) { principal.user_login_id },\n        name: ->(principal) { principal.name },\n      }\n      config.attributes = {\n        'eduPersonPrincipalName' => {\n          'name' => 'urn:oid:1.3.6.1.4.1.5923.1.1.1.6',\n          'name_format' => 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',\n          'getter' => ->(principal) { principal.email },\n        },\n      }\n      config.attribute_service_location = \"#{saml_url}/attributes\"\n      config.single_service_post_location = \"#{saml_url}/auth\"\n      config.single_logout_service_post_location = \"#{saml_url}/logout\"\n      config.single_logout_service_redirect_location = \"#{saml_url}/logout\"\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/users/auth_controller.rb",
    "content": "class Users::AuthController < ApplicationController\n\n  def log_in\n    unless ENV['SIGN_IN_TYPE'] == 'form'\n      return redirect_to root_path\n    end\n\n    email = params.require(:email)\n    name = params.require(:name)\n\n    unless User.valid_domain? email.split('@').last\n      return render plain: 'Your domain is unauthorized', status: :unauthorized\n    end\n\n    user = User.create_user(name, email)\n    user.generate_two_factor_auth\n    sign_in_and_redirect user, event: :authentication\n  end\nend\n"
  },
  {
    "path": "app/controllers/users/omniauth_callbacks_controller.rb",
    "content": "class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController\n  def google_oauth2\n    # You need to implement the method below in your model (e.g. app/models/user.rb)\n    #\n    data = request.env['omniauth.auth']\n    domain = data['info']['email'].split('@').last\n\n    unless User.valid_domain? domain\n      return render plain: 'Your domain is unauthorized', status: :unauthorized\n    end\n\n    @user = User.create_user(data.info['name'], data.info['email'])\n\n    if @user.persisted?\n      @user.generate_two_factor_auth\n      sign_in_and_redirect @user, event: :authentication\n    else\n      session['devise.google_data'] = request.env['omniauth.auth']\n      redirect_to new_user_registration_url\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/users_controller.rb",
    "content": "class UsersController < ApplicationController\n  before_action :authenticate_user!, except: %i[user_id verify authenticate authenticate_cas authenticate_ms_chap authenticate_pam public_key]\n  before_action :authorize_user, only: %i[create update]\n\n  def index\n    @user_search = params[:user_search]\n    @users = []\n    @users = User.where('name like ?', \"%#{@user_search}%\").take(20) if @user_search.present?\n  end\n\n  def show\n    @user = User.where(id: params[:id]).first\n    return render_404 if @user.blank?\n\n    @user_groups = Group.\n      select(%{\n        groups.id AS id,\n        gid,\n        name,\n        deleted_at,\n        group_associations.expiration_date AS group_expiration_date\n      }).\n      joins('INNER JOIN group_associations ON groups.id = group_associations.group_id').\n      where('group_associations.user_id = ?', @user.id)\n\n    if @user.access_token.blank?\n      access_token = AccessToken.new\n      access_token.token = ROTP::Base32.random_base32\n      access_token.user = @user\n      access_token.save!\n    end\n\n    @vpns = Vpn.user_vpns @user\n\n    return unless current_user.admin? || current_user == @user\n\n    return unless @user.present? && (current_user.admin? || current_user.id == @user.id)\n\n    respond_to do |format|\n      format.html { render :show, flash: { token: access_token.try(:token) } }\n    end\n  end\n\n  def new\n    return redirect_to profile_path unless current_user.admin?\n\n    render :new, locals: {\n      roles: ENV['USER_ROLES'].split(','),\n      domains: ENV['GATE_HOSTED_DOMAINS'].split(','),\n    }\n  end\n\n  def create\n    user = User.add_user(\n      user_params[:first_name],\n        user_params[:last_name],\n        user_params[:user_role],\n        params[:user_domain]\n      )\n    if user.errors.present?\n      flash[:errors] = user.errors.full_messages\n      redirect_to(new_user_path)\n    else\n      flash[:success] = 'Successfully Created User'\n      redirect_to user_path(id: user.id)\n    end\n  end\n\n  def update\n    @user = User.find(params[:id])\n    begin\n      @user.update(product_name: product_name)\n      response_message = 'product name updated successfully!!'\n    rescue ActionController::ParameterMissing\n      response_message = 'Params are missing'\n    end\n\n    form_response(response_message)\n  end\n\n  def search\n    @users = User.\n      where('name LIKE :q OR email LIKE :q', q: \"%#{params[:q]}%\").\n      order('name ASC').\n      limit(20)\n    unless params[:include_inactive] == 'true'\n      @users = @users.where(active: true)\n    end\n    data = @users.map do |user|\n      {\n        id: user.id,\n        name: user.name,\n        email: user.email,\n        name_email: \"#{user.name} - #{user.email}\",\n      }\n    end\n    render json: data\n  end\n\n  # GET /users/:id/regenerate_token\n  def regenerate_token\n    @user = User.find(params[:id])\n    unless current_user.admin? || (current_user.id == @user.id)\n      return respond_to do |format|\n        format.html { redirect_to user_path(@user.id), notice: 'You cannot regenerate this token.' }\n        format.json { render json: @user.errors, status: :unauthorized }\n      end\n    end\n\n    @access_token = @user.access_token\n    @access_token.token = ROTP::Base32.random_base32\n    respond_to do |format|\n      if @access_token.save\n        format.html { redirect_to user_path(@user.id), notice: 'Token regenerated.', flash: { token: @access_token.token } }\n        format.json { render :show, status: :ok, location: @user }\n      else\n        format.html { redirect_to user_path(@user.id), notice: 'Token failed to regenerate.' }\n        format.json { render json: @user.errors, status: :unprocessable_entity }\n      end\n    end\n  end\n\n  private\n\n  def user_params\n    params.require(:user).permit(\n      :first_name, :last_name, :user_role\n    )\n  end\n\n  def form_response(message)\n    respond_to do |format|\n      format.html { redirect_to user_path, notice: message }\n    end\n  end\n\n  def product_name\n    params.require(:product_name)\n  end\n\n  def authorize_user\n    unless current_user.admin?\n      flash[:errors] = 'unauthorized access'\n      redirect_to profile_path\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/vpn_domain_name_servers_controller.rb",
    "content": "class VpnsController < ApplicationController\n  before_action :authorize_user\n  before_action :set_vpn, only: %i[show edit update destroy user_associated_groups]\n\n  def destroy; end\nend\n\n#TODO fix this in future. This is something not great.\nclass VpnDomainNameServersController < ApplicationController\nend\n"
  },
  {
    "path": "app/controllers/vpns_controller.rb",
    "content": "class VpnsController < ApplicationController\n  before_action :authorize_user, except: %i[create_group_associated_users show index\n                                            user_associated_groups group_associated_users search]\n  before_action :set_vpn, only: %i[show edit update destroy\n                                   user_associated_groups add_dns_server remove_dns_server\n                                   add_search_domain remove_search_domain\n                                   add_supplemental_match_domain remove_supplemental_match_domain\n                                   migrate_to_new_group assign_group]\n\n  before_action :authenticate_user!\n\n  require 'securerandom'\n\n  def index\n    @vpns = Vpn.order(:name)\n  end\n\n  def update\n    if current_user.admin?\n      @vpn = Vpn.find(params[:id])\n      if @vpn.update(vpn_params)\n        redirect_to vpn_path(@vpn), notice: 'Vpn was successfully updated.'\n      end\n    else\n      redirect_to vpn_path(@vpn), notice: 'You can not update, not sufficient privileges.'\n    end\n  end\n\n  def create\n    @vpn = Vpn.new(vpn_params)\n    @vpn.uuid = SecureRandom.uuid\n    respond_to do |format|\n      if @vpn.save\n        format.html { redirect_to vpns_path, notice: 'Vpn was successfully added.' }\n        format.json { render status: :created, json: \"#{@vpn.name}host created\" }\n      else\n        format.html { redirect_to vpns_path, notice: \"Can't save '#{vpn_params[:name]}'\" }\n        format.json { render status: :error, json: \"#{@vpn.name} not created\" }\n      end\n    end\n  end\n\n  def new\n    @vpn = Vpn.new\n  end\n\n  def add_dns_server\n    if current_user.admin? && params[:server_address].present?\n      VpnDomainNameServer.find_or_create_by(server_address: params[:server_address], vpn: @vpn)\n    end\n    redirect_to vpn_path(@vpn, anchor: 'dns_hosts')\n  end\n\n  def add_search_domain\n    if current_user.admin? && params[:search_domain].present?\n      VpnSearchDomain.find_or_create_by(search_domain: params[:search_domain], vpn: @vpn)\n    end\n    redirect_to vpn_path(@vpn, anchor: 'search_domains')\n  end\n\n  def add_supplemental_match_domain\n    if current_user.admin? && params[:supplemental_match_domain].present?\n      VpnSupplementalMatchDomain.find_or_create_by(\n        supplemental_match_domain: params[:supplemental_match_domain],\n        vpn: @vpn\n      )\n    end\n    redirect_to vpn_path(@vpn, anchor: 'match_domains')\n  end\n\n  def remove_dns_server\n    if current_user.admin?\n      VpnDomainNameServer.delete(params[:vpn_domain_name_server_id])\n    end\n    redirect_to vpn_path(@vpn, anchor: 'dns_hosts')\n  end\n\n  def remove_search_domain\n    if current_user.admin?\n      VpnSearchDomain.delete(params[:vpn_search_domain_id])\n    end\n    redirect_to vpn_path(@vpn, anchor: 'search_domains')\n  end\n\n  def remove_supplemental_match_domain\n    if current_user.admin?\n      VpnSupplementalMatchDomain.delete(params[:vpn_supplemental_match_domain_id])\n    end\n    redirect_to vpn_path(@vpn, anchor: 'match_domains')\n  end\n\n  def assign_group\n    if current_user.admin?\n      @vpn.groups.delete_all\n      @vpn.groups << Group.where(id: params[:group_id]).first\n    end\n    redirect_to vpn_path(@vpn, anchor: 'match_domains')\n  end\n\n  def show\n    @vpn = Vpn.find(params[:id])\n    @groups = Group.order(:name)\n  end\n\n  def user_associated_groups\n    @groups_under_current_user = []\n    @group_id = params[:group_id]\n    @vpn_id = params[:id]\n    if current_user.admin?\n      @groups_under_current_user = Group.all\n    else\n      @vpn.groups.each do |vpn_group|\n        if vpn_group.group_admin.try(:user) == current_user\n          @groups_under_current_user << vpn_group\n        end\n      end\n    end\n\n    render 'show'\n  end\n\n  def group_associated_users\n    @group = Group.find(params[:group_id])\n    if current_user.admin? || @group.group_admin.try(:user) == current_user\n      @users = @group.users\n      @vpn_group_user_associations = VpnGroupUserAssociation.where(\n        vpn_id: params[:vpn_id],\n        group_id: params[:group_id]\n      )\n      @vpn_enabled_users = @vpn_group_user_associations.map(&:user)\n\n      @vpn_disabled_users = @users - @vpn_enabled_users\n\n      @vpn_enabled_users = @vpn_enabled_users.sort_by(&:email)\n      @vpn_disabled_users = @vpn_disabled_users.sort_by(&:email)\n    end\n    respond_to do |format|\n      format.json { render status: :ok, json: { enabled: @vpn_enabled_users, disabled: @vpn_disabled_users } }\n    end\n  end\n\n  def create_group_associated_users\n    if current_user.admin? ||\n        VpnGroupAssociation.find_by_vpn_id_and_group_id(\n          params[:vpn_id].to_i,\n          params[:group_id]\n        ).group.group_admin.user == current_user\n      @users_selected = params[:users] || []\n      @associations_made = []\n      VpnGroupUserAssociation.where(\n        vpn_id: params[:vpn_id].to_i,\n        group_id: params[:group_id].to_i\n      ).each do |vpn_association|\n        unless @users_selected.include? vpn_association.user.id\n          vpn_association.destroy\n        end\n      end\n      @users_selected.each do |user|\n        @associations_made << VpnGroupUserAssociation.find_or_create_by(\n          vpn_id: params[:vpn_id],\n          group_id: params[:group_id],\n          user_id: user.to_i\n        )\n      end\n\n      respond_to do |format|\n        format.json { render status: :ok, json: @associations_made }\n      end\n    else\n      respond_to do |format|\n        format.json { render status: :unauthorized, json: 'not gonna happen' }\n      end\n    end\n  end\n\n  def destroy\n    VpnGroupUserAssociation.where(vpn_id: params[:id]).destroy_all\n    VpnGroupAssociation.where(vpn_id: params[:id]).destroy_all\n    Vpn.destroy(params[:id])\n\n    respond_to do |format|\n      format.html { redirect_to vpns_path, notice: 'Vpn was successfully destroyed.' }\n      format.json { render status: :ok, json: 'vpn destroyed' }\n    end\n  end\n\n  def search\n    @vpns = Vpn.\n      where('name LIKE ?', \"%#{params[:q]}%\").\n      order('name ASC').\n      limit(20)\n    data = @vpns.map { |vpn| { id: vpn.id, name: vpn.name } }\n    render json: data\n  end\n\n  private\n\n  def set_vpn\n    @vpn = Vpn.find(params[:id])\n  end\n\n  def vpn_params\n    params.require(:vpn).permit(:name, :host_name, :ip_address)\n  end\n\n  def authorize_user\n    unless current_user.admin?\n      redirect_to profile_path\n    end\n  end\nend\n"
  },
  {
    "path": "app/helpers/admin_helper.rb",
    "content": "module AdminHelper\nend\n"
  },
  {
    "path": "app/helpers/api_resources_helper.rb",
    "content": "module ApiResourcesHelper\nend\n"
  },
  {
    "path": "app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\n  def add_placeholder_to_list(list, placeholder, string_convert: 'titleize')\n    (list.map do |row|\n      name = string_convert.present? ? row.send(string_convert.to_sym) : row\n      [name, row]\n    end).insert(0, [placeholder, ''])\n  end\nend\n"
  },
  {
    "path": "app/helpers/group_helper.rb",
    "content": "module GroupHelper\nend\n"
  },
  {
    "path": "app/helpers/groups_helper.rb",
    "content": "module GroupsHelper\nend\n"
  },
  {
    "path": "app/helpers/home_helper.rb",
    "content": "module HomeHelper\nend\n"
  },
  {
    "path": "app/helpers/host_access_groups_helper.rb",
    "content": "module HostAccessGroupsHelper\nend\n"
  },
  {
    "path": "app/helpers/host_machine_groups_helper.rb",
    "content": "module HostMachineGroupsHelper\nend\n"
  },
  {
    "path": "app/helpers/host_machines_helper.rb",
    "content": "module HostMachinesHelper\nend\n"
  },
  {
    "path": "app/helpers/nss_helper.rb",
    "content": "module NssHelper\nend\n"
  },
  {
    "path": "app/helpers/omniauth_callbacks_helper.rb",
    "content": "module OmniauthCallbacksHelper\nend\n"
  },
  {
    "path": "app/helpers/profile_helper.rb",
    "content": "module ProfileHelper\nend\n"
  },
  {
    "path": "app/helpers/users_helper.rb",
    "content": "module UsersHelper\nend\n"
  },
  {
    "path": "app/lib/datadog.rb",
    "content": "class Datadog < SamlApp\n\n  def initialize(org_id)\n    @app_name = 'datadog'\n    super(org_id)\n    if @config.persisted?\n      @client = DataDogClient.new(\n        @config.config['app_key'],\n        @config.config['api_key']\n      )\n    else\n      @config.config = { app_key: '', api_key: '' }\n    end\n  end\n\n  def save_config(sso_url, config = {})\n    @config.config = @config.config.merge(config)\n    super(sso_url, config)\n  end\n\n  def add_user(email)\n    user_detail_response = @client.get_user(email)\n    response = if user_detail_response.eql?({})\n                 @client.new_user(email)\n               else\n                 @client.activate_user(email)\n               end\n    super(email) unless response.eql?({})\n  end\n\n  def remove_user(email)\n    response = @client.deactivate_user(email)\n    super(email) unless response.eql?({})\n  end\nend\n"
  },
  {
    "path": "app/lib/saml_app.rb",
    "content": "class SamlApp\n\n  attr_accessor :config, :app_name\n\n  def initialize(org_id)\n    @config = SamlAppConfig.find_or_initialize_by(\n      app_name: @app_name, organisation_id: org_id\n    )\n  end\n\n  def save_config(sso_url, config = {})\n    unless @config.persisted?\n      group_name = \"#{@config.organisation.slug}_saml_#{app_name}_users\"\n      @config.group = Group.find_or_create_by(name: group_name)\n    end\n    @config.sso_url = sso_url\n    @config.save\n  end\n\n  def add_user(email)\n    user = User.where(email: email).first\n    unless user.blank?\n      @config.group.add_user(user.id)\n      return true\n    end\n    false\n  end\n\n  def remove_user(email)\n    user = User.where(email: email).first\n    unless user.blank?\n      @config.group.remove_user(user.id)\n      return true\n    end\n    false\n  end\nend\n"
  },
  {
    "path": "app/mailers/.keep",
    "content": ""
  },
  {
    "path": "app/models/.keep",
    "content": ""
  },
  {
    "path": "app/models/access_token.rb",
    "content": "class AccessToken < ApplicationRecord\n  attr_accessor :token\n\n\n  belongs_to :user\n\n  before_save :hash_token!\n\n  def self.find_token challenge_token\n    AccessToken.where(hashed_token: Digest::SHA512.hexdigest(challenge_token)).first\n  end\n\n  def self.valid_token challenge_token\n    find_token(challenge_token).present?\n  end\n\n  private\n\n  def hash_token!\n    self.hashed_token = Digest::SHA512.hexdigest self.token unless self.token.blank?\n  end\nend\n"
  },
  {
    "path": "app/models/api_resource.rb",
    "content": "class ApiResource < ApplicationRecord\n  attr_accessor :access_key\n\n  validates :name, format: { with: /\\A[a-zA-Z0-9_-]+\\Z/ }, uniqueness: true, presence: true\n  validates :access_key, presence: true, on: :create\n  belongs_to :user\n  belongs_to :group\n\n  before_save :hash_access_key!\n\n  def self.authenticate access_key, access_token\n    api_resource = ApiResource.find_by(hashed_access_key: Digest::SHA512.hexdigest(access_key))\n    user = AccessToken.find_by(hashed_token: Digest::SHA512.hexdigest(access_token)).user\n    api_resource.group.member? user\n  end\n\n  private\n\n  def hash_access_key!\n    self.hashed_access_key = Digest::SHA512.hexdigest self.access_key unless self.access_key.blank?\n  end\nend\n"
  },
  {
    "path": "app/models/application_record.rb",
    "content": "class ApplicationRecord < ActiveRecord::Base\n  self.abstract_class = true\nend\n"
  },
  {
    "path": "app/models/concerns/.keep",
    "content": ""
  },
  {
    "path": "app/models/concerns/ms_chap_auth.rb",
    "content": "module MsChapAuth\n\n  def hexlify(msg)\n    msg.unpack('H*').first\n  end\n\n  def unhexlify(msg)\n    msg.scan(/../).collect { |c| c.to_i(16).chr }.join\n  end\n\n  def test_key key, challenge\n    des = OpenSSL::Cipher::DES.new('ECB')\n    des.encrypt\n    des.key = key\n    des.update challenge\n    #need to do it twice, don't know why!!! :-(\n    des.update challenge\n    end\n\n    def ntlm_challenge_response word, challenge\n\n      uword = word.encode('iso-8859-1').encode('utf-16le')\n      ntlmhash = md4_hash uword\n\n      response = []\n\n      response.push(test_key(key56_to_key64(ntlmhash[0...14]), challenge))\n      response.push(test_key(key56_to_key64(ntlmhash[14...28]), challenge))\n      response.push(test_key(key56_to_key64(ntlmhash[28...ntlmhash.length] + '0000000000'), challenge))\n      hexlify(response[0]) + hexlify(response[1]) + hexlify(response[2])\n    end\n\n\n    def md4_hash word\n      md4 = OpenSSL::Digest::MD4.new\n      return md4.hexdigest(word)\n    end\n\n    def set_key_odd_parity key\n      for pos in 0..key.length - 1\n        for k in 0..6\n          bit = 0\n          t = key[pos] >> k\n          bit = (t ^ bit) & 0x1\n        end\n        key[pos] = (key[pos] & 0xFE) | bit\n      end\n      return key\n    end\n\n    def key56_to_key64 key_raw\n      key_raw = unhexlify(key_raw)\n      key_56 = []\n      key_raw.split(\"\").each {|c| key_56.push(c.ord)}\n\n      key = []\n      (0..7).to_a.each {|i| key.push(0)}\n\n      key[0] = key_56[0]\n      key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);\n      key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);\n      key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);\n      key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);\n      key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);\n      key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);\n      key[7] =  (key_56[6] << 1) & 0xFF;\n\n      key = set_key_odd_parity(key)\n\n      keyout = ''\n      key.each {|k| keyout += k.chr}\n      return keyout\n\n    end\n\n    def nt_password_hash password\n      md4 = OpenSSL::Digest::MD4.new\n      md4.digest(password)\n    end\n\n    def get_nt_key password\n      unicode_pwd = password.encode('iso-8859-1').encode('utf-16le')\n      pwd_hash = nt_password_hash(unicode_pwd)\n      nt_key   = nt_password_hash(pwd_hash)\n      return hexlify(nt_key)\n    end\n\n    def authenticate_ms_chap password, challenge, response\n      if ntlm_challenge_response(password, unhexlify(challenge)) == response\n        return \"NT_KEY: \" + get_nt_key(password).upcase\n      end\n      return (\"NT_STATUS_UNSUCCESSFUL: Failure (0xC0000001)\")\n    end\n\n    def authenticate_ms_chap_with_drift passwords, challenge, response\n      passwords.each do |password|\n        if ntlm_challenge_response(password, unhexlify(challenge)) == response\n          return \"NT_KEY: \" + get_nt_key(password).upcase\n        end\n      end\n      return (\"NT_STATUS_UNSUCCESSFUL: Failure (0xC0000001)\")\n    end\n\n  end\n\n\n"
  },
  {
    "path": "app/models/endpoint.rb",
    "content": "class Endpoint < ApplicationRecord\n\n\n  has_many :group_endpoints\n  has_many :groups, through: :group_endpoints\n\n  validates_presence_of :path\n  validates_presence_of :method\n  validates :method, inclusion: { in: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] }\n  validates_format_of :path, with: /\\A((\\/(([0-9, a-z,\\-,_]+)|(:[a-z]+))+)+|\\/)\\Z/i\nend\n"
  },
  {
    "path": "app/models/group.rb",
    "content": "class Group < ApplicationRecord\n\n  has_many :group_admins, dependent: :destroy\n  has_many :group_associations\n  has_many :users, through: :group_associations\n\n  has_many :vpn_group_associations\n  has_many :vpns, through: :vpn_group_associations\n\n  has_many :host_access_groups\n  has_many :host_machines, through: :host_access_groups\n  belongs_to :vpn\n\n  has_many :group_endpoints\n  has_many :endpoints, through: :group_endpoints\n\n  validates_uniqueness_of :name, case_sensitive: false\n  validates :name, presence: true\n\n  before_create :set_lower_case_name\n  acts_as_paranoid\n\n  after_create :add_gid\n\n  GID_CONSTANT = 9000\n\n  def burst_host_cache\n    if host_machines.count.positive?\n      host_machines.each do |host|\n        if host.access_key.present?\n          REDIS_CACHE.del \"#{GROUP_RESPONSE}:#{host.access_key}\"\n          REDIS_CACHE.del \"#{PASSWD_RESPONSE}:#{host.access_key}\"\n          Rails.logger.info \"hello #{host.name} #{host.access_key}\"\n        end\n      end\n    end\n  end\n\n  def add_admin(user)\n    GroupAdmin.find_or_create_by(group_id: id, user: user)\n  end\n\n  def set_lower_case_name\n    self.name = name.downcase\n  end\n\n  def add_gid\n    self.gid = id + GID_CONSTANT\n    save!\n  end\n\n  def self.get_name_response(name)\n    response = REDIS_CACHE.get(GROUP_NSS_RESPONSE + name)\n    if response.blank?\n      group = Group.where(name: name).first\n      group = [] if group.blank?\n      response = group.group_response.to_json\n      REDIS_CACHE.set(GROUP_NSS_RESPONSE + name, response)\n      REDIS_CACHE.expire(GROUP_NSS_RESPONSE + name, REDIS_KEY_EXPIRY)\n    end\n    JSON.parse(response, symbolize_names: true)\n  end\n\n  def self.get_all_response\n    response = REDIS_CACHE.get(GROUP_ALL_RESPONSE)\n    if response.blank?\n      response_array = []\n      Group.all.includes(:users).each do |group|\n        response_array << group.group_response\n      end\n      response = response_array.to_json\n      REDIS_CACHE.set(GROUP_ALL_RESPONSE, response)\n      REDIS_CACHE.expire(GROUP_ALL_RESPONSE, REDIS_KEY_EXPIRY)\n    end\n    response\n  end\n\n  def self.get_gid_response(gid)\n    group = Group.where(gid: gid).first\n    return [] if group.blank?\n\n    group.group_response\n  end\n\n  def admin?(user)\n    GroupAdmin.where(group_id: self, user_id: user).first.present?\n  end\n\n  def member?(user)\n    users.exists? user.id\n  end\n\n  def self.generate_group_response(name, gid, members)\n    {\n      gr_name: name,\n      gr_passwd: 'x',\n      gr_gid: gid,\n      gr_mem: members,\n    }\n  end\n\n  def group_response\n    Group.group_nss_response name\n  end\n\n  def self.group_nss_response(name)\n    group_response = REDIS_CACHE.get(\"#{GROUP_NSS_RESPONSE}:#{name}\")\n    group_response = JSON.parse(group_response) if group_response.present?\n\n    if group_response.blank?\n      group = Group.find_by(name: name)\n      if group.present?\n        members = group.users.map(&:user_login_id)\n        response_hash = Group.generate_group_response(group.name, group.gid, members)\n        REDIS_CACHE.set(\"#{GROUP_NSS_RESPONSE}:#{group.name}\", response_hash.to_json)\n        REDIS_CACHE.expire(\"#{GROUP_NSS_RESPONSE}:#{group.name}\", REDIS_KEY_EXPIRY)\n        group_response = response_hash\n      end\n    end\n    group_response\n  end\n\n  def self.get_sysadmins_and_groups(sysadmins, default_admins = true)\n    sysadmins_login_ids = User.\n      select(:user_login_id).\n      where('id IN (?)', sysadmins).\n      map(&:user_login_id)\n\n    # TODO: extract to query object\n    groups = Group.\n      select(%(\n        id,\n        name,\n        gid,\n        (\n          SELECT GROUP_CONCAT(user_login_id)\n          FROM users\n          INNER JOIN group_associations\n            ON users.id = group_associations.user_id\n          WHERE group_associations.group_id = groups.id\n        ) AS members\n      )).\n      where('name IN (?)', sysadmins_login_ids).\n      map do |group|\n        members = (group.members || '').split(',')\n        Group.generate_group_response(group.name, group.gid, members)\n      end\n    groups << Group.get_default_sysadmin_group_for_host(sysadmins_login_ids, default_admins)\n    groups.to_json\n  end\n\n  def get_user_ids\n    user_ids = REDIS_CACHE.get(\"#{GROUP_UID_RESPONSE}:#{name}\")\n    user_ids = JSON.parse(user_ids) if user_ids.present?\n\n    if user_ids.blank?\n      user_ids = users.map(&:user_login_id)\n      REDIS_CACHE.set(\"#{GROUP_UID_RESPONSE}:#{name}\", user_ids.to_json)\n      REDIS_CACHE.expire(\"#{GROUP_UID_RESPONSE}:#{name}\", REDIS_KEY_EXPIRY)\n    end\n    user_ids\n  end\n\n  def self.get_default_sysadmin_group_for_host(sysadmins_login_ids, default_admins = true)\n    sysadmins = sysadmins_login_ids\n\n    if default_admins\n      group = Group.find_by(name: 'sysadmins')\n\n      if group.present?\n        sysadmins = sysadmins + group.get_user_ids\n      end\n    end\n    group_id = group.blank? ? 8999 : group.id\n\n    sysadmin_group = Group.generate_group_response('sysadmins', group_id, sysadmins.uniq)\n    sysadmin_group\n  end\n\n  def add_user(user_id)\n    add_user_with_expiration(user_id, nil)\n  end\n\n  def add_user_with_expiration(user_id, expiration_date)\n    remove_user user_id\n    group_associations.create(user_id: user_id, expiration_date: expiration_date)\n  end\n\n  def remove_user(user_id)\n    group_associations.where(user_id: user_id).destroy_all\n    burst_host_cache\n  end\nend\n"
  },
  {
    "path": "app/models/group_admin.rb",
    "content": "class GroupAdmin < ApplicationRecord\n  belongs_to :user\n  belongs_to :group\nend\n"
  },
  {
    "path": "app/models/group_association.rb",
    "content": "class GroupAssociation < ApplicationRecord\n  belongs_to :user\n  belongs_to :group\n\n  def self.revoke_expired(date = Date.today)\n    where('expiration_date < ?', date).destroy_all\n  end\nend\n"
  },
  {
    "path": "app/models/group_endpoint.rb",
    "content": "class GroupEndpoint < ApplicationRecord\n\n\n  belongs_to :group\n  belongs_to :endpoint\n\n  validates :group, uniqueness: { scope: :endpoint }\n  validates_presence_of :group\n  validates_presence_of :endpoint\nend\n"
  },
  {
    "path": "app/models/host.rb",
    "content": "class Host < ApplicationRecord\n  belongs_to :user\n  acts_as_paranoid\nend\n"
  },
  {
    "path": "app/models/host_access_group.rb",
    "content": "class HostAccessGroup < ApplicationRecord\n  belongs_to :host_machine\n  belongs_to :group\nend\n"
  },
  {
    "path": "app/models/host_machine.rb",
    "content": "class HostMachine < ApplicationRecord\n\n  has_many :host_access_groups\n  has_many :groups, through: :host_access_groups\n  validates_uniqueness_of :name, case_sensitive: false\n  validates :name, presence: true\n\n  before_create :set_lower_case_name\n  before_create :set_host_access_key\n\n  def set_host_access_key\n    self.access_key = ROTP::Base32.random_base32\n  end\n\n  def set_lower_case_name\n    self.name = self.name.downcase\n  end\n\n  def self.get_group_response name\n    host_machine = HostMachine.find_by_name(name)\n    response = {}\n    return response if host_machine.blank?\n    response[:host_name] = name\n    response[:groups] = host_machine.groups.collect { |g| g.name }\n    response\n  end\n\n  def sysadmins\n    users = GroupAssociation.\n      select(:user_id).\n      distinct.\n      joins(:user).\n      where(\"group_id IN (?)\", groups.collect(&:id)).\n      collect(&:user_id)\n  end\n\n  def add_host_group(name)\n    name = name.squish\n    if name.present?\n      name = \"#{name}_host_group\"\n      self.add_group(name.downcase)\n    end\n  end\n\n  def add_group(name)\n    name = name.squish\n    if name.present?\n      group =  Group.find_or_initialize_by(name: name.downcase)\n      self.groups << group unless self.groups.include? group\n      self.save\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/ip_address.rb",
    "content": "class IpAddress < ApplicationRecord\nend\n"
  },
  {
    "path": "app/models/organisation.rb",
    "content": "class Organisation < ApplicationRecord\n  validates :name, :website, :domain, :country, :state, :address,\n            :admin_email_address, :slug, presence: true\n  validates :address, format: {\n    with: /\\A[a-zA-Z0-9\\s]+\\z/,\n    message: 'Invalid - Only Alphabets, Space and Numbers Allowed',\n  }\n  validates :admin_email_address, email: true\n  validates :slug, uniqueness: true\n\n  attr_accessor :cert, :rsa_key\n\n  UPDATE_KEYS = %w(\n    name website domain country state address admin_email_address slug unit_name\n  ).freeze\n\n  def self.find_by_slug(slug)\n    Organisation.where(slug: slug).first\n  end\n\n  def self.setup(attrs = {})\n    attrs = attrs.stringify_keys\n    attrs = attrs.select { |k, _v| UPDATE_KEYS.include?(k) }\n    org = Organisation.new(attrs)\n    org.save if org.valid?\n    org\n  end\n\n  def update_profile(attrs = {})\n    attrs = attrs.stringify_keys\n    attrs = attrs.select { |k, _v| UPDATE_KEYS.include?(k) }\n    assign_attributes(attrs)\n    save if valid?\n  end\n\n  def saml_setup?\n    cert_fingerprint.present? && cert_key.present? && cert_private_key.present?\n  end\n\n  def setup_saml_certs\n    return false unless persisted?\n    require 'openssl'\n    self.rsa_key = OpenSSL::PKey::RSA.new(2048)\n    private_key = rsa_key.to_pem\n    public_key = rsa_key.public_key\n    subject = \"/C=#{country}/ST=#{state}/L=#{address}/O=#{name}/OU=#{unit_name}/CN=#{domain}\"\n    self.cert = OpenSSL::X509::Certificate.new\n    cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)\n    cert.not_before = Time.now\n    cert.not_after = Time.now + 365 * 24 * 60 * 60\n    cert.public_key = public_key\n    cert.serial = SecureRandom.random_number(10)\n    cert.version = 2\n    ef = OpenSSL::X509::ExtensionFactory.new\n    ef.subject_certificate = cert\n    ef.issuer_certificate = cert\n    cert.extensions = [\n      ef.create_extension('basicConstraints', 'CA:TRUE', true),\n      ef.create_extension('subjectKeyIdentifier', 'hash'),\n    ]\n    cert.add_extension ef.create_extension(\n      'authorityKeyIdentifier', 'keyid:always,issuer:always'\n    )\n    cert.sign rsa_key, OpenSSL::Digest::SHA1.new\n    update_attributes(\n      cert_fingerprint: OpenSSL::Digest::SHA256.hexdigest(cert.to_der).scan(/../).join(':'),\n      cert_key: cert.to_pem,\n      cert_private_key: private_key\n    )\n  end\nend\n"
  },
  {
    "path": "app/models/saml_app_config.rb",
    "content": "class SamlAppConfig < ApplicationRecord\n  belongs_to :group\n  belongs_to :organisation\n\n  serialize :config, JSON\n\n  def self.get_config(app_name, org_id)\n    SamlAppConfig.find_or_initialize_by(\n      app_name: app_name, organisation_id: org_id\n    )\n  end\nend\n"
  },
  {
    "path": "app/models/user.rb",
    "content": "class User < ApplicationRecord\n\n\n  include MsChapAuth\n  devise :timeoutable, :omniauthable, omniauth_providers: [:google_oauth2]\n  has_many :hosts\n  has_many :group_associations\n  has_many :groups, through: :group_associations\n  has_many :group_admin, dependent: :destroy\n  has_one :access_token\n\n  # TODO: Need to add the validations for the user model, right now a lot of tests fail due to enabling this\n  # validates :first_name, :last_name, :mobile, :user_role, presence: true\n  # validates :first_name, :last_name, format: { with: /[a-zA-Z]/}, allow_blank: true\n  # validates :user_role, inclusion: { in: ENV['USER_ROLES'].split(',') }\n  # validate :validate_email_domain\n  \n  \n  validates :email, uniqueness: true\n\n  # TODO: Need to enable these again after rails 7 update\n  # validate :remove_default_admin, on: :update\n\n  # before_save :revoke_admin_when_inactive, on: :update\n  # before_save :set_deactivated_at_when_inactive, on: :update\n\n  HOME_DIR = '/home'.freeze\n  USER_SHELL = '/bin/bash'.freeze\n\n  def self.add_user(first_name, last_name, user_role, domain)\n    user = User.new(first_name: first_name, last_name: last_name, user_role: user_role)\n    user.assign_attributes(\n      user_login_id: \"#{first_name.downcase}.#{last_name.downcase}\",\n      uid: user.generate_uid,\n      email: \"#{first_name.downcase}.#{last_name.downcase}@#{domain}\",\n      name: \"#{first_name} #{last_name}\"\n    )\n    user.save\n    user.initialise_host_and_group if user.persisted?\n    user\n  end\n\n  def generate_login_id\n    email.split('@').first\n  end\n\n  def generate_uid(uid_buffer = 5000)\n    uid_buffer = ENV['UID_BUFFER'].present? ? ENV['UID_BUFFER'].to_i : uid_buffer\n    User.last.blank? ? uid_buffer : User.last.id.to_i + uid_buffer\n  end\n\n  def initialise_host_and_group\n    host = Host.find_or_initialize_by(user: self)\n    unless ENV['DEFAULT_HOST_PATTERN'].blank?\n      host.host_pattern = ENV['DEFAULT_HOST_PATTERN']\n    end\n    hosts << host\n    groups << Group.find_or_initialize_by(name: user_login_id)\n  end\n\n  def generate_two_factor_auth(force_create = false)\n    if persisted? && (force_create || (!force_create && auth_key.blank?))\n      self.auth_key = ROTP::Base32.random_base32\n      totp = ROTP::TOTP.new(auth_key)\n      self.provisioning_uri = totp.provisioning_uri \"GoJek-C #{email}\"\n      save!\n    end\n  end\n\n  def self.create_user(name, email)\n    user = User.find_or_initialize_by(email: email)\n    unless user.persisted?\n      user.assign_attributes(\n        name: name, user_login_id: user.generate_login_id, uid: user.generate_uid\n      )\n      user.admin = User.first.blank?\n      user.initialise_host_and_group\n      user.save! if user.valid?\n    end\n    user\n  end\n\n  def self.add_temp_user(name, email)\n    email += \"@#{ENV['GATE_HOSTED_DOMAIN']}\"\n    user = User.create_user(name, email)\n    user.generate_two_factor_auth\n    user.auth_key\n  end\n\n  def update_profile(attrs = {})\n    allowed_keys = %w(public_key name product_name admin active)\n    attrs = attrs.stringify_keys\n    attrs = attrs.select { |k, v| allowed_keys.include?(k) && (v.present? || v.eql?(false)) }\n    assign_attributes(attrs)\n    if active.eql?(false) && deactivated_at.blank?\n      self.deactivated_at = Time.current\n    end\n    save! if valid?\n  end\n\n  def name_email\n    \"#{name} (#{email})\"\n  end\n\n  def self.get_sysadmins user_ids\n    users = User.\n      select(%Q(\n        id,\n        name,\n        uid,\n        user_login_id,\n        (\n          SELECT gid\n          FROM groups\n          INNER JOIN group_associations\n            ON groups.id = group_associations.group_id\n          WHERE group_associations.user_id = users.id\n          AND groups.name = users.user_login_id\n          LIMIT 1\n        ) AS gid,\n        (\n          SELECT COUNT(gid)\n          FROM groups\n          INNER JOIN group_associations\n            ON groups.id = group_associations.group_id\n          WHERE group_associations.user_id = users.id\n          AND groups.name = users.user_login_id\n          LIMIT 1\n        ) AS gid_count\n      )).\n      where(id: user_ids)\n    users.map(&:user_passwd_response)\n  end\n\n  def purge!\n    if !self.active\n      self.group_associations.each{ |g| g.destroy }\n    end\n  end\n\n  def self.includes_restricted_characters? input_string\n    return false if input_string.include?('@') == false\n    restricted_characters = [ ' ', '-', '*']\n    status  = false\n\n    restricted_characters.each do |char|\n      break if status\n      status = input_string.include?(char)\n    end\n\n    status\n  end\n\n  def self.check_email_address email_address\n    !includes_restricted_characters?(email_address) && email_address.split(\"@\").count == 2 ? true : false\n  end\n\n  def self.valid_domain? domain\n    hosted_domains = ENV['GATE_HOSTED_DOMAINS'].split(',')\n    hosted_domains.include?(domain)\n  end\n\n  def self.verify params\n    addresses = params[:addresses]\n    return false if addresses.empty?\n    address_array = addresses.split\n\n\n    user = User.get_user params[:user]\n    return false if user.blank?\n\n    return user.permitted_hosts? address_array\n  end\n\n  def self.authenticate_pam params\n    addresses = params[:addresses]\n    return false if addresses.empty?\n    address_array = addresses.split\n\n    email, token = get_user_pass_attributes params\n    return false if email.blank? || token.blank?\n\n    user_auth = find_and_check_user email, token\n    return check_user_host(email, address_array) if user_auth\n\n    return user_auth\n  end\n\n  def self.check_user_host email, address_array\n    user = User.get_user email\n    return user.permitted_hosts? address_array\n  end\n\n  def permitted_vpns? address_array\n    address_array.each do |host_address|\n      Vpn.user_vpns(self).each do |vpn|\n        return true if vpn.ip_address == host_address\n      end\n    end\n    return false\n  end\n\n  def permitted_hosts? address_array\n    address_array.each do |host_address|\n      host_name = nil\n      begin\n        host_name = Resolv.getname(host_address)\n      rescue\n        Rails.logger.info \"Can't resolve name\"\n      end\n      host_name = host_address if host_name.blank?\n      hosts.each do |host|\n        return true if /^#{host.host_pattern}/.match(host_name).to_s.present?\n      end\n    end\n    return false\n  end\n\n  def self.authenticate_cas encoded_string\n    username_password = Base64.decode64 encoded_string.split(\" \")[1]\n    username = username_password.split(':').first\n    password = username_password.split(':').last\n\n    if User.find_and_check_user username, password\n      return username\n    else\n      return nil\n    end\n  end\n\n\n  def self.authenticate params\n    email, token = User.get_user_pass_attributes params\n    return false if email.blank? || token.blank?\n    return User.find_and_check_user email, token\n  end\n\n  def self.get_user user_login_id\n    return User.where(user_login_id: user_login_id, active: true).first\n  end\n\n  def self.find_and_validate_saml_user(email, password, app_name)\n    query = 'users.email = ? and users.active = ? and groups.name = ?'\n    users = User.joins(:groups).where(query, email, true, app_name)\n    if users.present?\n      users.first.valid_otp?(password) ? users.first : false\n    else\n      false\n    end\n  end\n\n  def valid_otp?(password)\n    user_key = \"#{id}:#{Time.now.hour}\"\n    request_count = REDIS_CACHE.incrby user_key, 1\n    REDIS_CACHE.expire user_key, 3600\n    return false if request_count > RATE_LIMIT\n    password.eql?(ROTP::TOTP.new(self.auth_key).now)\n  end\n\n  def self.find_and_check_user email, token\n    user = User.get_user email\n    return false if user.blank?\n    return false if !user.active\n    user_key = \"#{user.id}:#{Time.now.hour}\"\n    request_count = REDIS_CACHE.incrby user_key, 1\n    REDIS_CACHE.expire user_key, 3600\n    return false if request_count > RATE_LIMIT\n    token == ROTP::TOTP.new(user.auth_key).now\n  end\n\n  def self.get_user_pass_attributes params\n    token = params[:token].present? ? params[:token] : params[:password]\n    email = params[:email].present? ? params[:email] : params[:user]\n    return [nil, nil] if email.blank? || token.blank?\n    [email, token]\n  end\n\n\n  def self.get_shadow_name_response name\n    user = User.where(name: name).first\n    return nil if user.blank?\n\n    user.get_shadow_hash\n  end\n\n  def get_shadow_hash\n    shadow_hash = {}\n    shadow_hash[:sp_namp] = user_login_id\n    shadow_hash[:sp_pwdp] = \"X\"\n    shadow_hash[:sp_lstchg] = updated_at.to_i\n    shadow_hash[:sp_min] = 0\n    shadow_hash[:sp_max] = 99999\n    shadow_hash[:sp_warn] = 7\n    shadow_hash[:sp_inact]= nil\n    shadow_hash[:sp_expire] = nil\n    shadow_hash[:sp_flag] = nil\n    shadow_hash\n  end\n\n  def self.get_all_shadow_response\n    user_array = []\n    User.all.each do |user|\n      user_array << user.get_shadow_hash\n    end\n    user_array\n  end\n\n  def self.get_all_passwd_response\n    user_array = []\n    User.all.each do |user|\n      user_array << user.user_passwd_response\n    end\n    user_array\n  end\n\n  def self.get_passwd_name_response name\n    user = User.where(\"email like ?\", \"#{name}@%\").first\n    return [] if user.blank?\n    user.user_passwd_response\n  end\n\n  def self.response_array response\n    user_response = []\n    user_response << response\n    user_response\n  end\n\n  def self.find_active_user_by_email(email)\n    User.where(email: email, active: true).first\n  end\n\n  def group_names_list\n    self.groups.map(&:name)\n  end\n\n  def reset_login_limit\n    user_key = \"#{self.id}:#{Time.now.hour}\"\n    REDIS_CACHE.set user_key, 0\n  end\n\n  def within_limits?\n    user_key = \"#{self.id}:#{Time.now.hour}\"\n    request_count = REDIS_CACHE.incrby user_key, 1\n    REDIS_CACHE.expire user_key, 3600\n    request_count < RATE_LIMIT\n  end\n\n  def self.ms_chap_auth params\n    auth_failed_message =  \"NT_STATUS_UNSUCCESSFUL: Failure (0xC0000001)\"\n\n    addresses = params[:addresses]\n    user_name = params[:user]\n    challenge_string = params[:challenge]\n    response_string = params[:response]\n\n    return auth_failed_message  if user_name.blank? || challenge_string.blank? || response_string.blank? || addresses.blank?\n\n    address_array = addresses.split\n\n    user = User.get_user user_name\n    if user.present? && user.permitted_vpns?(address_array)\n      drift_interval = 30\n      t = Time.now\n      otps = []\n      otps.push(user.get_user_otp_at(t))\n      otps.push(user.get_user_otp_at(t - drift_interval))\n      otps.push(user.get_user_otp_at(t + drift_interval))\n      return user.authenticate_ms_chap_with_drift otps, challenge_string, response_string\n    else\n      return auth_failed_message\n    end\n  end\n\n  #this method is here because we need to mock/stub for testing\n  def get_user_otp\n    return ROTP::TOTP.new(self.auth_key).now\n  end\n\n  def get_user_otp_at time\n    return ROTP::TOTP.new(self.auth_key).at time\n  end\n\n  def user_passwd_response\n    user_hash = {}\n    user_hash[:pw_name] = user_login_id\n    user_hash[:pw_passwd] = 'x'\n    user_hash[:pw_uid] = uid.to_i\n\n    # If gid is supplied (avoid N+1)\n    if respond_to?(:gid) && gid\n      user_hash[:pw_gid] = gid.to_i\n    elsif respond_to?(:gid_count)\n      if gid_count.positive?\n        user_hash[:pw_gid] = groups.where(name: user_login_id).first.gid\n      end\n    elsif groups.where(name: user_login_id).count.positive?\n      user_hash[:pw_gid] = groups.where(name: user_login_id).first.gid\n    end\n\n    user_hash[:pw_gecos] = name.to_s\n    user_hash[:pw_dir] = \"#{HOME_DIR}/#{user_login_id}\"\n    user_hash[:pw_shell] = '/bin/bash'\n    user_hash\n  end\n\n  def group_admin?\n    GroupAdmin.find_by_user_id(self.id).present?\n  end\n\n  def permitted_endpoint?(endpoint)\n    return false if endpoint.nil?\n\n    user_groups_id = group_associations.select(:group_id)\n    endpoint.\n      group_endpoints.\n      where('group_id IN (?)', user_groups_id).\n      select(:endpoint_id).present?\n  end\n\n  private\n\n  def remove_default_admin\n    admin_users = User.where('active = ? and admin = ? and id <> ?', true, true, id)\n    if (!admin || !active) && admin_users.blank?\n      errors.add(:admin, 'You cannot remove or make inactive the default admin account')\n    end\n  end\n\n  def validate_email_domain\n    domain_list = ENV['GATE_HOSTED_DOMAINS'].split(',')\n    domain = email.split('@').last\n    errors.add(:email, \"Invalid Domain for Email Address\") unless domain_list.include?(domain)\n  end\n\n  def revoke_admin_when_inactive\n    self.admin = false unless active\n  end\n\n  def set_deactivated_at_when_inactive\n    if active.eql?(false) && deactivated_at.blank?\n      self.deactivated_at = Time.current\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/vpn.rb",
    "content": "class Vpn < ApplicationRecord\n  belongs_to :user\n  belongs_to :group\n\n  has_many :vpn_group_associations\n  has_many :groups, through: :vpn_group_associations\n\n  has_many :vpn_group_user_associations\n  has_many :users, through: :vpn_group_user_associations\n\n  has_many :vpn_domain_name_servers\n  has_many :vpn_search_domains\n  has_many :vpn_supplemental_match_domains\n\n  def self.administrator?(user)\n    vpn_group_ids = VpnGroupAssociation.select(:group_id).collect(&:group_id)\n    managed_group_vpns = user.group_admin.where(group_id: vpn_group_ids)\n    !managed_group_vpns.empty?\n  end\n\n  def self.managed_vpns(user)\n\n    vpn_group_ids = VpnGroupAssociation.select(:group_id).collect(&:group_id)\n    managed_group_vpn_ids = user.\n      group_admin.\n      select(:group_id).\n      where(group_id: vpn_group_ids).\n      collect(&:group_id)\n    VpnGroupAssociation.where(group_id: managed_group_vpn_ids).collect(&:vpn).uniq\n  end\n\n  def self.user_vpns user\n\n    vpn_group_ids = VpnGroupAssociation.select(:group_id).collect(&:group_id)\n    user_group_vpn_ids = user.\n      group_associations.\n      select(:group_id).\n      where(group_id: vpn_group_ids).\n      collect(&:group_id)\n    VpnGroupAssociation.where(group_id: user_group_vpn_ids).collect(&:vpn).uniq\n  end\n\n  def migrate_to_new_group\n    group_name = \"#{name}_vpn_group\".downcase.squish.gsub(' ', '_')\n    group = Group.where(name: group_name).first\n    if group.blank?\n      # create a new group for VPN\n      group = Group.create(name: group_name, description: \"#{name} VPN Access group\")\n      # Assign VPN to group\n      # get all vpn administrators\n      admins = []\n      groups.each do |admin_group|\n        admin_group.group_admins.each do |group_admin|\n          group.add_admin group_admin.user\n        end\n      end\n\n      # get all vpn users\n      # add all vpn users to new group\n      users.each do |user|\n        user.groups << group\n      end\n      group.save!\n      group.vpns << self\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/vpn_domain_name_server.rb",
    "content": "class VpnDomainNameServer < ApplicationRecord\n  belongs_to :vpn\nend\n"
  },
  {
    "path": "app/models/vpn_group_association.rb",
    "content": "class VpnGroupAssociation < ApplicationRecord\n  belongs_to :vpn\n  belongs_to :group\nend\n\n"
  },
  {
    "path": "app/models/vpn_group_user_association.rb",
    "content": "class VpnGroupUserAssociation < ApplicationRecord\n  belongs_to :vpn\n  belongs_to :user\nend\n\n\n"
  },
  {
    "path": "app/models/vpn_search_domain.rb",
    "content": "class VpnSearchDomain < ApplicationRecord\n  belongs_to :vpn\nend\n"
  },
  {
    "path": "app/models/vpn_supplemental_match_domain.rb",
    "content": "class VpnSupplementalMatchDomain < ApplicationRecord\n  belongs_to :vpn\nend\n"
  },
  {
    "path": "app/validators/email_validator.rb",
    "content": "class EmailValidator < ActiveModel::EachValidator\n  def validate_each(record, attribute, value)\n    unless value.to_s.match?(/\\A([^@\\s]+)@((?:[-a-z0-9]+\\.)+[a-z]{2,})\\z/i)\n      record.errors[attribute] << (options[:message] || 'is not an email')\n    end\n  end\nend\n"
  },
  {
    "path": "app/views/admin/index.html.slim",
    "content": "h1 hello\n"
  },
  {
    "path": "app/views/api/v1/users/show.json.jbuilder",
    "content": "json.(@user, :email, :uid, :name, :active, :admin, :home_dir, :shell, :public_key, :user_login_id, :product_name)\njson.groups @user.groups, :gid, :name\n"
  },
  {
    "path": "app/views/api_resources/_api_resource.json.jbuilder",
    "content": "json.extract! api_resource, :id, :name, :description, :access_key, :created_at, :updated_at\njson.url api_resource_url(api_resource, format: :json)\n"
  },
  {
    "path": "app/views/api_resources/_form.html.slim",
    "content": "= form_for @api_resource, :class => 'form-horizontal', html: {novalidate: 'true'} do |f|\n  - if @api_resource.errors.any?\n    #error_explanation\n      h2 = \"#{pluralize(@api_resource.errors.count, \"error\")} prohibited this api_resource from being saved:\"\n      ul\n        - @api_resource.errors.full_messages.each do |message|\n          li = message\n\n  hr\n    .mb-3\n      label for=\"name\"\n        | API Name\n      = f.text_field :name, required: true, class: \"form-control\", placeholder: \"API Name\"\n      .invalid-feedback\n    .mb-3\n      label for=\"description\" \n        | Description\n      = f.text_field :description, required: true, class: \"form-control\", placeholder: \"Description\"\n      .invalid-feedback\n    .mb-4\n      = f.submit \"Save\", class: \"btn btn-primary pull-right\"\n"
  },
  {
    "path": "app/views/api_resources/edit.html.slim",
    "content": "h1 Editing api_resource\n\n== render 'form'\n\n=> link_to 'Show', @api_resource\n'|\n=< link_to 'Back', api_resources_path\n\n"
  },
  {
    "path": "app/views/api_resources/index.html.slim",
    "content": ".container-fluid.col-md-8\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n        = notice\n\n\n  h4 API Keys\n\n  table.table.responsive\n    thead\n      tr\n        th Name\n        th Description\n        th Access key\n        th Owner\n        th Access Group\n        th\n\n    tbody\n      - @api_resources.each do |api_resource|\n        tr\n          td = api_resource.name\n          td = api_resource.description\n          td = link_to 'Regenerate', regenerate_access_key_api_resource_path(api_resource), data: { confirm: 'Are you sure?' }\n          td = link_to api_resource.user.name, api_resource.user if api_resource.user.present?\n          td = link_to api_resource.group.name, api_resource.group if api_resource.group.present?\n          td = link_to 'Destroy', api_resource, data: { confirm: 'Are you sure?' }, method: :delete\n\n  br\n\njavascript:\n  $(\"#apiresource-index\").addClass(\"active\");\n"
  },
  {
    "path": "app/views/api_resources/index.json.jbuilder",
    "content": "json.array! @api_resources, partial: 'api_resources/api_resource', as: :api_resource\n"
  },
  {
    "path": "app/views/api_resources/new.html.slim",
    "content": ".container-fluid.col-md-4.col-md-offset-4\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n        = notice\n  br\n  h5 Create new API\n\n  == render 'form'\n\n  = link_to 'Back', api_resources_path\n\n\njavascript:\n  $(\"#apiresource-index\").addClass(\"active\");\n"
  },
  {
    "path": "app/views/api_resources/show.html.slim",
    "content": ".container-fluid.col-md-4.col-md-offset-4\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n        = notice\n  br\n  h5 Show API\n\n  hr\n    .mb-3\n      label for=\"name\"\n        | API Name\n      p.form-control-static\n        = @api_resource.name\n    .mb-3\n      label for=\"description\"\n        | Description\n      p.form-control-static\n        = @api_resource.description\n    - if flash[:access_key]\n      .mb-3\n        label for=\"access_key\"\n          | Access Key\n        .alert.alert-warning role=\"alert\"\n          | Important! please make note of this access key, you will see it only this once.\n        = text_field_tag :access_key, flash[:access_key], class: 'form-control', readonly: \"\"\n    .mb-4\n      => link_to 'Edit', edit_api_resource_path(@api_resource)\n      '|\n      => link_to 'Regenerate Access Key', regenerate_access_key_api_resource_path(@api_resource), :data => {:confirm => 'Are you sure?'}\n      '|\n      =< link_to 'Back', api_resources_path\n"
  },
  {
    "path": "app/views/api_resources/show.json.jbuilder",
    "content": "json.partial! \"api_resources/api_resource\", api_resource: @api_resource\n"
  },
  {
    "path": "app/views/application/_admin.html.slim",
    "content": "div\n  ul.navbar-nav\n    li.nav-item.active\n      a.nav-link href=\"#\" Users\n    li.nav-item\n      a.nav-link href=\"#\" Groups\n"
  },
  {
    "path": "app/views/application/_groups_header.html.slim",
    "content": "#notice\n  = notice\n- if current_user.admin?\n  = form_tag groups_path, method: 'post' do\n    .row\n      .col-md-8\n        = text_field_tag \"group[name]\", \"\", class: \"form-control\", autofocus:  true\n      .col-md-4\n        = submit_tag \"Add Group\", class: \"form-control btn btn-sm btn-primary\", name: nil\n\nbr\n= form_tag groups_path, method: 'get' do\n  .row\n    .col-md-8\n      = text_field_tag :group_search, params[:group_search], class: \"form-control\" , autofocus: true\n    .col-md-4\n      = submit_tag \"Search groups\", class: \"form-control btn btn-sm btn-primary\", name: nil\n\nhr\n\n"
  },
  {
    "path": "app/views/application/_host_header.html.slim",
    "content": "ul.nav.nav-tabs\n  li#host_machine role=\"presentation\" \n    = link_to \"MyHosts\", host_machines_path\n  li#group role=\"presentation\" \n    = link_to \"Groups\", groups_path\n  li#host_machine_search role=\"presentation\" \n    a href=\"#\"  Search Host \n  li#host_machine_group_search role=\"presentation\" \n    a href=\"#\" Search Host Groups\np\n  br\n\n"
  },
  {
    "path": "app/views/common/errors.json.jbuilder",
    "content": "json.success false\njson.errors errors\n"
  },
  {
    "path": "app/views/groups/_form.html.slim",
    "content": ""
  },
  {
    "path": "app/views/groups/index.html.slim",
    "content": ".container-fluid.col.container-profile\n  - if current_user.admin?\n    = form_tag groups_path, method: 'get', class: \"p-2\" do\n      .input-group\n        = text_field_tag :group_search, params[:group_search], class: \"form-control\" , autofocus: true, placeholder: \"Search group name...\"\n        .input-group-append\n          = submit_tag \"Search\", class: \"button btn btn-secondary\", name: nil\n\n   \n  - if @groups.count > 0\n    h5 Your managed groups\n    .table-responsive\n      table.table.table-striped\n        thead\n          tr\n            th Name\n            th Gid\n            th Group Admin\n        tbody\n          - @groups.each do |group|\n            tr\n              td\n                = link_to \"#{group.name}\", group_path(group)\n              td\n                = \"#{group.gid}\"\n              td\n                - if group.group_admins.present?\n                  - group.group_admins.each do |admin| \n                    .row\n                      =  \"#{admin.user.try(:name)}\"\n   \n\njavascript:\n  $(\"#group-index\").addClass(\"active\");\n\n"
  },
  {
    "path": "app/views/groups/new.html.slim",
    "content": ".container-fluid.col-md-4.col-md-offset-4\n  h5 Add new group\n  = form_for @group, :class => 'form-horizontal' do |f|\n    - if @group.errors.any?\n      #error_explanation\n        h2 = \"#{pluralize(@group.errors.count, \"error\")} prohibited this vpn from being saved:\"\n        ul\n          - @group.errors.full_messages.each do |message|\n            li = message\n\n    hr\n    .mb-3\n      label for=\"name\" \n        | Name\n      = f.text_field :name, required: true, class: \"form-control\", placeholder: \"Group Name\"\n      .invalid-feedback\n        | Please enter a name\n\n    .mb-4\n      = f.submit \"Add Group\", class: \"btn btn-primary pull-right\"\n\n"
  },
  {
    "path": "app/views/groups/show.html.slim",
    "content": ".container.col-md-8\n  h5.mb-3 Group Details\n  hr\n  .row\n    .col \n      = \"Group Name: #{@group.name}\"\n    .col\n      = \"Group ID: #{@group.gid}\"\n  br\n  - if current_user.admin? or @group.admin?(current_user)\n    = form_tag add_admin_to_group_path(@group.id), method: :post do\n      .row\n        .col\n          | Group Administrator\n        .col\n          - if @group.group_admins.present?\n            - @group.group_admins.each do |admin| \n              .row\n                .col\n                  = \"#{admin.user.try(:name)}\"\n                .col\n                  = \"#{admin.user.try(:email)}\"\n                .col \n                  - if (current_user.admin?)\n                    = link_to \"Remove?\", [@group, admin], method: :delete, data: {confirm: \"Are you sure to remove #{admin.user.try(:name)} ?\"}\n          - if (current_user.admin?)\n            .row\n              .col-8\n                = text_field_tag \"user_id\", \"\", id: \"assign_admin_user_id\", class: \"form-control\"\n              .col\n                = submit_tag \"Assign admin\", class: \"form-control btn btn-md btn-primary\", disabled: true\n            .row\n              .col-8\n                = check_box_tag \"assign_admin_include_inactive_user\", \"true\", false\n                = \" Include Inactive User\"\n              .col\n  a name=\"group_members\"\n  br\n  .card\n    .card-body\n      h6.card-title Groups Members (#{@group_users.length})\n      .table-responsive\n        table.table.table-striped\n          thead\n            tr\n              th User Details\n              th Email Address\n              th Join Date\n              th Expiration Date\n              th\n          tbody\n            - @group_users.sort_by{ |user| user.email}.each do |user|\n              tr\n                td\n                  - if user.active?\n                    = link_to \"#{user.name}\", user_path(user)\n                  - else\n                    = link_to \"#{user.name} (inactive)\", user_path(user)\n                td\n                  = \"#{user.email}\"\n                td\n                  = \"#{user.join_date.strftime(\"%v\")}\"\n                td\n                  = \"#{user.group_expiration_date}\"\n                td\n                  = link_to \"Delete\", [@group, user], method: :delete, data: {confirm: 'Are you sure to remove this user from the group?'} if current_user.admin or @group.admin?(current_user)\n\n      br\n      = \"*This group does not have any group members\" if @group_users.length == 0\n      br\n      - if current_user.admin or @group.admin?(current_user)\n        .h7 Assign members\n        = form_tag  add_user_to_group_path(@group.id), method: :post do\n          .row\n            .col\n              .form_group\n                label\n                  | User\n                = text_field_tag \"user_id\", \"\", id: \"add_user_user_id\", class: \"form-control\"\n                = check_box_tag \"add_user_include_inactive_user\", \"true\", false\n                = \" Include Inactive User\"\n            .col\n              .form_group\n                label\n                  | Assignment Expiration date\n                = date_field_tag \"expiration_date\", \"\", id: \"expiration_date\", class: \"form-control\"\n                | * expiration date is optional (unspecified means permanent)\n            .col\n              br\n              = submit_tag \"Add User\", class: \"form-control btn-md btn-primary\", disabled: true            \n  br\n  .card\n    .card-body\n      h6.card-title VPN Access\n      .table-responsive\n        table.table.table-striped\n          thead\n            tr\n              th Vpn Details\n              th Vpn name\n              th Vpn Host name\n              th\n          tbody\n            - @group.vpns.sort_by{ |vpn| vpn.name}.uniq{|vpn| vpn.id}.each do |vpn|\n              tr\n                td\n                  = link_to \"#{vpn.name}\", vpn_path(vpn)\n                td\n                  = \"#{vpn.name}\"\n                td\n                  = \"#{vpn.host_name}\"\n                td\n                  - if current_user.admin?\n                    = link_to \"Delete\", [@group, vpn], method: :delete, data: {confirm: 'Are you sure to remove this vpn from the group?'}\n      br\n      = \"*This group does not have any VPNs associated with it\" if @group.vpns.count == 0\n      br\n      - if current_user.admin? \n        = form_tag  add_vpn_to_group_path(@group.id), method: :post do\n          .row\n            .col \n              | Assign VPNs\n            .col\n            .col\n              = text_field_tag \"vpn_id\", \"\", id: \"add_vpn_vpn_id\", class: \"form-control\"\n            .col\n              = submit_tag \"Add Vpn\", class: \"form-control btn-md btn-primary\", disabled: true\n  br\n  .card\n    .card-body\n      h6.card-title Group access hosts\n      .table-responsive\n        table.table.table-striped\n          thead\n            tr\n              th Host Name\n              th \n          tbody\n            - @group.host_machines.each do |machine|\n              tr\n                td\n                  = link_to \"#{machine.name}\", host_machine_path(machine)\n                td\n                  = link_to \"Delete\", [@group, machine], method: :delete, data: {confirm: 'Are you sure to remove this machine from the group??'} if current_user.admin?\n      br\n      = \"*This group does not have any host access\" if @group.host_machines.count == 0\n      br\n      hr\n      - if current_user.admin?\n        = form_tag  add_machine_to_group_path(@group.id), method: :post do\n          .row\n            .col \n              | Assign Hosts\n            .col\n            .col\n              = text_field_tag \"machine_id\", \"\", id: \"add_machine_machine_id\", class: \"form-control\"\n            .col\n              = submit_tag \"Add Host\", class: \"form-control btn-md btn-primary\", disabled: true\n\njavascript:\n  $(\"#group-index\").addClass(\"active\");\n"
  },
  {
    "path": "app/views/home/index.html.slim",
    "content": ".form-signin\n  .row\n    .col\n      h1 Gate-SSO \n  br\n  .row\n    .col\n      h3 Single Sign-On\n  br\n  br\n  br\n  br\n  - case ENV['SIGN_IN_TYPE']\n  - when 'form'\n    = form_tag user_sign_in_path, method: :post do\n      .row\n        .col-sm-10.offset-sm-1\n          = text_field_tag :name, '', class: 'form-control', placeholder: 'Name', required: true\n          = text_field_tag :email, '', class: 'form-control', placeholder: 'Email', required: true, type: 'email'\n          = submit_tag 'Sign in', class: 'form-control btn-md btn-primary'\n  - else\n    .row\n      .col\n      .col\n        a.btn.btn-block.btn-social.btn-google href=\"#{url_for user_google_oauth2_omniauth_authorize_path}\"\n          span.fa.fa-google\n          | Sign in with Google\n      .col\n \n"
  },
  {
    "path": "app/views/host_machines/index.html.slim",
    "content": ".container-fluid.col-md-6.col-md-offset-2\n  = form_tag host_machines_path, method: 'get', class: \"p-2\" do\n    .input-group\n      = text_field_tag :host_machine_search, params[:host_machine_search], class: \"form-control\" , autofocus: true, placeholder: \"Search host...\"\n      .input-group-append\n        = submit_tag \"Search\", class: \"button btn btn-secondary\", name: nil\n\n\n\n\n.container-fluid.col-md-6.col-md-offset-2\n  - if @host_machines.count > 0\n    .table-responsive\n      table.table.table-striped\n        thead\n          tr\n            th Name\n        tbody\n          - @host_machines.each do |host_machine|\n            tr\n              td\n                = link_to \"#{host_machine.name}\", host_machine\n  br\n\njavascript:\n  $(\"#hostmachine-index\").addClass(\"active\");\n"
  },
  {
    "path": "app/views/host_machines/new.html.slim",
    "content": ".container-fluid.col-md-4.col-md-offset-4\n  = form_tag host_machines_path, method: 'post' do\n    .input-group \n      = text_field_tag \"host_machine[name]\", \"\", class: \"form-control\", autofocus:  true\n    .input-group\n      = submit_tag \"Add Host machine\", class: \"form-control btn btn-sm btn-primary\", name: nil\n\n"
  },
  {
    "path": "app/views/host_machines/show.html.slim",
    "content": ".container.col-md-8\n  h5.mb-3 Host Details\n  hr\n  .row\n    .col \n      = @host_machine.name\n  - if current_user.admin?\n    .card\n      .card-body\n        h6.card-title Configurations \n        hr\n        = form_tag \"/host_machines/#{@host_machine.id}\", method: 'put' do\n          .row\n            .col\n              .custom-control.custom-checkbox\n                = check_box \"host_machine\", \"default_admins\", class: \"custom-control-input\", id: \"admin-checkbox\" \n                label.custom-control-label for=\"admin-checkbox\" Default Admins?\n            .col\n              = submit_tag \"Update\", class: \"form-control btn-md btn-primary\"\n  br\n\n  .card\n    .card-body\n      .table-responsive\n        table.table.table-striped\n          thead\n            tr\n              th Group Name\n              th \n          tbody\n            - @host_machine.groups.each do |group|\n              tr\n                td\n                  = link_to \"#{group.name}\", group_path(group)\n                td\n                  = link_to \"Delete\", [@machine, group], method: :delete, data: {confirm: 'Are you sure to remove this machine from the group??'} if current_user.admin?\n      br\n      = \"*This host does not have any group\" if @host_machine.groups.count == 0\n      br\n      hr\n      - if current_user.admin?\n        = form_tag  add_group_to_machine_path, method: :post do\n          .row\n            .col \n              | Assign group\n            .col\n            .col\n              = text_field_tag \"group_id\", \"\", class: \"form-control\"\n            .col\n              = submit_tag \"Add group\", class: \"form-control btn-md btn-primary\", disabled: true\n\n"
  },
  {
    "path": "app/views/layouts/application.html.slim",
    "content": "doctype html\nhtml lang=\"en\"\n  head\n    meta charset=\"utf-8\"\n    meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge,chrome=1\"\n    meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\n    title= content_for?(:title) ? yield(:title) : \"Gate\"\n    = csrf_meta_tags\n    = stylesheet_link_tag \"application\", :media => \"all\"\n    = stylesheet_link_tag \"general\", :media => \"all\"\n    = stylesheet_link_tag \"//cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.4/css/selectize.bootstrap3.min.css\"\n    = javascript_include_tag \"application\"\n    = javascript_include_tag \"//cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.4/js/standalone/selectize.min.js\"\n    /! Le HTML5 shim, for IE6-8 support of HTML elements\n    /[if lt IE 9]\n      = javascript_include_tag \"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.min.js\"\n\n  body\n    /! Fixed navbar\n    nav.navbar.navbar-expand-md.navbar-dark.fixed-top.bg-dark\n      a.navbar-brand href=\"/profile\"  gate\n      button.navbar-toggler aria-controls=\"navbarsExampleDefault\" aria-expanded=\"false\" aria-label=(\"Toggle navigation\") data-target=\"#main-navigation\" data-toggle=\"collapse\" type=\"button\"\n        span.navbar-toggler-icon\n      #main-navigation.collapse.navbar-collapse\n        ul.navbar-nav.mr-auto\n          - if current_user.admin?\n            li#user-index.nav-item\n              = link_to(\"Users\", users_path, class: \"nav-link\")\n            li#hostmachine-index.nav-item\n              = link_to \"Hosts\", host_machines_path, class: \"nav-link\"\n            li#organisation-index.nav-item\n              = link_to \"Organisations\", organisations_path, class: \"nav-link\"\n          - if current_user.admin? || current_user.group_admin?\n            li#group-index.nav-item\n              = link_to \"Groups\", groups_path, class: \"nav-link\"\n          li#apiresource-index.nav-item\n           = link_to \"APIs\", api_resources_path, class: \"nav-link\"\n          li#vpn-index.nav-item\n           = link_to \"VPNs\", vpns_path, class: \"nav-link\"\n          li.nav-item.dropdown\n            a#dropdown01.nav-link.dropdown-toggle aria-expanded=\"false\" aria-haspopup=\"true\" data-toggle=\"dropdown\" href=\"http://example.com\"  New\n            .dropdown-menu aria-labelledby=\"dropdown01\"\n              = link_to \"API\", new_api_resource_path, class: \"dropdown-item\"\n              - if current_user.admin?\n                = link_to \"Group\", new_group_path, class: \"dropdown-item\"\n                = link_to \"VPN\", new_vpn_path, class: \"dropdown-item\"\n                = link_to \"Organisation\", new_organisation_path, class: \"dropdown-item\"\n                = link_to \"User\", new_user_path, class: \"dropdown-item\"\n        ul.nav.navbar-nav.navbar-right\n          li.nav-link\n            = link_to \"Sign out\", users_sign_out_path,:method => :delete , class: \"nav-link\"\n          li.nav-link.active\n            = link_to current_user.name, user_path(current_user.id), class: \"nav-link\"\n        /! /.nav-collapse\n    .container-fluid\n      = yield\n"
  },
  {
    "path": "app/views/layouts/home.html.slim",
    "content": "doctype html\nhtml lang=\"en\"\n  head\n    link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'\n    meta charset=\"utf-8\"\n    meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge,chrome=1\"\n    meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\n    title= content_for?(:title) ? yield(:title) : \"Gate\"\n    = csrf_meta_tags\n    = stylesheet_link_tag \"application\", :media => \"all\"\n    = favicon_link_tag 'apple-touch-icon-144x144-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '144x144'\n    = favicon_link_tag 'apple-touch-icon-72x72-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '72x72'\n    = favicon_link_tag 'apple-touch-icon-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png'\n    = favicon_link_tag 'favicon.ico', :rel => 'shortcut icon'\n    = javascript_include_tag \"application\"\n    /! Le HTML5 shim, for IE6-8 support of HTML elements\n    /[if lt IE 9]\n      = javascript_include_tag \"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.min.js\"\n\n\n  body.home\n    = yield\n"
  },
  {
    "path": "app/views/layouts/profile.html.slim.disabled",
    "content": "doctype html\nhtml lang=\"en\"\n  head\n    meta charset=\"utf-8\"\n    meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge,chrome=1\"\n    meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\n    title= content_for?(:title) ? yield(:title) : \"Gate\"\n    = csrf_meta_tags\n    = stylesheet_link_tag \"application\", :media => \"all\"\n    = stylesheet_link_tag 'profile'\n    = stylesheet_link_tag \"general\", :media => \"all\"\n    = javascript_include_tag \"application\"\n    /! Le HTML5 shim, for IE6-8 support of HTML elements\n    /[if lt IE 9]\n      = javascript_include_tag \"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.min.js\"\n\n  body.profile_page\n    .container.container-profile\n      .header.clearfix\n        nav\n          ul.nav.nav-pills.pull-right\n            li role=\"presentation\" \n             = link_to \"Administration\", admin_path if current_user.admin?\n            li role=\"presentation\" \n             = link_to current_user.name, user_path(current_user.id)\n            li.active role=\"presentation\" \n              = link_to \"Sign out\", users_sign_out_path,:method => :delete \n        h3.text-muted Gate - Single Sign On\n      = yield\n\n      footer.footer\n        p &copy; 2015 Gate-SSO\n"
  },
  {
    "path": "app/views/nss/add_host.json.jbuilder",
    "content": "json.success true\njson.access_key host.access_key\njson.host host.name\njson.groups host.groups.map(&:name)\n"
  },
  {
    "path": "app/views/organisations/_form.html.slim",
    "content": "- if flash.key?(:errors)\n  .alert.alert-danger#organisation_form_errors\n    b Issue creating application\n    br\n    - flash[:errors].each do |msg|\n      = \"- #{msg}\"\n      br\n.form-group\n  = f.label :name\n  = f.text_field :name, class: 'form-control'\n.form-group\n  = f.label :website\n  = f.text_field :website, class: 'form-control'\n.form-group\n  = f.label :domain\n  = f.text_field :domain, class: 'form-control'\n.form-group\n  = f.label :country\n  = f.collection_select :country, Country.all.sort_by(&:name), :gec, :name, {}, class: 'form-control'\n.form-group\n  = f.label :state\n  = f.text_field :state, class: 'form-control'\n.form-group\n  = f.label :address\n  = f.text_field :address, class: 'form-control'\n.form-group\n  = f.label :admin_email_address\n  = f.text_field :admin_email_address, class: 'form-control'\n.form-group\n  = f.label :slug\n  = f.text_field :slug, class: 'form-control'\n.form-group\n  = f.label :unit_name\n  = f.text_field :unit_name, class: 'form-control'\n"
  },
  {
    "path": "app/views/organisations/config_saml_app.html.slim",
    "content": ".container.col-md-8\n  .row.mb-3.mt-2\n    .col-md-12\n      h5 Configure #{app_name.titleize}\n  .row.mb-3.mt-2\n    .col-md-12\n      = render partial: \"organisations/saml_apps/#{app_name}\", locals: {org: org, app_name: app_name, saml_config: saml_config, users: users}\n"
  },
  {
    "path": "app/views/organisations/index.html.slim",
    "content": ".container.col-md-8\n  .row.mb-3.mt-2\n    .col-md-8\n      h5 Organisations\n    .col-md-4.text-right\n      = link_to \"New Organisation\", new_organisation_path, class: \"btn btn-primary\", id: 'new_organisation_btn'\n  - if flash.key?(:success)\n    .alert.alert-success#organisation_form_success\n      = flash[:success]\n  - if flash.key?(:errors)\n    .alert.alert-danger#organisation_form_errors\n      = flash[:errors]\n  #organisation_list.table-responsive\n    table.table.table-striped\n      thead\n        tr\n          th Name\n          th URL\n          th Email Domain\n          th Actions\n      tbody\n        - if org_list.present?\n          - org_list.each do |org|\n            tr\n              td = link_to org.name, organisation_path(org)\n              td = link_to org.website, org.website\n              td = org.domain\n              td\n                - if org.saml_setup?\n                  .dropdown.position-relative#configureAppDropDownMenuContainer\n                    button.btn.btn-primary.btn-sm.dropdown-toggle type=\"button\" id=\"configureAppDropDownMenu\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\" data-boundary=\"configureAppDropDownMenuContainer\"\n                      = \"Configure App\"\n                    .dropdown-menu aria-labelledby=\"configureAppDropDownMenu\"\n                      = link_to \"Datadog\", organisation_config_saml_app_path(organisation_id: org.id, app_name: 'datadog'), class: 'dropdown-item'\n                - else\n                  = link_to \"Setup SAML\", organisation_setup_saml_path(org)\n        - else\n          td.text-center colspan='3'\n            p There are no organisations yet, why don't you create an organisation\n"
  },
  {
    "path": "app/views/organisations/new.html.slim",
    "content": ".container.col-md-8\n  .row.mb-3.mt-2\n    .col-md-12\n      h5 Create Organisation\n  hr\n  = form_for(org) do |f|\n    = render partial: 'form', locals: {f: f}\n    = f.submit 'Create Organisation', class: 'btn btn-primary mb-2'\n"
  },
  {
    "path": "app/views/organisations/saml_apps/_datadog.html.erb",
    "content": "<ul class=\"nav nav-tabs\" id=\"configAppTabs\" role=\"tablist\">\n  <li class=\"nav-item\">\n    <a class=\"nav-link active\" href=\"#instruction\" id=\"instruction-tab\" data-toggle=\"tab\" role=\"tab\" aria-controls=\"instruction\" aria-selected=\"true\">Instructions</a>\n  </li>\n  <li class=\"nav-item\">\n    <a class=\"nav-link\" href=\"#settings\" id=\"settings-tab\" data-toggle=\"tab\" role=\"tab\" aria-controls=\"settings\" aria-selected=\"true\">Settings</a>\n  </li>\n  <li class=\"nav-item\">\n    <a class=\"nav-link\" href=\"#manage-users\" id=\"manage-users-tab\" data-toggle=\"tab\" role=\"tab\" aria-controls=\"manage-users\" aria-selected=\"true\">Manage Users</a>\n  </li>\n</ul>\n<div class=\"tab-content\" id=\"configAppTabsContent\">\n  <div class=\"tab-pane fade show active\" id=\"instruction\" role=\"tabpanel\" aria-labelledby=\"instruction-tab\">\n    <h6>Pre Requisites</h6>\n    <ol>\n      <li>You need to have a valid account @ <a href=\"https://www.datadog.com\">datadog.com</a></li>\n      <li>You need to be an administrator of the datadog account</li>\n    </ol>\n    <h6>Instruction Steps</h6>\n    <ol>\n      <li><a href=\"#configureDataDog\">Configuration Steps on DataDog</a></li>\n      <li><a href=\"#configureGate\">Configuration Steps on Gate</a></li>\n      <li><a href=\"#manageUsersGate\">Manage Users on Gate</a></li>\n    </ol>\n    <h6 id=\"configureDataDog\">Configuration Steps on Datadog</h6>\n    <ol>\n      <li>Login to Datadog as an administrator</li>\n      <li>\n        <p>\n          Click on <img src='https://secure.gravatar.com/avatar/5800f8b6f41ce19a81012cf8aef1bfc1?s=48&d=retro' style='width: 20px;' /> in the bottom left, then select <b>Configure SAML</b>\n        </p>\n        <%= image_tag(\"saml_configure_datadog_01.png\") %>\n      </li>\n      <li>Download the IdP Metadata by clicking <%= link_to \"here\", metadata_path(slug: org.slug, app: 'datadog', download: true) %></li>\n      <li>\n        <p>Click <b>Choose file</b>, locate the <b>metadata.xml</b> you just saved, and then click Upload File</p>\n        <%= image_tag(\"saml_configure_datadog_02.png\") %>\n      </li>\n      <li>\n        <p>Click <b>Enable</b></p>\n        <%= image_tag(\"saml_configure_datadog_03.png\") %>\n      </li>\n      <li>Make a copy of your <b>Single Sign-on URL</b> value</li>\n      <li>\n        <p>For the below image follow the following steps:</p>\n        <ol>\n          <li>Enable <b>Identity Provider (IdP) Initiated Login</b></li>\n          <li>Enable <b>SAML Strict Mode</b></li>\n          <li><u>Note: We will not be using <b>JIT</b>, instead managing users will be done via a different panel</u></li>\n          <li>Hit <b>Save</b></li>\n        </ol>\n        <%= image_tag(\"saml_configure_datadog_04.png\") %>\n      </li>\n    </ol>\n    <h6 id=\"configureGate\">Configuration Steps on Gate</h6>\n    <ol>\n      <li>\n        <p>Copy the entity Id from the below picture after setting up SAML using the previous Instructions</p>\n        <%= image_tag(\"saml_configure_datadog_03.png\") %>\n      </li>\n      <li>Navigate to <a href=\"https://app.datadoghq.com/account/settings#api\">Datadog API Setup</a> to setup your API and App Keys</li>\n      <li>\n        <p>Refer the below image, to add the API Key</p>\n        <%= image_tag(\"saml_configure_datadog_05.png\") %>\n      </li>\n      <li>\n        <p>Refer the below image, to add the App Key</p>\n        <%= image_tag(\"saml_configure_datadog_06.png\") %>\n      </li>\n      <li>\n        <p>Update the SSO Url, Application Key, API Key from the previous steps by refering the below image</p>\n        <%= image_tag(\"saml_configure_datadog_07.png\") %>\n      </li>\n      <li>As a part of this process a group will be auto generated to add and remove users for accessing Datadog. The group created would be called <b>saml_datadog_users</b></li>\n    </ol>\n    <h6 id=\"manageUsersGate\">Manage Users on Gate</h6>\n    <ol>\n      <li>Click on <b>Manage Users</b> to add/remove users</li>\n      <li>\n        <p>Enter the email address in the below box to add users to datadog</p>\n        <%= image_tag(\"saml_configure_datadog_08.png\") %>\n      </li>\n      <li>\n        <p>When you click on the page, you will get a list of users with a remove button. You can click remove to remove users from accessing datadog</p>\n        <%= image_tag(\"saml_configure_datadog_09.png\") %>\n      </li>\n    </ol>\n  </div>\n  <div class=\"tab-pane fage\" id=\"settings\" role=\"tabpanel\" aria-labelledby=\"settings-tab\">\n    <%= form_for saml_config, url: organisation_save_config_saml_app_path(organisation_id: org.id, app_name: app_name), method: :post do |f|  %>\n      <div class='form-group'>\n        <%= label_tag :datadog_sso_url %>\n        <%= f.text_field :sso_url, class: 'form-control' %>\n      </div>\n      <div class='form-group'>\n        <%= label_tag :application_key %>\n        <%= text_field_tag 'config[app_key]', saml_config.config['app_key'], class: 'form-control' %>\n      </div>\n      <div class='form-group'>\n        <%= label_tag :api_key %>\n        <%= text_field_tag 'config[api_key]', saml_config.config['api_key'], class: 'form-control' %>\n      </div>\n      <%= f.submit 'Update Configuration', class: 'btn btn-primary mb-2'%>\n    <% end %>\n  </div>\n  <div class=\"tab-pane fage\" id=\"manage-users\" role=\"tabpanel\" aria-labelledby=\"manage-users-tab\">\n    <%= form_tag  organisation_add_user_saml_app_path(organisation_id: org.id, app_name: app_name), method: :post do  %>\n      <div class='form-group'>\n        <%= label_tag :email_address %>\n        <%= text_field_tag 'email', '', class: 'form-control' %>\n      </div>\n      <%= submit_tag 'Add User', class: 'btn btn-primary mb-2', id: 'add_user_to_app' %>\n    <% end %>\n    <div class=\"table_responsive\">\n      <table class=\"table table table-striped\">\n        <thead>\n          <tr>\n            <th>Name</th>\n            <th>Email</th>\n            <th>Actions</th>\n          </tr>\n        </thead>\n        <tbody>\n          <% users.each do |user| %>\n            <tr>\n              <td><%= link_to user.name, user_path(id: user.id) %></td>\n              <td><%= user.email %></td>\n              <td><%= link_to \"Remove\", organisation_remove_user_saml_app_path(organisation_id: org.id, app_name: app_name, email: user.email), method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-sm btn-danger', id: \"saml_user_remove_#{user.id}\" %></td>\n            </tr>\n          <% end %>\n        </tbody>\n      </table>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "app/views/organisations/show.html.slim",
    "content": ".container.col-md-8\n  .row.mb-3.mt-2\n    .col-md-12\n      h5 Update Organisation\n  hr\n  = form_for(org) do |f|\n    = render partial: 'form', locals: {f: f}\n    = f.submit 'Update Organisation', class: 'btn btn-primary mb-2'\n"
  },
  {
    "path": "app/views/profile/_group_search.html.slim",
    "content": ".container.container-profile\n  = form_tag profile_group_admin_path, method: 'get' do\n    .row \n      .col-md-8\n        = text_field_tag :group_search, params[:group_search], class: \"form-control\" , autofocus: true\n      .col-md-4\n        = submit_tag \"Search groups\", class: \"form-control btn btn-sm btn-primary\", name: nil\n\n  hr\n\n"
  },
  {
    "path": "app/views/profile/_user_search.html.slim",
    "content": ".container.container-profile\n  = form_tag profile_user_admin_path, method: 'get' do\n    .row \n      .col-md-8\n        = text_field_tag :user_search, params[:user_search], class: \"form-control\" , autofocus: true\n      .col-md-4\n        = submit_tag \"Search Users\", class: \"form-control btn btn-sm btn-primary\", name: nil\n\n  hr\n"
  },
  {
    "path": "app/views/profile/group_admin.html.slim",
    "content": "= render partial: \"group_search\"\n"
  },
  {
    "path": "app/views/profile/list.html.slim",
    "content": "= render partial: \"user_search\"\n- if @users.count > 0\n  .table-responsive\n    table.table.table-striped\n      thead\n        tr\n          th Name\n          th Email\n          th Is Active?\n          th Is Admin?\n      tbody\n        - @users.each do |user|\n          tr\n            td\n              = link_to \"#{user.name}\", user_path(user)\n            td\n              = \"#{user.email}\"\n            td\n              = \"#{user.active.to_s.camelcase}\"\n            td\n              = \"#{user.admin.to_s.camelcase}\"\n"
  },
  {
    "path": "app/views/profile/public_key.html.slim",
    "content": ""
  },
  {
    "path": "app/views/profile/show.html.slim",
    "content": ".container.container-profile\n  .row.mt-3\n  .jumbotron\n    .row.marketing\n      .col-lg-6\n        .row\n          .col-lg-12 \n            strong Use Google Authenticator to scan this QR Code\n        .row.mt-2\n        .row\n          .col-lg-12\n            .token-qr\n              - unless @token_qr.blank?\n                = raw @token_qr.as_html\n        .row\n          .col-lg-12\n            small Note: If you can't see the QR Code, please logout and login again.\n      .col-lg-6\n        .row\n          .col-lg-12 Or use this token for the authenticator\n        .row.mt-2\n        .row\n          .col-lg-12\n            pre\n              h5 #{current_user.auth_key}\n        .row\n          .col-lg-12\n            a.btn.btn-warning.btn-sm href=\"/regenerate_authentication\" role=\"button\"  data-turbolinks=\"false\" (Re)generate Token\n        .row.mt-4\n        .row\n          .col-lg-12 Download Profiles\n        .row.mt-3\n        .row\n          .col-lg-12\n            a.btn.btn-success.btn-sm href=\"/download_vpn\" role=\"button\"  data-turbolinks=\"false\" Download OpenVPN profile\n        .row.mt-3\n        .row\n          .col-lg-12\n            a.btn.btn-success.btn-sm href=\"/download_vpn_for_ios_and_mac\" role=\"button\"  data-turbolinks=\"false\" Download IPSec profile for iOS/Mac\n        .row.mt-4\n        .row\n          .col-lg-12 Google Authenticator\n        .row\n          .col-lg-12\n            a href=\"https://itunes.apple.com/us/app/google-authenticator/id388497605\" target=\"_blank\"\n              = image_tag \"app_store_en_badge.png\", width: 120, alt: \"Apple App Store\"\n            a href=\"https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en\" target=\"_blank\"\n              = image_tag \"google_play_en_badge.png\", width: 120, alt: \"Google Play Store\"\n  .row.marketing\n    .col-lg-6\n      h4\n        strong OpenVPN\n      h5\n        strong Step 1\n      p\n        | Download OpenVPN client. For Mac, use \n        a href=\"https://tunnelblick.net/\" target=\"_blank\" TunnelBlick.\n      h5\n        strong Step 2\n      p Download your OpenVPN profile and install to OpenVPN client.\n      h5\n        strong Step 3\n      p Launch OpenVPN client, connect to your profile and enter token code shown on Google Authenticator.\n    .col-lg-6\n      h4\n        strong IPSec VPN\n      h5\n        strong Step 1\n      p Download your IPSec VPN profile and install by double-clicking on it.\n      h5\n        strong Step 2\n      p \n        | Open \n        code System Preferences >> Network \n        | , you will see list of VPNs that you can connect to.\n      h5\n        strong Step 3\n      p Connect to any VPN and enter token code shown on Google Authenticator.\n"
  },
  {
    "path": "app/views/profile/user.html.slim",
    "content": ".container.container-profile\n  .row\n    .col-md-4.text-primary Name\n    .col-md-8\n      = @user.name\n  .row\n    .col-md-4.text-primary Email\n    .col-md-8\n      = @user.email\n  .row\n    .col-md-4.text-primary MFA Auth String\n    .col-md-8\n      = @user.auth_key\n  - if current_user.admin?\n    = form_tag user_update_path, method: 'post' do\n      .row\n        .col-md-4.text-primary Active?\n        .col-md-8\n          = check_box \"user\", \"active\"\n      .row\n        .col-md-4.text-primary Administrator?\n        .col-md-8\n          = check_box \"user\", \"admin\"\n      .row\n        .col-md-4\n        .col-md-2\n          = submit_tag \"Update\", class: \"form-control btn-sm btn-primary\"\n  hr\n  .row\n    .col-md-4.text-primary\n      = link_to \"Groups\", groups_path\n    .col-md-8\n      - @user.groups.each do |group|\n        - unless group.deleted?\n        .row\n          .col-md-8\n            = \"#{group.name} (#{group.gid})\"\n          .col-md-4\n            = link_to \"Delete\", [@user, group], method: :delete, data: {confirm: 'Are you sure to delete this group?'} if current_user.admin?\n      = \"No groups are associated with this user\" if @user.groups.count == 0\n  .row\n    .col-md-4\n    .col-md-8\n      .row\n        - if current_user.admin?\n          = form_tag  add_group_path, method: :post do\n            .col-sm-8\n              = select_tag \"group_id\", options_from_collection_for_select(@group.sort_by {|group| group.name}, \"id\", \"name\"), class: \"form-control\"\n            .col-sm-4\n              = submit_tag \"Add Group\", class: \"form-control btn-sm btn-primary\"\n  hr\n  .row\n    .col-md-4.text-primary Permitted Host Patterns\n    .col-md-8\n      - @user.hosts.each do |host|\n        - unless host.deleted?\n        .row\n          .col-md-8\n            = host.host_pattern\n          .col-md-4\n            = link_to \"Delete\", [host.user, host], method: :delete, data: {confirm: 'Are you sure to delete this pattern?'} if current_user.admin?\n\n      = \"No host patterns are associated with this user\" if @user.hosts.count == 0\n  .row\n    .col-md-4\n    .col-md-8\n      .row\n        - if current_user.admin?\n          = form_tag  add_host_path, method: :post do\n            .col-sm-8\n              = text_field_tag :host_pattern, \"\", class: \"form-control\"\n            .col-sm-4\n              = submit_tag \"Add host pattern\", class: \"form-control btn-sm btn-primary\"\n\n  br\n  hr\n  .row\n    .col-md-4.text-primary Public Key\n    .col-md-8\n      = form_tag user_public_key_update_path, method: :post do\n        .row\n          .col-sm-12\n            = text_area_tag \"public_key\", @user.public_key, class: \"form-contol\", style: \"font-family:monospace;\", rows: 15, cols: 35\n        .row\n          .col-sm-8\n          .col-sm-4\n            = submit_tag \"Update key\", class: \"form-control btn-sm btn-primary\"\n\n  hr\n  .row\n    .col-md-4.text-primary\n      = link_to \"Vpns\", vpns_path\n    .col-md-8\n      - @user.vpns.each do |vpn|\n        .row\n          .col-md-8\n            = \"#{vpn.name}\"\n\n  hr\n  .row\n    .col-md-4.text-primary VPN Profile\n    .col-md-8\n        .col-sm-12\n        .row\n          .col-sm-8\n          .col-sm-4\n            a.btn.btn-primary.btn-sm href=\"/download_vpn/#{@user.id}\" role=\"button\" Download VPN Profile\n  br\n\n\n"
  },
  {
    "path": "app/views/profile/user_admin.html.slim",
    "content": "= render partial: \"user_search\"\n"
  },
  {
    "path": "app/views/saml_idp/idp/new.html.erb",
    "content": "<!DOCTYPE html>\n<html xmlns:layout=\"http://www.ultraq.net.nz/thymeleaf/layout\">\n<head>\n  <meta charset=\"UTF-8\"/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n  <title>GoJek &#8211; Single Sign on</title>\n  <link rel=\"stylesheet\" href=\"//maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css\"/>\n  <link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css\" integrity=\"sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4\" crossorigin=\"anonymous\">\n  <style type=\"text/css\">\n    #container { margin-top: 50px; }\n    .login-header {\n      padding: 20px;\n      background-color: #d5d5d5;\n      margin-bottom: 10px;\n      border-radius: 4px 4px 0px 0px;\n    }\n    #login {\n      background-color: #efefef;\n      border-radius: 4px;\n    }\n    #login .login-header .fa-circle { color: #1190AB; }\n    #login form  { padding: 30px; padding-top: 15px; }\n    #login_container { width: 100%; max-width: 430px; margin: auto; }\n    #login_container img { width: 200px; }\n  </style>\n</head>\n<body>\n<div id=\"container\" class=\"container-fluid\">\n  <div class=\"row text-center\">\n    <div id=\"login_container\">\n      <%= image_tag(\"logo.png\") %>\n      <p>Single Sign-on Multifactor Authentication.</p>\n      <div id=\"login\">\n\t      <div class=\"login-header\">\n\t\t      <h2>Login</h2>\n      \t\t<span class=\"fa-stack fa-2x hidden-xs\">\n\t\t        <i class=\"fa fa-circle fa-stack-2x\"></i>\n      \t\t  <i class=\"fa fa-lock fa-stack-1x fa-inverse\"></i>\n\t\t      </span>\n\t      </div>\n\t      <%= form_tag auth_path, class: 'text-left' do %>\n\t\t      <%= hidden_field_tag(\"SAMLRequest\", params[:SAMLRequest]) %>\n      \t\t<%= hidden_field_tag(\"RelayState\", params[:RelayState]) %>\n\t\t      <div class=\"form-group\">\n    \t\t    <%= label_tag :email %>\n\t\t        <%= text_field_tag :email, params[:email], :autocapitalize => \"off\", :autocorrect => \"off\", :autofocus => \"autofocus\", :spellcheck => \"false\", :size => 30, :class => \"email_pwd txt form-control\" %>\n\t\t      </div>\n\t\t      <div class=\"form-group\">\n\t\t        <%= label_tag \"Google Authenticator Code\" %>\n\t\t        <%= password_field_tag :password, params[:password], :autocapitalize => \"off\", :autocorrect => \"off\", :spellcheck => \"false\", :size => 30, :class => \"email_pwd txt form-control\" %>\n\t\t      </div>\n\t\t      <%= submit_tag \"Sign in\", :class => \"button big blueish btn btn-primary\" %>\n\t      <% end %>\n      </div>\n    </div>\n  </div>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "app/views/users/_search.html.slim",
    "content": ".container-fluid.col-md-4.col-md-offset-4\n  = form_tag users_path, method: 'get', class: \"p-2\" do\n    .input-group\n      = text_field_tag :user_search, params[:user_search], class: \"form-control\" , autofocus: true, placeholder: \"Search user name...\"\n      .input-group-append\n        = submit_tag \"Search\", class: \"button btn btn-secondary\", name: nil\n\n"
  },
  {
    "path": "app/views/users/index.html.slim",
    "content": "= render partial: \"search\"\n- if @users.count > 0\n  .table-responsive\n    table.table.table-striped\n      thead\n        tr\n          th Name\n          th Email\n          th Is Active?\n          th Is Admin?\n      tbody\n        - @users.each do |user|\n          tr\n            td\n              = link_to \"#{user.name}\", user_path(user)\n            td\n              = \"#{user.email}\" \n            td\n              = \"#{user.active.to_s.camelcase}\"\n            td\n              = \"#{user.admin.to_s.camelcase}\"\n\njavascript:\n  $(\"#user-index\").addClass(\"active\");\n"
  },
  {
    "path": "app/views/users/new.html.erb",
    "content": "<div class=\"container col-md-8\">\n  <div class=\"row mb-3 mt-2\">\n    <div class=\"col-md-12\">\n      <h5>Create User</h5>\n      <hr />\n      <%= form_for(User.new) do |f| %>\n        <% if flash.key?(:errors) %>\n          <div class=\"alert alert-danger\" id=\"user_form_errors\">\n            <b>Issue Creating User</b><br/>\n            <%= flash[:errors].map { |msg| \"- #{msg}\".html_safe }.join(\"<br />\").html_safe %>\n          </div>\n        <% end %>\n        <div class=\"form-group\">\n          <%= f.label :first_name %>\n          <%= f.text_field :first_name, class: 'form-control' %>\n        </div>\n        <div class=\"form-group\">\n          <%= f.label :last_name %>\n          <%= f.text_field :last_name, class: 'form-control' %>\n        </div>\n        <div class=\"form-group\">\n          <%= f.label :mobile %>\n          <%= f.text_field :mobile, class: 'form-control' %>\n        </div>\n        <div class=\"form-group\">\n          <%= f.label :alternate_email %>\n          <%= f.text_field :alternate_email, class: 'form-control' %>\n        </div>\n        <div class=\"form-group\">\n          <%= f.label :user_role %>\n          <%= f.select :user_role, add_placeholder_to_list(roles, \"Select A Role\"), {}, class: 'form-control' %>\n        </div>\n        <div class=\"form-group\">\n          <%= label_tag :domain %>\n          <%= select_tag :user_domain, options_for_select(add_placeholder_to_list(domains, \"Select A Domain\", string_convert: '')), class: 'form-control' %>\n        </div>\n        <%= f.submit 'Create User', class: 'btn btn-primary mb-2' %>\n      <% end %>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "app/views/users/show.html.slim",
    "content": ".container.col-md-8\n  h5.mb-3 User Profile \n  hr\n  - if flash.key?(:success)\n    .alert.alert-success\n      = flash[:success]\n  form.needs-validation novalidate=\"\" \n    .form-row.mb-3\n      .col\n        label for=\"Name\" \n          b\n            = @user.name\n      .col\n        .input-group\n          .input-group-prepend\n             span.input-group-text @\n          input#email.form-control placeholder=\"you@example.com\" readonly=\"\" type=\"email\" value=\"#{@user.email}\"\n\n  - if (current_user.admin? or (current_user.id == @user.id))\n    form.needs-validation novalidate=\"\" \n      .form-row.mb-3\n        .col\n          b Your Access Token\n        .col\n          - if flash[:token]\n            .alert.alert-warning role=\"alert\"\n              | Important! please make note of this token, you will see it only this once.\n            pre\n              = flash[:token]\n          - else\n            = link_to 'Regenerate Token', regenerate_token_user_path(@user), :data => {:confirm => 'Are you sure?'}\n\n  - if current_user.admin?\n    .card\n      .card-body\n        h6.card-title Access and Permissions \n        hr\n        = form_tag user_update_path, method: 'post' do\n          .row\n            .col\n              .custom-control.custom-checkbox\n                = check_box \"user\", \"active\", class: \"custom-control-input\", id: \"active-checkbox\" \n                label.custom-control-label for=\"active-checkbox\"  Active?\n            .col\n              .custom-control.custom-checkbox\n                = check_box \"user\", \"admin\", class: \"custom-control-input\", id: \"admin-checkbox\" \n                label.custom-control-label for=\"admin-checkbox\"  Adminstrator?\n            .col\n              = submit_tag \"Update\", class: \"form-control btn-md btn-primary\"\n    br\n  .card\n    .card-body\n      h6.card-title Groups\n      hr\n      .table-responsive\n        table.table.table-striped\n          thead\n            tr\n              th Group Name\n              th Expiration Date\n              th Action\n          tbody\n            - @user_groups.each do |group|\n              - unless group.deleted?\n              tr\n                td\n                  = \"#{group.name} (#{group.gid})\"\n                td\n                  = \"#{group.group_expiration_date}\"\n                td\n                  = link_to \"Remove group\", [@user, group], method: :delete, data: {confirm: 'Are you sure to delete this group?'} if current_user.admin?\n      br\n      - if current_user.admin?\n          = form_tag  add_group_path, method: :post do\n            .row\n              .col\n                = text_field_tag \"group_id\", \"\", class: \"form-control\"\n              .col\n                .row\n                  .col\n                   = submit_tag \"Add Group\", class: \"form-control btn-md btn-primary pull-right\", disabled: true\n                  .col\n      = \"No groups are associated with this user\" if @user.groups.count == 0\n  br\n  .card\n    .card-body\n      - if (current_user.admin? or (current_user.id == @user.id))\n        h6.card-title Public Key\n        = form_tag user_public_key_update_path, method: :post do\n            .row\n              .col\n                = text_area_tag \"public_key\", @user.public_key, class: \"form-contol text-align-top\", style: \"height:200px;width:100%;\", placeholder: \"Your public key here...\"\n            br\n            .row\n              .col-md-3\n                = submit_tag \"Update key\", class: \"form-control btn-md btn-primary\"\n      - else\n        h6.card-title Public Key\n        .row\n          .col\n            = @user.public_key\n\n  br\n  .card\n    .card-body\n      h6.card-title VPN Access\n      table.table.responsive\n        thead\n          tr\n            th=\"Name\"\n            th=\"Hostname\"\n            th=\"IP Address\"\n            th\n\n          tbody\n            - @vpns.each do |vpn|\n              tr\n                td=link_to vpn.name, vpn\n                td=vpn.host_name\n                td=vpn.ip_address\n\n\n      = \"No vpns are associated with this user\" if @vpns.count == 0\n  \n  - if current_user.admin?\n    br\n    .card\n      .card-body\n        h6.card-title SAML Product Access\n        hr\n        - if @user.product_name.nil?\n          = form_tag  user_path, method: :patch do\n            .col-md-6\n              = select_tag :product_name, options_for_select(ENV['PRODUCT_LIST'].split(\",\")), class: \"form-control\"\n            .col-md-2\n              = submit_tag \"Assign\", class: \"form-control btn-md btn-primary\"\n        - else\n          .row\n            .col-md-4.text-primary\n              .col-md-6\n                = @user.product_name\n          br\n            .col-md-4\n            .col-md-8\n              .row\n                = form_tag user_path, method: :patch do\n                  .col-sm-8\n                    = select_tag :product_name, options_for_select(ENV['PRODUCT_LIST'].split(\",\")), class: \"form-control\"\n                  .col-sm-4\n                    = submit_tag \"Modify\", class: \"form-control btn-md btn-primary\"\n\njavascript:\n  $(\"#user-index\").addClass(\"active\");\n"
  },
  {
    "path": "app/views/vpns/_form.html.slim",
    "content": "  = form_for @vpn, :class => 'form-horizontal' do |f|\n    - if @vpn.errors.any?\n      #error_explanation\n        h2 = \"#{pluralize(@vpn.errors.count, \"error\")} prohibited this vpn from being saved:\"\n        ul\n          - @vpn.errors.full_messages.each do |message|\n            li = message\n\n    hr\n    .mb-3\n      label for=\"name\" \n        | Name\n      = f.text_field :name, required: true, class: \"form-control\", placeholder: \"VPN Name\"\n    .mb-3\n      label for=\"host_name\" \n        | Host name\n      = f.text_field :host_name, required: true, class: \"form-control\", placeholder: \"Host name\"\n      .invalid-feedback\n        | Please enter host name.\n    .mb-3\n      label for=\"ip_address\" \n        | IP Address\n      = f.text_field :ip_address, required: true, class: 'form-control', placeholder: \"IP address\"\n      .invalid-feedback\n        | Please enter IP address.\n\n    .mb-4\n      = f.submit \"Save\", class: \"btn btn-primary pull-right\"\n\n"
  },
  {
    "path": "app/views/vpns/edit.html.slim",
    "content": ".container-fluid.col-md-4.col-md-offset-4\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n        = notice\n  br\n  h5 Edit VPN Details\n  = render \"form\"\n\njavascript:\n  $(\"#vpn-index\").addClass(\"active\");\n\n"
  },
  {
    "path": "app/views/vpns/index.html.slim",
    "content": ".container-fluid.col-md-8.col-md-offset-4\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n        = notice\n\n  - if Vpn.administrator? current_user\n    h5 Managed VPNs \n    table.table.responsive\n      thead\n        tr\n          th=\"Name\"\n          th=\"Hostname\"\n          th=\"IP Address\"\n      - Vpn.managed_vpns(current_user).each do |vpn|\n        tbody\n          tr\n            td= link_to vpn.name, vpn\n            td= vpn.host_name\n            td= vpn.ip_address\n\n  h5 \n    b Listing VPNs\n  table.table.responsive\n    thead\n      tr\n        th=\"Name\"\n        th=\"Hostname\"\n        th=\"IP Address\"\n        th\n\n      tbody\n        - @vpns.each do |vpn|\n          tr\n            td= link_to vpn.name, vpn\n            td= vpn.host_name\n            td= vpn.ip_address\n            td= link_to \"Delete\", vpn, method: :delete, data: {confirm: 'Are you sure to remove this vpn from gate?'} if current_user.admin?\n\njavascript:\n  $(\"#vpn-index\").addClass(\"active\");\n"
  },
  {
    "path": "app/views/vpns/new.html.slim",
    "content": ".container-fluid.col-md-4.col-md-offset-4\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n        = notice\n  br\n  h5 Add new VPN\n  = render \"form\"\n\njavascript:\n  $(\"#vpn-index\").addClass(\"active\");\n"
  },
  {
    "path": "app/views/vpns/show.html.slim",
    "content": ".container.container-profile\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n        = notice\n  br\n  .row\n    .col\n      h5.mb-3 \n        = @vpn.name\n    .col\n    .col\n      = link_to \"Manage this vpn\", @vpn.groups.first if @vpn.groups.count > 0\n\n  hr\n    .row\n      .col\n        label for=\"VPN Host : \" \n          b\n            = @vpn.host_name\n      .col\n        label for=\"VPN Host IP : \" \n          b\n            = @vpn.ip_address\n      .col\n        = link_to \"Edit VPN Details\", edit_vpn_path, class: \"form-control btn-sm btn-primary\" if current_user.admin?\n\n  br\n\n  a name=\"dns_hosts\"\n  .card\n    .card-body\n      h6.card-title DNS Hosts\n      hr\n      - @vpn.vpn_domain_name_servers.each do |dns_server|\n        .row\n          .col\n            = \"#{dns_server.server_address}\"\n          .col.append\n            = link_to \"Remove server\", remove_dns_from_vpn_path(@vpn, dns_server), method: :delete, data: {confirm: 'Are you sure to delete this DNS host?'} if current_user.admin?\n      br\n      = \"*This VPN does not have any domain name servers associated with it\" if @vpn.vpn_domain_name_servers.count == 0\n      br\n      hr\n      - if current_user.admin? \n        = form_tag  add_dns_to_vpn_path, method: :post do\n          .row\n            .col\n              = text_field_tag :server_address, \"\", class: \"form-control\", placeholder: 'dns server...', required: true\n            .col\n              = submit_tag \"Add DNS Server\", class: \"form-control btn-md btn-primary\"\n  \n \n  br\n  a name=\"search_domains\"\n  .card\n    .card-body\n      h6.card-title Search Domains\n      hr\n      - @vpn.vpn_search_domains.each do |search_domain|\n        .row\n          .col\n            = \"#{search_domain.search_domain}\"\n          .col.append\n            = link_to \"Remove search domain\", remove_search_domain_from_vpn_path(@vpn, search_domain), method: :delete, data: {confirm: 'Are you sure to delete this search domain string?'} if current_user.admin?\n      br\n      = \"*This VPN does not have any search domain strings\" if @vpn.vpn_search_domains.count == 0\n      br\n      hr\n      - if current_user.admin? \n        = form_tag  add_search_domain_to_vpn_path, method: :post do\n          .row\n            .col\n              = text_field_tag :search_domain, \"\", class: \"form-control\", placeholder: 'dns search string...', required: true\n            .col\n              = submit_tag \"Add search domain\", class: \"form-control btn-md btn-primary\"\n  \n  br\n  a name=\"match_domains\"\n  .card\n    .card-body\n      h6.card-title Supplemental match domain\n      hr\n      - @vpn.vpn_supplemental_match_domains.each do |match_domain|\n        .row\n          .col\n            = \"#{match_domain.supplemental_match_domain}\"\n          .col.append\n            = link_to \"Remove supplemental search domain\", remove_supplemental_match_domain_from_vpn_path(@vpn, match_domain), method: :delete, data: {confirm: 'Are you sure to delete this Supplemental match domain?'} if current_user.admin?\n      br\n      = \"*This VPN does not have any search domain strings\" if @vpn.vpn_search_domains.count == 0\n      br\n      hr\n      - if current_user.admin? \n        = form_tag  add_supplemental_match_domain_to_vpn_path, method: :post do\n          .row\n            .col\n              = text_field_tag :supplemental_match_domain, \"\", class: \"form-control\", placeholder: 'Supplemental match domain...', required: true\n            .col\n              = submit_tag \"Add Supplemental match domain\", class: \"form-control btn-md btn-primary\"\n  \n  br\n  .card\n    .card-body\n      .row  \n        .col\n          h6.card-title VPN Access Group:\n        - @vpn.groups.each do |group|\n          - unless group.deleted?\n            .col\n              = link_to group.name, group\n      = \"No groups are associated with this VPN, Goto Groups page to add VPN\" if @vpn.groups.count == 0\njavascript:\n  $(\"#vpn-index\").addClass(\"active\");\n"
  },
  {
    "path": "bin/bundle",
    "content": "#!/usr/bin/env ruby\nENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)\nload Gem.bin_path('bundler', 'bundle')\n"
  },
  {
    "path": "bin/rails",
    "content": "#!/usr/bin/env ruby\nAPP_PATH = File.expand_path(\"../config/application\", __dir__)\nrequire_relative \"../config/boot\"\nrequire \"rails/commands\"\n"
  },
  {
    "path": "bin/rake",
    "content": "#!/usr/bin/env ruby\nrequire_relative \"../config/boot\"\nrequire \"rake\"\nRake.application.run\n"
  },
  {
    "path": "bin/setup",
    "content": "#!/usr/bin/env ruby\nrequire \"fileutils\"\n\n# path to your application root.\nAPP_ROOT = File.expand_path(\"..\", __dir__)\n\ndef system!(*args)\n  system(*args) || abort(\"\\n== Command #{args} failed ==\")\nend\n\nFileUtils.chdir APP_ROOT do\n  # This script is a way to set up or update your development environment automatically.\n  # This script is idempotent, so that you can run it at any time and get an expectable outcome.\n  # Add necessary setup steps to this file.\n\n  puts \"== Installing dependencies ==\"\n  system! \"gem install bundler --conservative\"\n  system(\"bundle check\") || system!(\"bundle install\")\n\n  # puts \"\\n== Copying sample files ==\"\n  # unless File.exist?(\"config/database.yml\")\n  #   FileUtils.cp \"config/database.yml.sample\", \"config/database.yml\"\n  # end\n\n  puts \"\\n== Preparing database ==\"\n  system! \"bin/rails db:prepare\"\n\n  puts \"\\n== Removing old logs and tempfiles ==\"\n  system! \"bin/rails log:clear tmp:clear\"\n\n  puts \"\\n== Restarting application server ==\"\n  system! \"bin/rails restart\"\nend\n"
  },
  {
    "path": "bin/update",
    "content": "#!/usr/bin/env ruby\nrequire 'pathname'\nrequire 'fileutils'\ninclude FileUtils\n\n# path to your application root.\nAPP_ROOT = Pathname.new File.expand_path('../../', __FILE__)\n\ndef system!(*args)\n  system(*args) || abort(\"\\n== Command #{args} failed ==\")\nend\n\nchdir APP_ROOT do\n  # This script is a way to update your development environment automatically.\n  # Add necessary update steps to this file.\n\n  puts '== Installing dependencies =='\n  system! 'gem install bundler --conservative'\n  system('bundle check') || system!('bundle install')\n\n  puts \"\\n== Updating database ==\"\n  system! 'bin/rails db:migrate'\n\n  puts \"\\n== Removing old logs and tempfiles ==\"\n  system! 'bin/rails log:clear tmp:clear'\n\n  puts \"\\n== Restarting application server ==\"\n  system! 'bin/rails restart'\nend\n"
  },
  {
    "path": "config/application.rb",
    "content": "require_relative \"boot\"\n\nrequire \"rails/all\"\n\n# Require the gems listed in Gemfile, including any gems\n# you've limited to :test, :development, or :production.\nBundler.require(*Rails.groups)\n\nmodule Gate\n  class Application < Rails::Application\n    # Initialize configuration defaults for originally generated Rails version.\n    config.load_defaults 5.0\n\n    # Configuration for the application, engines, and railties goes here.\n    #\n    # These settings can be overridden in specific environments using the files\n    # in config/environments, which are processed later.\n    #\n    # config.time_zone = \"Central Time (US & Canada)\"\n    # config.eager_load_paths << Rails.root.join(\"extras\")\n  end\nend\n"
  },
  {
    "path": "config/boot.rb",
    "content": "ENV[\"BUNDLE_GEMFILE\"] ||= File.expand_path(\"../Gemfile\", __dir__)\n\nrequire \"bundler/setup\" # Set up gems listed in the Gemfile.\n"
  },
  {
    "path": "config/cable.yml",
    "content": "development:\n  adapter: async\n\ntest:\n  adapter: async\n\nproduction:\n  adapter: redis\n  url: redis://localhost:6379/1\n"
  },
  {
    "path": "config/database.yml",
    "content": "default: &default\n  adapter: mysql2\n  pool: 5\n  timeout: 5000\n  host: <%= ENV['GATE_DB_HOST'] %>\n  port: <%= ENV['GATE_DB_PORT'] %>\n  username: <%= ENV['GATE_DB_USER'] %>\n  password: <%= ENV['GATE_DB_PASSWORD'] %>\n\n\ndevelopment:\n  <<: *default\n  database: gate_development\n  host: localhost\n  post: 3306\n  username: gate_development\n  password: password\n  properties:\n    useSSL: false\n\ntest:\n  <<: *default\n  database: gate_test\n  host: localhost\n  post: 3306\n  username: gate_test\n  password: password\n  properties:\n    useSSL: false\n\nintegration:\n  <<: *default\n  database: <%= ENV['GATE_DB_NAME'] %>\n\nproduction:\n  <<: *default\n  pool: 16\n  database: <%= ENV['GATE_DB_NAME'] %>\n"
  },
  {
    "path": "config/environment.rb",
    "content": "# Load the Rails application.\nrequire_relative \"application\"\n\n# Initialize the Rails application.\nRails.application.initialize!\n"
  },
  {
    "path": "config/environments/development.rb",
    "content": "require \"active_support/core_ext/integer/time\"\n\nRails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # In the development environment your application's code is reloaded any time\n  # it changes. This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.cache_classes = false\n\n  # Do not eager load code on boot.\n  config.eager_load = false\n\n  # Show full error reports.\n  config.consider_all_requests_local = true\n\n  # Enable server timing\n  config.server_timing = true\n\n  # Enable/disable caching. By default caching is disabled.\n  # Run rails dev:cache to toggle caching.\n  if Rails.root.join(\"tmp/caching-dev.txt\").exist?\n    config.action_controller.perform_caching = true\n    config.action_controller.enable_fragment_cache_logging = true\n\n    config.cache_store = :memory_store\n    config.public_file_server.headers = {\n      \"Cache-Control\" => \"public, max-age=#{2.days.to_i}\"\n    }\n  else\n    config.action_controller.perform_caching = false\n\n    config.cache_store = :null_store\n  end\n\n  # Store uploaded files on the local file system (see config/storage.yml for options).\n  config.active_storage.service = :local\n\n  # Don't care if the mailer can't send.\n  config.action_mailer.raise_delivery_errors = false\n\n  config.action_mailer.perform_caching = false\n\n  # Print deprecation notices to the Rails logger.\n  config.active_support.deprecation = :log\n\n  # Raise exceptions for disallowed deprecations.\n  config.active_support.disallowed_deprecation = :raise\n\n  # Tell Active Support which deprecation messages to disallow.\n  config.active_support.disallowed_deprecation_warnings = []\n\n  # Raise an error on page load if there are pending migrations.\n  config.active_record.migration_error = :page_load\n\n  # Highlight code that triggered database queries in logs.\n  config.active_record.verbose_query_logs = true\n\n  # Suppress logger output for asset requests.\n  config.assets.quiet = true\n\n  # Raises error for missing translations.\n  # config.i18n.raise_on_missing_translations = true\n\n  # Annotate rendered view with file names.\n  # config.action_view.annotate_rendered_view_with_filenames = true\n\n  # Uncomment if you wish to allow Action Cable access from any origin.\n  # config.action_cable.disable_request_forgery_protection = true\n  config.legacy_connection_handling = false\nend\n"
  },
  {
    "path": "config/environments/integration.rb",
    "content": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # Code is not reloaded between requests.\n  config.cache_classes = true\n\n  # Eager load code on boot. This eager loads most of Rails and\n  # your application in memory, allowing both threaded web servers\n  # and those relying on copy on write to perform better.\n  # Rake tasks automatically ignore this option for performance.\n  config.eager_load = true\n\n  # Full error reports are disabled and caching is turned on.\n  config.consider_all_requests_local       = false\n  config.action_controller.perform_caching = true\n\n  # Enable Rack::Cache to put a simple HTTP cache in front of your application\n  # Add `rack-cache` to your Gemfile before enabling this.\n  # For large-scale production use, consider using a caching reverse proxy like\n  # NGINX, varnish or squid.\n  # config.action_dispatch.rack_cache = true\n\n  # Disable serving static files from the `/public` folder by default since\n  # Apache or NGINX already handles this.\n  config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'] == 'true'\n\n  # Compress JavaScripts and CSS.\n  config.assets.js_compressor = :uglifier\n  # config.assets.css_compressor = :sass\n\n  # Do not fallback to assets pipeline if a precompiled asset is missed.\n  config.assets.compile = false\n\n  # Asset digests allow you to set far-future HTTP expiration dates on all assets,\n  # yet still be able to expire them through the digest params.\n  config.assets.digest = true\n\n  # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb\n\n  # Specifies the header that your server uses for sending files.\n  # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache\n  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  # config.force_ssl = true\n\n  # Use the lowest log level to ensure availability of diagnostic information\n  # when problems arise.\n  config.logger = Logger.new(STDOUT)\n  config.log_level = :DEBUG\n\n  # Prepend all log lines with the following tags.\n  # config.log_tags = [ :subdomain, :uuid ]\n\n  # Use a different logger for distributed setups.\n  # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)\n\n  # Use a different cache store in production.\n  # config.cache_store = :mem_cache_store\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server.\n  # config.action_controller.asset_host = 'http://assets.example.com'\n\n  # Ignore bad email addresses and do not raise email delivery errors.\n  # Set this to true and configure the email server for immediate delivery to raise delivery errors.\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation cannot be found).\n  config.i18n.fallbacks = true\n\n  # Send deprecation notices to registered listeners.\n  config.active_support.deprecation = :notify\n\n  # Use default logging formatter so that PID and timestamp are not suppressed.\n  config.log_formatter = ::Logger::Formatter.new\n\n  # Do not dump schema after migrations.\n  config.active_record.dump_schema_after_migration = false\nend\n"
  },
  {
    "path": "config/environments/production.rb",
    "content": "require \"active_support/core_ext/integer/time\"\n\nRails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # Code is not reloaded between requests.\n  config.cache_classes = true\n\n  # Eager load code on boot. This eager loads most of Rails and\n  # your application in memory, allowing both threaded web servers\n  # and those relying on copy on write to perform better.\n  # Rake tasks automatically ignore this option for performance.\n  config.eager_load = true\n\n  # Full error reports are disabled and caching is turned on.\n  config.consider_all_requests_local       = false\n  config.action_controller.perform_caching = true\n\n  # Ensures that a master key has been made available in either ENV[\"RAILS_MASTER_KEY\"]\n  # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).\n  # config.require_master_key = true\n\n  # Disable serving static files from the `/public` folder by default since\n  # Apache or NGINX already handles this.\n  config.public_file_server.enabled = ENV[\"RAILS_SERVE_STATIC_FILES\"].present?\n\n  # Compress CSS using a preprocessor.\n  # config.assets.css_compressor = :sass\n\n  # Do not fallback to assets pipeline if a precompiled asset is missed.\n  config.assets.compile = false\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server.\n  # config.asset_host = \"http://assets.example.com\"\n\n  # Specifies the header that your server uses for sending files.\n  # config.action_dispatch.x_sendfile_header = \"X-Sendfile\" # for Apache\n  # config.action_dispatch.x_sendfile_header = \"X-Accel-Redirect\" # for NGINX\n\n  # Store uploaded files on the local file system (see config/storage.yml for options).\n  config.active_storage.service = :local\n\n  # Mount Action Cable outside main process or domain.\n  # config.action_cable.mount_path = nil\n  # config.action_cable.url = \"wss://example.com/cable\"\n  # config.action_cable.allowed_request_origins = [ \"http://example.com\", /http:\\/\\/example.*/ ]\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  # config.force_ssl = true\n\n  # Include generic and useful information about system operation, but avoid logging too much\n  # information to avoid inadvertent exposure of personally identifiable information (PII).\n  config.log_level = :info\n\n  # Prepend all log lines with the following tags.\n  config.log_tags = [ :request_id ]\n\n  # Use a different cache store in production.\n  # config.cache_store = :mem_cache_store\n\n  # Use a real queuing backend for Active Job (and separate queues per environment).\n  # config.active_job.queue_adapter     = :resque\n  # config.active_job.queue_name_prefix = \"gate_production\"\n\n  config.action_mailer.perform_caching = false\n\n  # Ignore bad email addresses and do not raise email delivery errors.\n  # Set this to true and configure the email server for immediate delivery to raise delivery errors.\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation cannot be found).\n  config.i18n.fallbacks = true\n\n  # Don't log any deprecations.\n  config.active_support.report_deprecations = false\n\n  # Use default logging formatter so that PID and timestamp are not suppressed.\n  config.log_formatter = ::Logger::Formatter.new\n\n  # Use a different logger for distributed setups.\n  # require \"syslog/logger\"\n  # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new \"app-name\")\n\n  if ENV[\"RAILS_LOG_TO_STDOUT\"].present?\n    logger           = ActiveSupport::Logger.new(STDOUT)\n    logger.formatter = config.log_formatter\n    config.logger    = ActiveSupport::TaggedLogging.new(logger)\n  end\n\n  # Do not dump schema after migrations.\n  config.active_record.dump_schema_after_migration = false\n  config.legacy_connection_handling = false\nend\n"
  },
  {
    "path": "config/environments/test.rb",
    "content": "require \"active_support/core_ext/integer/time\"\n\n# The test environment is used exclusively to run your application's\n# test suite. You never need to work with it otherwise. Remember that\n# your test database is \"scratch space\" for the test suite and is wiped\n# and recreated between test runs. Don't rely on the data there!\n\nRails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # Turn false under Spring and add config.action_view.cache_template_loading = true.\n  config.cache_classes = true\n\n  # Eager loading loads your whole application. When running a single test locally,\n  # this probably isn't necessary. It's a good idea to do in a continuous integration\n  # system, or in some way before deploying your code.\n  config.eager_load = ENV[\"CI\"].present?\n\n  # Configure public file server for tests with Cache-Control for performance.\n  config.public_file_server.enabled = true\n  config.public_file_server.headers = {\n    \"Cache-Control\" => \"public, max-age=#{1.hour.to_i}\"\n  }\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n  config.cache_store = :null_store\n\n  # Raise exceptions instead of rendering exception templates.\n  config.action_dispatch.show_exceptions = false\n\n  # Disable request forgery protection in test environment.\n  config.action_controller.allow_forgery_protection = false\n\n  # Store uploaded files on the local file system in a temporary directory.\n  config.active_storage.service = :test\n\n  config.action_mailer.perform_caching = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  config.action_mailer.delivery_method = :test\n\n  # Print deprecation notices to the stderr.\n  config.active_support.deprecation = :stderr\n\n  # Raise exceptions for disallowed deprecations.\n  config.active_support.disallowed_deprecation = :raise\n\n  # Tell Active Support which deprecation messages to disallow.\n  config.active_support.disallowed_deprecation_warnings = []\n\n  # Raises error for missing translations.\n  # config.i18n.raise_on_missing_translations = true\n\n  # Annotate rendered view with file names.\n  # config.action_view.annotate_rendered_view_with_filenames = true\n  config.active_record.legacy_connection_handling = false\nend\n"
  },
  {
    "path": "config/initializers/application_controller_renderer.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# ApplicationController.renderer.defaults.merge!(\n#   http_host: 'example.org',\n#   https: false\n# )\n"
  },
  {
    "path": "config/initializers/assets.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Version of your assets, change this if you want to expire all your assets.\nRails.application.config.assets.version = \"1.0\"\n\n# Add additional assets to the asset load path.\n# Rails.application.config.assets.paths << Emoji.images_path\n\n# Precompile additional assets.\n# application.js, application.css, and all non-JS/CSS in the app/assets\n# folder are already added.\n# Rails.application.config.assets.precompile += %w( admin.js admin.css )\n"
  },
  {
    "path": "config/initializers/backtrace_silencers.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!\n"
  },
  {
    "path": "config/initializers/content_security_policy.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Define an application-wide content security policy.\n# See the Securing Rails Applications Guide for more information:\n# https://guides.rubyonrails.org/security.html#content-security-policy-header\n\n# Rails.application.configure do\n#   config.content_security_policy do |policy|\n#     policy.default_src :self, :https\n#     policy.font_src    :self, :https, :data\n#     policy.img_src     :self, :https, :data\n#     policy.object_src  :none\n#     policy.script_src  :self, :https\n#     policy.style_src   :self, :https\n#     # Specify URI for violation reports\n#     # policy.report_uri \"/csp-violation-report-endpoint\"\n#   end\n#\n#   # Generate session nonces for permitted importmap and inline scripts\n#   config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }\n#   config.content_security_policy_nonce_directives = %w(script-src)\n#\n#   # Report violations without enforcing the policy.\n#   # config.content_security_policy_report_only = true\n# end\n"
  },
  {
    "path": "config/initializers/cookies_serializer.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Specify a serializer for the signed and encrypted cookie jars.\n# Valid options are :json, :marshal, and :hybrid.\nRails.application.config.action_dispatch.cookies_serializer = :json\n"
  },
  {
    "path": "config/initializers/devise.rb",
    "content": "# Use this hook to configure devise mailer, warden hooks and so forth.\n# Many of these configuration options can be set straight in your model.\nDevise.setup do |config|\n  # The secret key used by Devise. Devise uses this key to generate\n  # random tokens. Changing this key will render invalid all existing\n  # confirmation, reset password and unlock tokens in the database.\n  # Devise will use the `secret_key_base` as its `secret_key`\n  # by default. You can change it below and use your own secret key.\n  # config.secret_key = 'ffd38086f1b74e9ba7fe5876d3536949b4c1e93a50f3ad16f0c50dc1ecb48262c350f819a83ac3482ea51f6aefbd4aa3cd52cd9b6c666ca5f8cbb6f29d760aae'\n\n  config.stretches = Rails.env.test? ? 1 : 10\n  # ==> Mailer Configuration\n  # Configure the e-mail address which will be shown in Devise::Mailer,\n  # note that it will be overwritten if you use your own mailer class\n  # with default \"from\" parameter.\n\n  config.omniauth :google_oauth2, ENV['GATE_OAUTH_CLIENT_ID'], ENV['GATE_OAUTH_CLIENT_SECRET'], { hd: ENV['GATE_HOSTED_DOMAINS'].split(','), site: ENV['GATE_SERVER_URL'] }\n  config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'\n  config.secret_key = ENV['GATE_CONFIG_SECRET']\n\n  # Configure the class responsible to send e-mails.\n  # config.mailer = 'Devise::Mailer'\n\n  # Configure the parent class responsible to send e-mails.\n  # config.parent_mailer = 'ActionMailer::Base'\n\n  # ==> ORM configuration\n  # Load and configure the ORM. Supports :active_record (default) and\n  # :mongoid (bson_ext recommended) by default. Other ORMs may be\n  # available as additional gems.\n  require 'devise/orm/active_record'\n\n  # ==> Configuration for any authentication mechanism\n  # Configure which keys are used when authenticating a user. The default is\n  # just :email. You can configure it to use [:username, :subdomain], so for\n  # authenticating a user, both parameters are required. Remember that those\n  # parameters are used only when authenticating and not when retrieving from\n  # session. If you need permissions, you should implement that in a before filter.\n  # You can also supply a hash where the value is a boolean determining whether\n  # or not authentication should be aborted when the value is not present.\n  # config.authentication_keys = [:email]\n\n  # Configure parameters from the request object used for authentication. Each entry\n  # given should be a request method and it will automatically be passed to the\n  # find_for_authentication method and considered in your model lookup. For instance,\n  # if you set :request_keys to [:subdomain], :subdomain will be used on authentication.\n  # The same considerations mentioned for authentication_keys also apply to request_keys.\n  # config.request_keys = []\n\n  # Configure which authentication keys should be case-insensitive.\n  # These keys will be downcased upon creating or modifying a user and when used\n  # to authenticate or find a user. Default is :email.\n  config.case_insensitive_keys = [:email]\n\n  # Configure which authentication keys should have whitespace stripped.\n  # These keys will have whitespace before and after removed upon creating or\n  # modifying a user and when used to authenticate or find a user. Default is :email.\n  config.strip_whitespace_keys = [:email]\n\n  # Tell if authentication through request.params is enabled. True by default.\n  # It can be set to an array that will enable params authentication only for the\n  # given strategies, for example, `config.params_authenticatable = [:database]` will\n  # enable it only for database (email + password) authentication.\n  # config.params_authenticatable = true\n\n  # Tell if authentication through HTTP Auth is enabled. False by default.\n  # It can be set to an array that will enable http authentication only for the\n  # given strategies, for example, `config.http_authenticatable = [:database]` will\n  # enable it only for database authentication. The supported strategies are:\n  # :database      = Support basic authentication with authentication key + password\n  # config.http_authenticatable = false\n\n  # If 401 status code should be returned for AJAX requests. True by default.\n  # config.http_authenticatable_on_xhr = true\n\n  # The realm used in Http Basic Authentication. 'Application' by default.\n  # config.http_authentication_realm = 'Application'\n\n  # It will change confirmation, password recovery and other workflows\n  # to behave the same regardless if the e-mail provided was right or wrong.\n  # Does not affect registerable.\n  # config.paranoid = true\n\n  # By default Devise will store the user in session. You can skip storage for\n  # particular strategies by setting this option.\n  # Notice that if you are skipping storage for all authentication paths, you\n  # may want to disable generating routes to Devise's sessions controller by\n  # passing skip: :sessions to `devise_for` in your config/routes.rb\n  config.skip_session_storage = [:http_auth]\n\n  # By default, Devise cleans up the CSRF token on authentication to\n  # avoid CSRF token fixation attacks. This means that, when using AJAX\n  # requests for sign in and sign up, you need to get a new CSRF token\n  # from the server. You can disable this option at your own risk.\n  # config.clean_up_csrf_token_on_authentication = true\n\n  # ==> Configuration for :database_authenticatable\n  # For bcrypt, this is the cost for hashing the password and defaults to 11. If\n  # using other algorithms, it sets how many times you want the password to be hashed.\n  #\n  # Limiting the stretches to just one in testing will increase the performance of\n  # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use\n  # a value less than 10 in other environments. Note that, for bcrypt (the default\n  # algorithm), the cost increases exponentially with the number of stretches (e.g.\n  # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation).\n  config.stretches = Rails.env.test? ? 1 : 11\n\n  # Set up a pepper to generate the hashed password.\n  # config.pepper = '16f08c87da9835ae3ac2cc84ba3ce339d6820771cfb4dfdd79360036bbace40ee9b74a691f26aa2eeb17cbc449cfda195e09874a792df258a2abe9d696f4a98b'\n\n  # Send a notification email when the user's password is changed\n  # config.send_password_change_notification = false\n\n  # ==> Configuration for :confirmable\n  # A period that the user is allowed to access the website even without\n  # confirming their account. For instance, if set to 2.days, the user will be\n  # able to access the website for two days without confirming their account,\n  # access will be blocked just in the third day. Default is 0.days, meaning\n  # the user cannot access the website without confirming their account.\n  # config.allow_unconfirmed_access_for = 2.days\n\n  # A period that the user is allowed to confirm their account before their\n  # token becomes invalid. For example, if set to 3.days, the user can confirm\n  # their account within 3 days after the mail was sent, but on the fourth day\n  # their account can't be confirmed with the token any more.\n  # Default is nil, meaning there is no restriction on how long a user can take\n  # before confirming their account.\n  # config.confirm_within = 3.days\n\n  # If true, requires any email changes to be confirmed (exactly the same way as\n  # initial account confirmation) to be applied. Requires additional unconfirmed_email\n  # db field (see migrations). Until confirmed, new email is stored in\n  # unconfirmed_email column, and copied to email column on successful confirmation.\n  config.reconfirmable = true\n\n  # Defines which key will be used when confirming an account\n  # config.confirmation_keys = [:email]\n\n  # ==> Configuration for :rememberable\n  # The time the user will be remembered without asking for credentials again.\n  # config.remember_for = 2.weeks\n\n  # Invalidates all the remember me tokens when the user signs out.\n  config.expire_all_remember_me_on_sign_out = true\n\n  # If true, extends the user's remember period when remembered via cookie.\n  # config.extend_remember_period = false\n\n  # Options to be passed to the created cookie. For instance, you can set\n  # secure: true in order to force SSL only cookies.\n  # config.rememberable_options = {}\n\n  # ==> Configuration for :validatable\n  # Range for password length.\n  config.password_length = 6..128\n\n  # Email regex used to validate email formats. It simply asserts that\n  # one (and only one) @ exists in the given string. This is mainly\n  # to give user feedback and not to assert the e-mail validity.\n  config.email_regexp = /\\A[^@]+@[^@]+\\z/\n\n    # ==> Configuration for :timeoutable\n    # The time you want to timeout the user session without activity. After this\n    # time the user will be asked for credentials again. Default is 30 minutes.\n    # config.timeout_in = 30.minutes\n\n    # ==> Configuration for :lockable\n    # Defines which strategy will be used to lock an account.\n    # :failed_attempts = Locks an account after a number of failed attempts to sign in.\n    # :none            = No lock strategy. You should handle locking by yourself.\n    # config.lock_strategy = :failed_attempts\n\n    # Defines which key will be used when locking and unlocking an account\n    # config.unlock_keys = [:email]\n\n    # Defines which strategy will be used to unlock an account.\n    # :email = Sends an unlock link to the user email\n    # :time  = Re-enables login after a certain amount of time (see :unlock_in below)\n    # :both  = Enables both strategies\n    # :none  = No unlock strategy. You should handle unlocking by yourself.\n    # config.unlock_strategy = :both\n\n    # Number of authentication tries before locking an account if lock_strategy\n    # is failed attempts.\n    # config.maximum_attempts = 20\n\n    # Time interval to unlock the account if :time is enabled as unlock_strategy.\n    # config.unlock_in = 1.hour\n\n    # Warn on the last attempt before the account is locked.\n    # config.last_attempt_warning = true\n\n    # ==> Configuration for :recoverable\n    #\n    # Defines which key will be used when recovering the password for an account\n    # config.reset_password_keys = [:email]\n\n    # Time interval you can reset your password with a reset password key.\n    # Don't put a too small interval or your users won't have the time to\n    # change their passwords.\n    config.reset_password_within = 6.hours\n\n  # When set to false, does not sign a user in automatically after their password is\n  # reset. Defaults to true, so a user is signed in automatically after a reset.\n  # config.sign_in_after_reset_password = true\n\n  # ==> Configuration for :encryptable\n  # Allow you to use another hashing or encryption algorithm besides bcrypt (default).\n  # You can use :sha1, :sha512 or algorithms from others authentication tools as\n  # :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20\n  # for default behavior) and :restful_authentication_sha1 (then you should set\n  # stretches to 10, and copy REST_AUTH_SITE_KEY to pepper).\n  #\n  # Require the `devise-encryptable` gem when using anything other than bcrypt\n  # config.encryptor = :sha512\n\n  # ==> Scopes configuration\n  # Turn scoped views on. Before rendering \"sessions/new\", it will first check for\n  # \"users/sessions/new\". It's turned off by default because it's slower if you\n  # are using only default views.\n  # config.scoped_views = false\n\n  # Configure the default scope given to Warden. By default it's the first\n  # devise role declared in your routes (usually :user).\n  # config.default_scope = :user\n\n  # Set this configuration to false if you want /users/sign_out to sign out\n  # only the current scope. By default, Devise signs out all scopes.\n  # config.sign_out_all_scopes = true\n\n  # ==> Navigation configuration\n  # Lists the formats that should be treated as navigational. Formats like\n  # :html, should redirect to the sign in page when the user does not have\n  # access, but formats like :xml or :json, should return 401.\n  #\n  # If you have any extra navigational formats, like :iphone or :mobile, you\n  # should add them to the navigational formats lists.\n  #\n  # The \"*/*\" below is required to match Internet Explorer requests.\n  # config.navigational_formats = ['*/*', :html]\n\n  # The default HTTP method used to sign out a resource. Default is :delete.\n  config.sign_out_via = :delete\n\n  # ==> OmniAuth\n  # Add a new OmniAuth provider. Check the wiki for more information on setting\n  # up on your models and hooks.\n  # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'\n\n  # ==> Warden configuration\n  # If you want to use other strategies, that are not supported by Devise, or\n  # change the failure app, you can configure them inside the config.warden block.\n  #\n  # config.warden do |manager|\n  #   manager.intercept_401 = false\n  #   manager.default_strategies(scope: :user).unshift :some_external_strategy\n  # end\n\n  # ==> Mountable engine configurations\n  # When using Devise inside an engine, let's call it `MyEngine`, and this engine\n  # is mountable, there are some extra configurations to be taken into account.\n  # The following options are available, assuming the engine is mounted as:\n  #\n  #     mount MyEngine, at: '/my_engine'\n  #\n  # The router that invoked `devise_for`, in the example above, would be:\n  # config.router_name = :my_engine\n  #\n  # When using OmniAuth, Devise cannot automatically set OmniAuth path,\n  # so you need to do it manually. For the users scope, it would be:\n  # config.omniauth_path_prefix = '/my_engine/users/auth'\n  end\n"
  },
  {
    "path": "config/initializers/dotenv.rb",
    "content": "begin\n  Dotenv.require_keys('GATE_DB_HOST',\n    'GATE_DB_PORT',\n    'GATE_DB_USER',\n    'GATE_DB_PASSWORD',\n    'CACHE_HOST',\n    'CACHE_PORT',\n    'GATE_HOSTED_DOMAINS',\n    'GATE_HOSTED_DOMAIN')\nrescue => exception\n  puts exception.to_s\n  exit(-1)\nend\n"
  },
  {
    "path": "config/initializers/filter_parameter_logging.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Configure parameters to be filtered from the log file. Use this to limit dissemination of\n# sensitive information. See the ActiveSupport::ParameterFilter documentation for supported\n# notations and behaviors.\nRails.application.config.filter_parameters += [\n  :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn\n]\n"
  },
  {
    "path": "config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format. Inflections\n# are locale specific, and you may define rules for as many different\n# locales as you wish. All of these examples are active by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.plural /^(ox)$/i, \"\\\\1en\"\n#   inflect.singular /^(ox)en/i, \"\\\\1\"\n#   inflect.irregular \"person\", \"people\"\n#   inflect.uncountable %w( fish sheep )\n# end\n\n# These inflection rules are supported but not enabled by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.acronym \"RESTful\"\n# end\n"
  },
  {
    "path": "config/initializers/mime_types.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n"
  },
  {
    "path": "config/initializers/new_framework_defaults.rb",
    "content": "# Be sure to restart your server when you modify this file.\n#\n# This file contains migration options to ease your Rails 5.0 upgrade.\n#\n# Once upgraded flip defaults one by one to migrate to the new default.\n#\n# Read the Rails 5.0 release notes for more info on each option.\n\n# Enable per-form CSRF tokens. Previous versions had false.\n#Rails.application.config.action_controller.per_form_csrf_tokens = false\n\n# Enable origin-checking CSRF mitigation. Previous versions had false.\n#Rails.application.config.action_controller.forgery_protection_origin_check = false\n\n# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`.\n# Previous versions had false.\n#ActiveSupport.to_time_preserves_timezone = false\n\n# Require `belongs_to` associations by default. Previous versions had false.\n#Rails.application.config.active_record.belongs_to_required_by_default = false\n\n"
  },
  {
    "path": "config/initializers/new_framework_defaults_7_0.rb",
    "content": "# Be sure to restart your server when you modify this file.\n#\n# This file eases your Rails 7.0 framework defaults upgrade.\n#\n# Uncomment each configuration one by one to switch to the new default.\n# Once your application is ready to run with all new defaults, you can remove\n# this file and set the `config.load_defaults` to `7.0`.\n#\n# Read the Guide for Upgrading Ruby on Rails for more info on each option.\n# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html\n\n# `button_to` view helper will render `<button>` element, regardless of whether\n# or not the content is passed as the first argument or as a block.\n# Rails.application.config.action_view.button_to_generates_button_tag = true\n\n# `stylesheet_link_tag` view helper will not render the media attribute by default.\n# Rails.application.config.action_view.apply_stylesheet_media_default = false\n\n# Change the digest class for the key generators to `OpenSSL::Digest::SHA256`.\n# Changing this default means invalidate all encrypted messages generated by\n# your application and, all the encrypted cookies. Only change this after you\n# rotated all the messages using the key rotator.\n#\n# See upgrading guide for more information on how to build a rotator.\n# https://guides.rubyonrails.org/v7.0/upgrading_ruby_on_rails.html\n# Rails.application.config.active_support.key_generator_hash_digest_class = OpenSSL::Digest::SHA256\n\n# Change the digest class for ActiveSupport::Digest.\n# Changing this default means that for example Etags change and\n# various cache keys leading to cache invalidation.\n# Rails.application.config.active_support.hash_digest_class = OpenSSL::Digest::SHA256\n\n# Don't override ActiveSupport::TimeWithZone.name and use the default Ruby\n# implementation.\n# Rails.application.config.active_support.remove_deprecated_time_with_zone_name = true\n\n# Change the format of the cache entry.\n# Changing this default means that all new cache entries added to the cache\n# will have a different format that is not supported by Rails 6.1 applications.\n# Only change this value after your application is fully deployed to Rails 7.0\n# and you have no plans to rollback.\n# Rails.application.config.active_support.cache_format_version = 7.0\n\n# Calls `Rails.application.executor.wrap` around test cases.\n# This makes test cases behave closer to an actual request or job.\n# Several features that are normally disabled in test, such as Active Record query cache\n# and asynchronous queries will then be enabled.\n# Rails.application.config.active_support.executor_around_test_case = true\n\n# Define the isolation level of most of Rails internal state.\n# If you use a fiber based server or job processor, you should set it to `:fiber`.\n# Otherwise the default of `:thread` if preferable.\n# Rails.application.config.active_support.isolation_level = :thread\n\n# Set both the `:open_timeout` and `:read_timeout` values for `:smtp` delivery method.\n# Rails.application.config.action_mailer.smtp_timeout = 5\n\n# The ActiveStorage video previewer will now use scene change detection to generate\n# better preview images (rather than the previous default of using the first frame\n# of the video).\n# Rails.application.config.active_storage.video_preview_arguments =\n#   \"-vf 'select=eq(n\\\\,0)+eq(key\\\\,1)+gt(scene\\\\,0.015),loop=loop=-1:size=2,trim=start_frame=1' -frames:v 1 -f image2\"\n\n# Automatically infer `inverse_of` for associations with a scope.\n# Rails.application.config.active_record.automatic_scope_inversing = true\n\n# Raise when running tests if fixtures contained foreign key violations\n# Rails.application.config.active_record.verify_foreign_keys_for_fixtures = true\n\n# Disable partial inserts.\n# This default means that all columns will be referenced in INSERT queries\n# regardless of whether they have a default or not.\n# Rails.application.config.active_record.partial_inserts = false\n#\n# Protect from open redirect attacks in `redirect_back_or_to` and `redirect_to`.\n# Rails.application.config.action_controller.raise_on_open_redirects = true\n\n# Change the variant processor for Active Storage.\n# Changing this default means updating all places in your code that\n# generate variants to use image processing macros and ruby-vips\n# operations. See the upgrading guide for detail on the changes required.\n# The `:mini_magick` option is not deprecated; it's fine to keep using it.\n# Rails.application.config.active_storage.variant_processor = :vips\n\n# If you're upgrading and haven't set `cookies_serializer` previously, your cookie serializer\n# was `:marshal`. Convert all cookies to JSON, using the `:hybrid` formatter.\n#\n# If you're confident all your cookies are JSON formatted, you can switch to the `:json` formatter.\n#\n# Continue to use `:marshal` for backward-compatibility with old cookies.\n#\n# If you have configured the serializer elsewhere, you can remove this.\n#\n# See https://guides.rubyonrails.org/action_controller_overview.html#cookies for more information.\n# Rails.application.config.action_dispatch.cookies_serializer = :hybrid\n\n# Enable parameter wrapping for JSON.\n# Previously this was set in an initializer. It's fine to keep using that initializer if you've customized it.\n# To disable parameter wrapping entirely, set this config to `false`.\n# Rails.application.config.action_controller.wrap_parameters_by_default = true\n\n# Specifies whether generated namespaced UUIDs follow the RFC 4122 standard for namespace IDs provided as a\n# `String` to `Digest::UUID.uuid_v3` or `Digest::UUID.uuid_v5` method calls.\n#\n# See https://guides.rubyonrails.org/configuring.html#config-active-support-use-rfc4122-namespaced-uuids for\n# more information.\n# Rails.application.config.active_support.use_rfc4122_namespaced_uuids = true\n\n# Change the default headers to disable browsers' flawed legacy XSS protection.\n# Rails.application.config.action_dispatch.default_headers = {\n#   \"X-Frame-Options\" => \"SAMEORIGIN\",\n#   \"X-XSS-Protection\" => \"0\",\n#   \"X-Content-Type-Options\" => \"nosniff\",\n#   \"X-Download-Options\" => \"noopen\",\n#   \"X-Permitted-Cross-Domain-Policies\" => \"none\",\n#   \"Referrer-Policy\" => \"strict-origin-when-cross-origin\"\n# }\n"
  },
  {
    "path": "config/initializers/permissions_policy.rb",
    "content": "# Define an application-wide HTTP permissions policy. For further\n# information see https://developers.google.com/web/updates/2018/06/feature-policy\n#\n# Rails.application.config.permissions_policy do |f|\n#   f.camera      :none\n#   f.gyroscope   :none\n#   f.microphone  :none\n#   f.usb         :none\n#   f.fullscreen  :self\n#   f.payment     :self, \"https://secure.example.com\"\n# end\n"
  },
  {
    "path": "config/initializers/session_store.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\nRails.application.config.session_store :active_record_store, key: '_gate_session'\n"
  },
  {
    "path": "config/initializers/wrap_parameters.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# This file contains settings for ActionController::ParamsWrapper which\n# is enabled by default.\n\n# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.\nActiveSupport.on_load(:action_controller) do\n  wrap_parameters format: [:json]\nend\n\n# To enable root element in JSON for ActiveRecord objects.\n# ActiveSupport.on_load(:active_record) do\n#   self.include_root_in_json = true\n# end\n"
  },
  {
    "path": "config/locales/devise.en.yml",
    "content": "# Additional translations at https://github.com/plataformatec/devise/wiki/I18n\n\nen:\n  devise:\n    confirmations:\n      confirmed: \"Your email address has been successfully confirmed.\"\n      send_instructions: \"You will receive an email with instructions for how to confirm your email address in a few minutes.\"\n      send_paranoid_instructions: \"If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes.\"\n    failure:\n      already_authenticated: \"You are already signed in.\"\n      inactive: \"Your account is not activated yet.\"\n      invalid: \"Invalid %{authentication_keys} or password.\"\n      locked: \"Your account is locked.\"\n      last_attempt: \"You have one more attempt before your account is locked.\"\n      not_found_in_database: \"Invalid %{authentication_keys} or password.\"\n      timeout: \"Your session expired. Please sign in again to continue.\"\n      unauthenticated: \"You need to sign in or sign up before continuing.\"\n      unconfirmed: \"You have to confirm your email address before continuing.\"\n    mailer:\n      confirmation_instructions:\n        subject: \"Confirmation instructions\"\n      reset_password_instructions:\n        subject: \"Reset password instructions\"\n      unlock_instructions:\n        subject: \"Unlock instructions\"\n      password_change:\n        subject: \"Password Changed\"\n    omniauth_callbacks:\n      failure: \"Could not authenticate you from %{kind} because \\\"%{reason}\\\".\"\n      success: \"Successfully authenticated from %{kind} account.\"\n    passwords:\n      no_token: \"You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided.\"\n      send_instructions: \"You will receive an email with instructions on how to reset your password in a few minutes.\"\n      send_paranoid_instructions: \"If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes.\"\n      updated: \"Your password has been changed successfully. You are now signed in.\"\n      updated_not_active: \"Your password has been changed successfully.\"\n    registrations:\n      destroyed: \"Bye! Your account has been successfully cancelled. We hope to see you again soon.\"\n      signed_up: \"Welcome! You have signed up successfully.\"\n      signed_up_but_inactive: \"You have signed up successfully. However, we could not sign you in because your account is not yet activated.\"\n      signed_up_but_locked: \"You have signed up successfully. However, we could not sign you in because your account is locked.\"\n      signed_up_but_unconfirmed: \"A message with a confirmation link has been sent to your email address. Please follow the link to activate your account.\"\n      update_needs_confirmation: \"You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address.\"\n      updated: \"Your account has been updated successfully.\"\n    sessions:\n      signed_in: \"Signed in successfully.\"\n      signed_out: \"Signed out successfully.\"\n      already_signed_out: \"Signed out successfully.\"\n    unlocks:\n      send_instructions: \"You will receive an email with instructions for how to unlock your account in a few minutes.\"\n      send_paranoid_instructions: \"If your account exists, you will receive an email with instructions for how to unlock it in a few minutes.\"\n      unlocked: \"Your account has been unlocked successfully. Please sign in to continue.\"\n  errors:\n    messages:\n      already_confirmed: \"was already confirmed, please try signing in\"\n      confirmation_period_expired: \"needs to be confirmed within %{period}, please request a new one\"\n      expired: \"has expired, please request a new one\"\n      not_found: \"not found\"\n      not_locked: \"was not locked\"\n      not_saved:\n        one: \"1 error prohibited this %{resource} from being saved:\"\n        other: \"%{count} errors prohibited this %{resource} from being saved:\"\n"
  },
  {
    "path": "config/locales/en.bootstrap.yml",
    "content": "# Sample localization file for English. Add more files in this directory for other locales.\n# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.\n\nen:\n  breadcrumbs:\n    application:\n      root: \"Index\"\n    pages:\n      pages: \"Pages\"\n  helpers:\n    actions: \"Actions\"\n    links:\n      back: \"Back\"\n      cancel: \"Cancel\"\n      confirm: \"Are you sure?\"\n      destroy: \"Delete\"\n      new: \"New\"\n      edit: \"Edit\"\n    titles:\n      edit: \"Edit %{model}\"\n      save: \"Save %{model}\"\n      new: \"New %{model}\"\n      delete: \"Delete %{model}\"\n"
  },
  {
    "path": "config/locales/en.yml",
    "content": "# Files in the config/locales directory are used for internationalization\n# and are automatically loaded by Rails. If you want to use locales other\n# than English, add the necessary files in this directory.\n#\n# To use the locales, use `I18n.t`:\n#\n#     I18n.t 'hello'\n#\n# In views, this is aliased to just `t`:\n#\n#     <%= t('hello') %>\n#\n# To use a different locale, set it with `I18n.locale`:\n#\n#     I18n.locale = :es\n#\n# This would use the information in config/locales/es.yml.\n#\n# To learn more, please read the Rails Internationalization guide\n# available at http://guides.rubyonrails.org/i18n.html.\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "config/newrelic.yml",
    "content": "common: &default_settings\n  # Required license key associated with your New Relic account.\n  license_key: <%= ENV['NEWRELIC_LICENSE_KEY'] %>\n\n  # Your application name. Renaming here affects where data displays in New\n  # Relic.  For more details, see https://docs.newrelic.com/docs/apm/new-relic-apm/maintenance/renaming-applications\n  app_name: <%= ENV['NEWRELIC_APP_NAME'] %>\n\n  # To disable the agent regardless of other settings, uncomment the following:\n  agent_enabled: <%= ENV['NEWRELIC_AGENT_ENABLED'] == 'true' %>\n\n  # Logging level for log/newrelic_agent.log\n  log_level: info\n\n\n# Environment-specific settings are in this section.\n# RAILS_ENV or RACK_ENV (as appropriate) is used to determine the environment.\n# If your application has other named environments, configure them here.\ndevelopment:\n  <<: *default_settings\n  app_name: <%= ENV['NEWRELIC_APP_NAME'] + \"(Development)\" %>\n\ntest:\n  <<: *default_settings\n  # It doesn't make sense to report to New Relic from automated test runs.\n  monitor_mode: false\n\nproduction:\n  <<: *default_settings\n"
  },
  {
    "path": "config/puma.rb",
    "content": "# Puma can serve each request in a thread from an internal thread pool.\n# The `threads` method setting takes two numbers a minimum and maximum.\n# Any libraries that use thread pools should be configured to match\n# the maximum value specified for Puma. Default is set to 5 threads for minimum\n# and maximum, this matches the default thread size of Active Record.\n#\nthreads_count = ENV.fetch(\"RAILS_MAX_THREADS\") { 5 }.to_i\nthreads threads_count, threads_count\n\n# Specifies the `port` that Puma will listen on to receive requests, default is 3000.\n#\nport        ENV.fetch(\"PORT\") { 3000 }\n\n# Specifies the `environment` that Puma will run in.\n#\nenvironment ENV.fetch(\"RAILS_ENV\") { \"development\" }\n\n# Specifies the number of `workers` to boot in clustered mode.\n# Workers are forked webserver processes. If using threads and workers together\n# the concurrency of the application would be max `threads` * `workers`.\n# Workers do not work on JRuby or Windows (both of which do not support\n# processes).\n#\n# workers ENV.fetch(\"WEB_CONCURRENCY\") { 2 }\n\n# Use the `preload_app!` method when specifying a `workers` number.\n# This directive tells Puma to first boot the application and load code\n# before forking the application. This takes advantage of Copy On Write\n# process behavior so workers use less memory. If you use this option\n# you need to make sure to reconnect any threads in the `on_worker_boot`\n# block.\n#\n# preload_app!\n\n# The code in the `on_worker_boot` will be called if you are using\n# clustered mode by specifying a number of `workers`. After each worker\n# process is booted this block will be run, if you are using `preload_app!`\n# option you will want to use this block to reconnect to any threads\n# or connections that may have been created at application boot, Ruby\n# cannot share connections between processes.\n#\n# on_worker_boot do\n#   ActiveRecord::Base.establish_connection if defined?(ActiveRecord)\n# end\n\n# Allow puma to be restarted by `rails restart` command.\nplugin :tmp_restart\n"
  },
  {
    "path": "config/redis.yml",
    "content": "default: &default\n  host: <%= ENV['CACHE_HOST'] %>\n  port: <%= ENV['CACHE_PORT'] %>\n  limit: 20\n\ndevelopment:\n  <<: *default\n\ntest:\n  <<: *default\n\nintegration:\n  <<: *default\n\nproduction:\n  <<: *default\n"
  },
  {
    "path": "config/routes.rb",
    "content": "Rails.application.routes.draw do\n  devise_for :users, :controllers => { :omniauth_callbacks => \"users/omniauth_callbacks\" }, :path_names => { :sign_in => 'login', :sign_out => 'logout' }\n\n  scope '/:slug/:app/saml' do\n    get '/auth' => 'saml_idp#new'\n    get '/metadata' => 'saml_idp#show'\n    post '/auth' => 'saml_idp#create'\n    match '/logout' => 'saml_idp#logout', via: [:get, :post, :delete]\n  end\n\n  devise_scope :user do\n    authenticated :user do\n      resources :organisations, except: %i(destroy) do\n        get 'setup_saml', action: :setup_saml\n        get 'config_saml_app/:app_name', action: :config_saml_app, as: 'config_saml_app'\n        post 'config_saml_app/:app_name', action: :save_config_saml_app, as: :save_config_saml_app\n        post 'config_saml_app/:app_name/add_user', action: :add_user_saml_app, as: :add_user_saml_app\n        delete 'config_saml_app/:app_name', action: :remove_user_saml_app, as: :remove_user_saml_app\n      end\n    end\n\n    post '/users/sign_in' => 'users/auth#log_in', as: 'user_sign_in'\n    delete \"/users/sign_out\" => \"devise/sessions#destroy\"\n    match 'download_vpn', to: 'profile#download_vpn', via: :get, format: :html\n    match 'download_vpn_for_ios_and_mac', to: 'profile#download_vpn_for_ios_and_mac', via: :get, format: :html\n    match 'download_vpn/:id', to: 'profile#download_vpn_for_user', via: :get, format: :html\n  end\n\n  match 'profile', to: 'profile#show', via: :get, format: :html\n  match 'profile/verify', to: 'profile#verify', via: :get, format: :text\n  match 'profile/authenticate', to: 'profile#authenticate', via: :get, format: :text\n  match 'profile/authenticate_cas', to: 'profile#authenticate_cas', via: :post, format: :json\n  match 'profile/authenticate_pam', to: 'profile#authenticate_pam', via: :get, format: :text\n  match 'profile/authenticate_ms_chap', to: 'profile#authenticate_ms_chap', via: :get, format: :text\n  match 'profile/admin', to: 'profile#admin', via: :get\n  match 'profile/user_admin', to: 'profile#user_admin', via: :get\n  get 'profile/list' => 'profile#list', as: 'profile_list'\n\n  get 'profile/:id' => 'users#index', as: 'user_profile'\n  post 'profile/:id' => 'profile#update', as: 'user_update'\n  get 'profile/:id/edit' => 'profile#user_edit', as: 'user_edit'\n  post 'profile/:id/public_key' => 'profile#public_key_update', as: 'user_public_key_update'\n  constraints(name: /[^\\/]+/) do\n    get 'profile/:name/key' => 'profile#public_key', as: 'user_public_key', format: :text\n    get 'profile/:name/id' => 'profile#user_id', as: 'user_public_id', format: :text\n  end\n\n  post 'profile/:id/host' => 'host#add_host', as: 'add_host'\n  delete 'profile/:user_id/host/:id' => 'host#delete_host', as: 'user_host'\n\n  post 'profile/:id/vpn' => 'profile#add_vpn_group_association', as: 'add_vpn_group_user_association'\n  delete 'profile/:user_id/vpn/:id' => 'profile#delete_vpn_group_association', as: 'delete_vpn_group_association'\n\n  get '/regenerate_authentication' => 'profile#regen_auth', as: 'regenerate_authentication', format: :html\n  #Group Function\n\n  post 'profile/:id/group' => 'groups#add_group', as: 'add_group'\n  delete 'profile/:user_id/group/:id' => 'groups#delete_group', as: 'user_group'\n  get 'nss/group' => 'nss#group', as: 'nss_group', format: :json\n  get 'nss/shadow' => 'nss#shadow', as: 'nss_shadow', format: :json\n  get 'nss/passwd' => 'nss#passwd', as: 'nss_passwd', format: :json\n  get 'nss/host' => 'nss#host', as: 'nss_host', format: :json\n  post 'nss/host' => 'nss#add_host', as: 'add_nss_host', format: :json\n  get 'nss/user/groups' => 'nss#groups_list', as: 'profile_groups_list', format: :json\n\n  #Specific Group routes\n\n  post 'groups/:id/add_user' => 'groups#add_user', as: 'add_user_to_group'\n  delete 'groups/:id/user/:user_id' => 'groups#delete_user', as: 'group_user'\n  post 'groups/:id/add_vpn' => 'groups#add_vpn', as: 'add_vpn_to_group'\n  delete 'groups/:id/vpn/:vpn_id' => 'groups#delete_vpn', as: 'group_vpn'\n  post 'groups/:id/add_machine' => 'groups#add_machine', as: 'add_machine_to_group'\n  post 'groups/:id/add_admin' => 'groups#add_admin', as: 'add_admin_to_group'\n  delete 'groups/:id/remove_admin/:group_admin_id' => 'groups#remove_admin', as: 'group_group_admin'\n  delete 'groups/:id/host_machine/:host_machine_id' => 'groups#delete_machine', as: 'group_host_machine'\n  delete 'host_machines/:id/groups/:group_id' => 'host_machines#delete_group', as: 'host_machine_group'\n  post 'host_machines/:id/add_group' => 'host_machines#add_group', as: 'add_group_to_machine'\n  get 'groups/search' => 'groups#search', format: :json\n\n  # api routes\n  namespace :api do\n    namespace :v1 do\n      post 'users' => 'users#create', as: 'add_users_api', format: :json\n      patch 'users/:id/deactivate' => 'users#deactivate', as: 'deactivate_user', format: :json, :constraints => { format: 'json' }\n      get 'users/profile' => 'users#show', format: :json, :constraints => { format: 'json' }\n      post 'users/profile' => 'users#update', format: :json, :constraints => { format: 'json' }\n      post 'endpoints' => 'endpoints#create', format: :json, :constraints => { format: 'json' }\n      post 'endpoints/:id/add_group' => 'endpoints#add_group', format: :json, constraints: { format: 'json' }\n      post 'groups/:id/users' => 'groups#add_user', format: :json, constraints: { format: 'json' }\n\n      resources :groups, only: [:create], format: :json\n      resources :vpns, only: [:create], format: :json do\n        member do\n          post 'assign_group'\n        end\n      end\n    end\n  end\n\n  root 'home#index'\n\n  get '/admin' => 'admin#index'\n\n  resources :host_machines do\n    collection do\n      get 'search', format: :json\n    end\n  end\n\n  resources :groups\n  #resources :groups do\n  #  resources :members\n  #  resources :group_admins\n  #end\n  resources :users do\n    collection do\n      get 'search', format: :json\n    end\n    member do\n      get 'regenerate_token'\n    end\n  end\n\n  resource :ping, only: [:show]\n\n  resources :vpns do\n    collection do\n      get 'search', format: :json\n    end\n  end\n  resources :api_resources do\n    collection do\n      get 'search', format: :json\n    end\n    member do\n      get 'regenerate_access_key'\n    end\n  end\n\n  get \"api_resource/authenticate/:access_key/:access_token\" => \"api_resources#authenticate\", as: \"api_resource_authenticate\"\n  post 'vpns/:id/dns_server' => 'vpns#add_dns_server', as: 'add_dns_to_vpn'\n  post 'vpns/:id/search_domain' => 'vpns#add_search_domain', as: 'add_search_domain_to_vpn'\n  post 'vpns/:id/supplemental_match_domain' => 'vpns#add_supplemental_match_domain', as: 'add_supplemental_match_domain_to_vpn'\n  delete 'vpns/:id/dns_server/:vpn_domain_name_server_id' => 'vpns#remove_dns_server', as: 'remove_dns_from_vpn'\n  delete 'vpns/:id/search_domain/:vpn_search_domain_id' => 'vpns#remove_search_domain', as: 'remove_search_domain_from_vpn'\n  delete 'vpns/:id/supplemental_match_domain/:vpn_supplemental_match_domain_id' => 'vpns#remove_supplemental_match_domain', as: 'remove_supplemental_match_domain_from_vpn'\n  post 'vpns/:id/group' => 'vpns#assign_group', as: 'assign_group_to_vpn'\n  get 'vpns/:id/groups/:group_id/groups' => 'vpns#user_associated_groups', format: :json\n  get 'vpns/:vpn_id/groups/:group_id/users' => 'vpns#group_associated_users', format: :json\n  post 'vpns/:vpn_id/groups/:group_id/users' => 'vpns#create_group_associated_users', format: :json\n\n  # SAML routes\n\n  get 'sso/saml/metadata' => 'saml_idp#show', format: :xml\n  get '/saml/auth' => 'saml_idp#new'\n  post '/saml/auth' => 'saml_idp#create'\n  post '/saml/sp' => 'saml_idp#add_saml_sp'\n  get '/saml/sp/:name' => 'saml_idp#get_saml_sp'\nend\n"
  },
  {
    "path": "config/schedule.rb",
    "content": "# Use this file to easily define all of your cron jobs.\n#\n# It's helpful, but not entirely necessary to understand cron before proceeding.\n# http://en.wikipedia.org/wiki/Cron\n\n# Example:\n#\n# set :output, \"/path/to/my/cron_log.log\"\n#\n# every 2.hours do\n#   command \"/usr/bin/some_great_command\"\n#   runner \"MyModel.some_method\"\n#   rake \"some:great:rake:task\"\n# end\n#\n# every 4.days do\n#   runner \"AnotherModel.prune_old_records\"\n# end\n\n# Learn more: http://github.com/javan/whenever\n\nevery 1.day, at: '2:00 am' do\n  rake 'users:purge_inactive'\n  rake 'users:revoke_expired_membership'\nend\n"
  },
  {
    "path": "config/secrets.yml",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key is used for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n\n# Make sure the secret is at least 30 characters and all random,\n# no regular words or you'll be exposed to dictionary attacks.\n# You can use `rails secret` to generate a secure secret key.\n\n# Make sure the secrets in this file are kept private\n# if you're sharing your code publicly.\n\ndevelopment:\n  secret_key_base: 6a4735f30c7ab8c09019b80cbfd60ba2ead1d1c6b2c60632522602f002460d4590741f9e6713b89e67fd298bd155ff4f5d2f010a09c19388b0a209bb4841a2f8\n\ntest:\n  secret_key_base: b575eb4f653d58f0b666df115bc91026211573c0224a516e0e20d8b2817d268df6183dd4a028a38780c7b5846e255af900de9319e395cc3e004a387fadb99555\n\nintegration:\n  secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>\n  secret_api_key: <%= ENV['SECRET_API_KEY'] %>\n\n# Do not keep production secrets in the repository,\n# instead read values from the environment.\nproduction:\n  secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>\n  secret_api_key: <%= ENV['SECRET_API_KEY'] %>\n"
  },
  {
    "path": "config/spring.rb",
    "content": "%w(\n  .ruby-version\n  .rbenv-vars\n  tmp/restart.txt\n  tmp/caching-dev.txt\n).each { |path| Spring.watch(path) }\n"
  },
  {
    "path": "config/storage.yml",
    "content": "test:\n  service: Disk\n  root: <%= Rails.root.join(\"tmp/storage\") %>\n\nlocal:\n  service: Disk\n  root: <%= Rails.root.join(\"storage\") %>\n\n# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)\n# amazon:\n#   service: S3\n#   access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>\n#   secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>\n#   region: us-east-1\n#   bucket: your_own_bucket-<%= Rails.env %>\n\n# Remember not to checkin your GCS keyfile to a repository\n# google:\n#   service: GCS\n#   project: your_project\n#   credentials: <%= Rails.root.join(\"path/to/gcs.keyfile\") %>\n#   bucket: your_own_bucket-<%= Rails.env %>\n\n# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)\n# microsoft:\n#   service: AzureStorage\n#   storage_account_name: your_account_name\n#   storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>\n#   container: your_container_name-<%= Rails.env %>\n\n# mirror:\n#   service: Mirror\n#   primary: local\n#   mirrors: [ amazon, google, microsoft ]\n"
  },
  {
    "path": "config.ru",
    "content": "# This file is used by Rack-based servers to start the application.\n\nrequire ::File.expand_path('../config/environment', __FILE__)\nrun Rails.application\n"
  },
  {
    "path": "db/migrate/20160419122430_devise_create_users.rb",
    "content": "class DeviseCreateUsers < ActiveRecord::Migration[5.0]\n  def change\n    create_table :users do |t|\n      ## Database authenticatable\n      t.string :email,              null: false, default: \"\"\n      t.string :encrypted_password, null: false, default: \"\"\n\n      ## Recoverable\n      t.string   :reset_password_token\n      t.datetime :reset_password_sent_at\n\n      ## Rememberable\n      t.datetime :remember_created_at\n\n      ## Trackable\n      t.integer  :sign_in_count, default: 0, null: false\n      t.datetime :current_sign_in_at\n      t.datetime :last_sign_in_at\n      t.string   :current_sign_in_ip\n      t.string   :last_sign_in_ip\n\n      ## Confirmable\n      # t.string   :confirmation_token\n      # t.datetime :confirmed_at\n      # t.datetime :confirmation_sent_at\n      # t.string   :unconfirmed_email # Only if using reconfirmable\n\n      ## Lockable\n      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts\n      # t.string   :unlock_token # Only if unlock strategy is :email or :both\n      # t.datetime :locked_at\n\n\n      t.timestamps null: false\n    end\n\n    add_index :users, :email,                unique: true\n    add_index :users, :reset_password_token, unique: true\n    # add_index :users, :confirmation_token,   unique: true\n    # add_index :users, :unlock_token,         unique: true\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160419132647_add_provider_to_users.rb",
    "content": "class AddProviderToUsers < ActiveRecord::Migration[5.0]\n  def change\n    add_column :users, :provider, :string\n    add_column :users, :uid, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160419144739_add_name_to_users.rb",
    "content": "class AddNameToUsers < ActiveRecord::Migration[5.0]\n  def change\n    add_column :users, :name, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160427123146_add_auth_key_to_user.rb",
    "content": "class AddAuthKeyToUser < ActiveRecord::Migration[5.0]\n  def change\n    add_column :users, :auth_key, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160427123233_add_provisioning_uri_to_user.rb",
    "content": "class AddProvisioningUriToUser < ActiveRecord::Migration[5.0]\n  def change\n    add_column :users, :provisioning_uri, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160519042340_add_active_to_users.rb",
    "content": "class AddActiveToUsers < ActiveRecord::Migration[5.0]\n  def change\n    add_column :users, :active, :boolean\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160519064340_add_default_value_to_users.rb",
    "content": "class AddDefaultValueToUsers < ActiveRecord::Migration[5.0]\n  def change\n    change_column :users, :active, :boolean, default: true\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160615044834_create_hosts.rb",
    "content": "class CreateHosts < ActiveRecord::Migration[5.0]\n  def change\n    create_table :hosts do |t|\n      t.string :host_pattern\n\n      t.timestamps null: false\n    end\n    add_index :hosts, :host_pattern\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160615045052_add_admin_to_user.rb",
    "content": "class AddAdminToUser < ActiveRecord::Migration[5.0]\n  def change\n    add_column :users, :admin, :boolean, default: false\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160615112805_add_user_to_host.rb",
    "content": "class AddUserToHost < ActiveRecord::Migration[5.0]\n  def change\n    add_reference :hosts, :user, index: true, foreign_key: true\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160628140022_add_deleted_at_to_host.rb",
    "content": "class AddDeletedAtToHost < ActiveRecord::Migration[5.0]\n  def change\n    add_column :hosts, :deleted_at, :datetime\n    add_index :hosts, :deleted_at\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160628140440_add_deleted_by_to_host.rb",
    "content": "class AddDeletedByToHost < ActiveRecord::Migration[5.0]\n  def change\n    add_column :hosts, :deleted_by, :integer\n    add_index :hosts, :deleted_by\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160629043358_add_homedir_to_user.rb",
    "content": "class AddHomedirToUser < ActiveRecord::Migration[5.0]\n  def change\n    add_column :users, :home_dir, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160629043415_add_shell_to_user.rb",
    "content": "class AddShellToUser < ActiveRecord::Migration[5.0]\n  def change\n    add_column :users, :shell, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160629075435_create_groups.rb",
    "content": "class CreateGroups < ActiveRecord::Migration[5.0]\n  def change\n    create_table :groups do |t|\n      t.string :name\n      t.integer :gid\n\n      t.timestamps null: false\n    end\n    add_index :groups, :name\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160701090045_create_group_associations.rb",
    "content": "class CreateGroupAssociations < ActiveRecord::Migration[5.0]\n  def change\n    create_table :group_associations do |t|\n      t.references :user\n      t.references :group\n\n      t.timestamps null: false\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160701112600_add_deleted_properties_to_group.rb",
    "content": "class AddDeletedPropertiesToGroup < ActiveRecord::Migration[5.0]\n  def change\n    add_column :groups, :deleted_by, :integer\n    add_column :groups, :deleted_at, :datetime\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160707115313_create_access_tokens.rb",
    "content": "class CreateAccessTokens < ActiveRecord::Migration[5.0]\n  def change\n    create_table :access_tokens do |t|\n      t.string :token\n\n      t.timestamps null: false\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160714115228_add_public_key_to_user.rb",
    "content": "class AddPublicKeyToUser < ActiveRecord::Migration[5.0]\n  def change\n    add_column :users, :public_key, :text\n  end\nend\n"
  },
  {
    "path": "db/migrate/20160908081651_create_host_machines.rb",
    "content": "class CreateHostMachines < ActiveRecord::Migration[5.0]\n  def change\n    create_table :host_machines do |t|\n      t.string :name\n\n      t.timestamps null: false\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20161003145832_create_host_access_groups.rb",
    "content": "class CreateHostAccessGroups < ActiveRecord::Migration[5.0]\n  def change\n    create_table :host_access_groups do |t|\n      t.references :host_machine\n      t.references :group\n\n      t.timestamps null: false\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20170803140620_add_user_login_id_to_user.rb",
    "content": "class AddUserLoginIdToUser < ActiveRecord::Migration[5.0]\n  def change\n    add_column :users, :user_login_id, :string\n    add_index :users, :user_login_id\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171013115441_create_versions.rb",
    "content": "# This migration creates the `versions` table, the only schema PT requires.\n# All other migrations PT provides are optional.\nclass CreateVersions < ActiveRecord::Migration[5.0]\n\n  # The largest text column available in all supported RDBMS is\n  # 1024^3 - 1 bytes, roughly one gibibyte.  We specify a size\n  # so that MySQL will use `longtext` instead of `text`.  Otherwise,\n  # when serializing very large objects, `text` might not be big enough.\n  TEXT_BYTES = 1_073_741_823\n\n  def change\n    #create_table :versions, { options: \"ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci\" } do |t|\n    create_table :versions do |t|\n      t.string   :item_type, {:null=>false, :limit=>191}\n      t.integer  :item_id,   null: false\n      t.string   :event,     null: false\n      t.string   :whodunnit\n      t.text     :object, limit: TEXT_BYTES\n      t.text :object_changes  # Optional column-level changes\n\n      # Known issue in MySQL: fractional second precision\n      # -------------------------------------------------\n      #\n      # MySQL timestamp columns do not support fractional seconds unless\n      # defined with \"fractional seconds precision\". MySQL users should manually\n      # add fractional seconds precision to this migration, specifically, to\n      # the `created_at` column.\n      # (https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html)\n      #\n      # MySQL users should also upgrade to rails 4.2, which is the first\n      # version of ActiveRecord with support for fractional seconds in MySQL.\n      # (https://github.com/rails/rails/pull/14359)\n      #\n      t.datetime :created_at\n    end\n    add_index :versions, %i(item_type item_id)\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171016064705_remove_index_group_name.rb",
    "content": "class RemoveIndexGroupName < ActiveRecord::Migration[5.0]\n  def change\n    remove_index :groups, [:name]\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171016071526_add_unique_index_on_groups_name.rb",
    "content": "class AddUniqueIndexOnGroupsName < ActiveRecord::Migration[5.0]\n  def change\n    add_index :groups, :name, unique: true\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171031060034_create_group_admin.rb",
    "content": "class CreateGroupAdmin < ActiveRecord::Migration[5.0]\n  def change\n    create_table :group_admins do |t|\n      t.integer :group_id\n      t.integer :user_id\n\n      t.timestamps\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171031060217_add_foreign_key_ref_on_group_admin.rb",
    "content": "class AddForeignKeyRefOnGroupAdmin < ActiveRecord::Migration[5.0]\n  def change\n    add_foreign_key :group_admins, :groups\n    add_foreign_key :group_admins, :users\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171031100758_create_vpns.rb",
    "content": "class CreateVpns < ActiveRecord::Migration[5.0]\n  def change\n    create_table :vpns do |t|\n      t.string :name\n      t.string :host_name\n      t.string :url\n\n      t.timestamps\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171031101026_create_vpn_group_association.rb",
    "content": "class CreateVpnGroupAssociation < ActiveRecord::Migration[5.0]\n  def change\n    create_table :vpn_group_associations do |t|\n      t.integer :group_id\n      t.integer :vpn_id\n\n      t.timestamps\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171031103518_add_foreign_key_ref_on_vpn_group_association.rb",
    "content": "class AddForeignKeyRefOnVpnGroupAssociation < ActiveRecord::Migration[5.0]\n  def change\n    add_foreign_key :vpn_group_associations, :groups\n    add_foreign_key :vpn_group_associations, :vpns\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171031113123_create_vpn_group_user_association.rb",
    "content": "class CreateVpnGroupUserAssociation < ActiveRecord::Migration[5.0]\n  def change\n    create_table :vpn_group_user_associations do |t|\n      t.integer :user_id\n      t.integer :vpn_id\n      t.integer :group_id\n\n      t.timestamps\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171031121702_add_foreign_key_ref_on_vpn_group_user_association.rb",
    "content": "class AddForeignKeyRefOnVpnGroupUserAssociation < ActiveRecord::Migration[5.0]\n  def change\n    add_foreign_key :vpn_group_user_associations, :users\n    add_foreign_key :vpn_group_user_associations, :groups\n    add_foreign_key :vpn_group_user_associations, :vpns\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171102071909_add_ip_address_to_vpns.rb",
    "content": "class AddIpAddressToVpns < ActiveRecord::Migration[5.0]\n  def change\n    add_column :vpns, :ip_address, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171107114249_remove_url_from_vpns.rb",
    "content": "class RemoveUrlFromVpns < ActiveRecord::Migration[5.0]\n  def change\n    remove_column :vpns, :url, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171108130234_add_user_id_to_access_token.rb",
    "content": "class AddUserIdToAccessToken < ActiveRecord::Migration[5.0]\n  def change\n    add_column :access_tokens, :user_id, :integer\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171108130353_add_foreign_key_ref_on_access_tokens.rb",
    "content": "class AddForeignKeyRefOnAccessTokens < ActiveRecord::Migration[5.0]\n  def change\n     add_foreign_key \"access_tokens\", \"users\"\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171124090240_add_uuid_to_vpns.rb",
    "content": "class AddUuidToVpns < ActiveRecord::Migration[5.0]\n  def change\n    add_column :vpns, :uuid, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171124114427_create_vpn_domain_name_servers.rb",
    "content": "class CreateVpnDomainNameServers < ActiveRecord::Migration[5.0]\n  def change\n    create_table :vpn_domain_name_servers do |t|\n      t.integer :vpn_id\n      t.string :server_address\n\n      t.timestamps\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171124114830_create_vpn_search_domains.rb",
    "content": "class CreateVpnSearchDomains < ActiveRecord::Migration[5.0]\n  def change\n    create_table :vpn_search_domains do |t|\n      t.integer :vpn_id\n      t.string :search_domain\n\n      t.timestamps\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20171124115925_create_vpn_supplemental_match_domains.rb",
    "content": "class CreateVpnSupplementalMatchDomains < ActiveRecord::Migration[5.0]\n  def change\n    create_table :vpn_supplemental_match_domains do |t|\n      t.integer :vpn_id\n      t.string :supplemental_match_domain\n\n      t.timestamps\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180104081814_add_product_name_to_users.rb",
    "content": "class AddProductNameToUsers < ActiveRecord::Migration[5.0]\n  def change\n    add_column :users, :product_name, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180202102206_create_saml_service_providers.rb",
    "content": "class CreateSamlServiceProviders < ActiveRecord::Migration[5.0]\n  def change\n    create_table :saml_service_providers do |t|\n      t.string :name, null: false, unique: true\n      t.string :sso_url, null: false, unique: true\n      t.string :metadata_url, null: false, unique: true\n\n      t.timestamps null: false\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180214050204_add_api_key_to_host_machines.rb",
    "content": "class AddApiKeyToHostMachines < ActiveRecord::Migration[5.0]\n  def change\n    add_column :host_machines, :api_key, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180214052451_create_ip_addresses.rb",
    "content": "class CreateIpAddresses < ActiveRecord::Migration[5.0]\n  def change\n    create_table :ip_addresses do |t|\n      t.string :address\n      t.string :mac_address\n\n      t.timestamps null: false\n    end\n    add_index :ip_addresses, :address\n    add_index :ip_addresses, :mac_address\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180214052644_add_host_machine_to_ip_address.rb",
    "content": "class AddHostMachineToIpAddress < ActiveRecord::Migration[5.0]\n  def change\n    add_reference :ip_addresses, :host_machine, index: true, foreign_key: true\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180219150818_add_description_to_group.rb",
    "content": "class AddDescriptionToGroup < ActiveRecord::Migration[5.0]\n  def change\n    add_column :groups, :description, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180222135930_add_access_key_to_host_machine.rb",
    "content": "class AddAccessKeyToHostMachine < ActiveRecord::Migration[5.0]\n  def change\n    add_column :host_machines, :access_key, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180222140000_add_access_key_to_user.rb",
    "content": "class AddAccessKeyToUser < ActiveRecord::Migration[5.0]\n  def change\n    add_column :users, :access_key, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180227051732_create_api_resources.rb",
    "content": "class CreateApiResources < ActiveRecord::Migration[5.0]\n  def change\n    create_table :api_resources do |t|\n      t.string :name\n      t.string :description\n      t.string :access_key\n\n      t.timestamps null: false\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180301010021_add_user_to_api_resources.rb",
    "content": "class AddUserToApiResources < ActiveRecord::Migration[5.0]\n  def change\n    add_reference :api_resources, :user, index: true, foreign_key: true\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180301010035_add_group_to_api_resources.rb",
    "content": "class AddGroupToApiResources < ActiveRecord::Migration[5.0]\n  def change\n    add_reference :api_resources, :group, index: true, foreign_key: true\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180306231200_add_deactivated_at_to_users.rb",
    "content": "class AddDeactivatedAtToUsers < ActiveRecord::Migration[5.0]\n  def change\n    add_column :users, :deactivated_at, :datetime\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180311082600_rename_access_key_in_api_resources.rb",
    "content": "class RenameAccessKeyInApiResources < ActiveRecord::Migration[5.0]\n  def change\n    rename_column :api_resources, :access_key, :hashed_access_key\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180311161200_rename_token_in_access_tokens.rb",
    "content": "class RenameTokenInAccessTokens < ActiveRecord::Migration[5.0]\n  def change\n    rename_column :access_tokens, :token, :hashed_token\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180318083000_create_indexes_to_speedup_nss_controller.rb",
    "content": "class CreateIndexesToSpeedupNssController < ActiveRecord::Migration[5.0]\n  def change\n    add_index :users, :uid\n    add_index :access_tokens, :hashed_token\n    add_index :host_machines, :access_key\n    add_index :host_access_groups, [:host_machine_id, :group_id]\n    add_index :group_associations, [:group_id, :user_id]\n    add_index :groups, :gid\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180613074108_create_organisations.rb",
    "content": "class CreateOrganisations < ActiveRecord::Migration[5.0]\n  def change\n    create_table :organisations do |t|\n      t.string :name\n      t.string :url\n      t.string :email_domain\n\n      t.timestamps null: false\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180613165050_drop_saml_service_providers.rb",
    "content": "class DropSamlServiceProviders < ActiveRecord::Migration[5.0]\n  def change\n    drop_table :saml_service_providers\n  end\nend\n"
  },
  {
    "path": "db/migrate/20180723175600_update_organisations_for_saml.rb",
    "content": "class UpdateOrganisationsForSaml < ActiveRecord::Migration[5.0]\n  def change\n    rename_column :organisations, :url, :website\n    rename_column :organisations, :email_domain, :domain\n    add_column :organisations, :country, :string\n    add_column :organisations, :state, :string\n    add_column :organisations, :address, :string\n    add_column :organisations, :unit_name, :string\n    add_column :organisations, :admin_email_address, :string\n    add_column :organisations, :slug, :string\n    add_column :organisations, :cert_fingerprint, :string\n    add_column :organisations, :cert_key, :text\n    add_column :organisations, :cert_private_key, :text\n  end\nend\n"
  },
  {
    "path": "db/migrate/20181002023107_add_default_admins_to_host_machines.rb",
    "content": "class AddDefaultAdminsToHostMachines < ActiveRecord::Migration[5.0]\n  def change\n    add_column :host_machines, :default_admins, :boolean, default: true\n  end\nend\n"
  },
  {
    "path": "db/migrate/20181016093315_create_saml_app_configs.rb",
    "content": "class CreateSamlAppConfigs < ActiveRecord::Migration[5.0]\n  def change\n    create_table :saml_app_configs do |t|\n      t.references :group, foreign_key: true\n      t.string :sso_url\n      t.json :config\n      t.references :organisation, foreign_key: true\n      t.string :app_name\n\n      t.timestamps\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20181208184236_add_fields_to_user.rb",
    "content": "class AddFieldsToUser < ActiveRecord::Migration[5.1]\n  def change\n    add_column :users, :first_name, :string\n    add_column :users, :last_name, :string\n    add_column :users, :user_role, :string\n    add_column :users, :mobile, :string\n    add_column :users, :alternate_email, :string\n  end\nend\n"
  },
  {
    "path": "db/migrate/20190624024930_add_expiration_date_to_group_associations.rb",
    "content": "class AddExpirationDateToGroupAssociations < ActiveRecord::Migration[5.1]\n  def change\n    add_column :group_associations, :expiration_date, :date\n  end\nend\n"
  },
  {
    "path": "db/migrate/20190820070910_create_endpoints.rb",
    "content": "class CreateEndpoints < ActiveRecord::Migration[5.1]\n  def change\n    create_table :endpoints do |t|\n      t.string :path, null: false\n      t.string :method, null: false\n\n      t.timestamps null: false\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20190820075040_create_group_endpoints.rb",
    "content": "class CreateGroupEndpoints < ActiveRecord::Migration[5.1]\n  def change\n    create_table :group_endpoints do |t|\n      t.integer :group_id\n      t.bigint :endpoint_id\n\n      t.timestamps null: false\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20190820080624_add_foreign_key_ref_on_group_endpoints.rb",
    "content": "class AddForeignKeyRefOnGroupEndpoints < ActiveRecord::Migration[5.1]\n  def change\n    add_foreign_key :group_endpoints, :groups\n    add_foreign_key :group_endpoints, :endpoints\n  end\nend\n"
  },
  {
    "path": "db/migrate/20200113065717_add_sessions_table.rb",
    "content": "class AddSessionsTable < ActiveRecord::Migration[5.1]\n  def change\n    create_table :sessions do |t|\n      t.string :session_id, :null => false\n      t.text :data\n      t.timestamps\n    end\n\n    add_index :sessions, :session_id, :unique => true\n    add_index :sessions, :updated_at\n  end\nend\n"
  },
  {
    "path": "db/migrate/20220926001848_add_service_name_to_active_storage_blobs.active_storage.rb",
    "content": "# This migration comes from active_storage (originally 20190112182829)\nclass AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]\n  def up\n    return unless table_exists?(:active_storage_blobs)\n\n    unless column_exists?(:active_storage_blobs, :service_name)\n      add_column :active_storage_blobs, :service_name, :string\n\n      if configured_service = ActiveStorage::Blob.service.name\n        ActiveStorage::Blob.unscoped.update_all(service_name: configured_service)\n      end\n\n      change_column :active_storage_blobs, :service_name, :string, null: false\n    end\n  end\n\n  def down\n    return unless table_exists?(:active_storage_blobs)\n\n    remove_column :active_storage_blobs, :service_name\n  end\nend\n"
  },
  {
    "path": "db/migrate/20220926001849_create_active_storage_variant_records.active_storage.rb",
    "content": "# This migration comes from active_storage (originally 20191206030411)\nclass CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]\n  def change\n    return unless table_exists?(:active_storage_blobs)\n\n    # Use Active Record's configured type for primary key\n    create_table :active_storage_variant_records, id: primary_key_type, if_not_exists: true do |t|\n      t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type\n      t.string :variation_digest, null: false\n\n      t.index %i[ blob_id variation_digest ], name: \"index_active_storage_variant_records_uniqueness\", unique: true\n      t.foreign_key :active_storage_blobs, column: :blob_id\n    end\n  end\n\n  private\n    def primary_key_type\n      config = Rails.configuration.generators\n      config.options[config.orm][:primary_key_type] || :primary_key\n    end\n\n    def blobs_primary_key_type\n      pkey_name = connection.primary_key(:active_storage_blobs)\n      pkey_column = connection.columns(:active_storage_blobs).find { |c| c.name == pkey_name }\n      pkey_column.bigint? ? :bigint : pkey_column.type\n    end\nend\n"
  },
  {
    "path": "db/migrate/20220926001850_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb",
    "content": "# This migration comes from active_storage (originally 20211119233751)\nclass RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0]\n  def change\n    return unless table_exists?(:active_storage_blobs)\n\n    change_column_null(:active_storage_blobs, :checksum, true)\n  end\nend\n"
  },
  {
    "path": "db/schema.rb",
    "content": "# This file is auto-generated from the current state of the database. Instead\n# of editing this file, please use the migrations feature of Active Record to\n# incrementally modify your database, and then regenerate this schema definition.\n#\n# This file is the source Rails uses to define your schema when running `bin/rails\n# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to\n# be faster and is potentially less error prone than running all of your\n# migrations from scratch. Old migrations may fail to apply correctly if those\n# migrations use external dependencies or application code.\n#\n# It's strongly recommended that you check this file into your version control system.\n\nActiveRecord::Schema[7.0].define(version: 2022_09_26_001850) do\n  create_table \"access_tokens\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.string \"hashed_token\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.integer \"user_id\"\n    t.index [\"hashed_token\"], name: \"index_access_tokens_on_hashed_token\"\n    t.index [\"user_id\"], name: \"fk_rails_96fc070778\"\n  end\n\n  create_table \"api_resources\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.string \"name\"\n    t.string \"description\"\n    t.string \"hashed_access_key\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.integer \"user_id\"\n    t.integer \"group_id\"\n    t.index [\"group_id\"], name: \"index_api_resources_on_group_id\"\n    t.index [\"user_id\"], name: \"index_api_resources_on_user_id\"\n  end\n\n  create_table \"endpoints\", charset: \"utf8mb4\", force: :cascade do |t|\n    t.string \"path\", null: false\n    t.string \"method\", null: false\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n  end\n\n  create_table \"group_admins\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.integer \"group_id\"\n    t.integer \"user_id\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.index [\"group_id\"], name: \"fk_rails_1a1d29d2d3\"\n    t.index [\"user_id\"], name: \"fk_rails_0ac5a6fa32\"\n  end\n\n  create_table \"group_associations\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.integer \"user_id\"\n    t.integer \"group_id\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.date \"expiration_date\"\n    t.index [\"group_id\", \"user_id\"], name: \"index_group_associations_on_group_id_and_user_id\"\n    t.index [\"group_id\"], name: \"index_group_associations_on_group_id\"\n    t.index [\"user_id\"], name: \"index_group_associations_on_user_id\"\n  end\n\n  create_table \"group_endpoints\", charset: \"utf8mb4\", force: :cascade do |t|\n    t.integer \"group_id\"\n    t.bigint \"endpoint_id\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.index [\"endpoint_id\"], name: \"fk_rails_b700efc1d7\"\n    t.index [\"group_id\"], name: \"fk_rails_b6c29808cd\"\n  end\n\n  create_table \"groups\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.string \"name\"\n    t.integer \"gid\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.integer \"deleted_by\"\n    t.datetime \"deleted_at\", precision: nil\n    t.string \"description\"\n    t.index [\"gid\"], name: \"index_groups_on_gid\"\n    t.index [\"name\"], name: \"index_groups_on_name\", unique: true\n  end\n\n  create_table \"host_access_groups\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.integer \"host_machine_id\"\n    t.integer \"group_id\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.index [\"group_id\"], name: \"index_host_access_groups_on_group_id\"\n    t.index [\"host_machine_id\", \"group_id\"], name: \"index_host_access_groups_on_host_machine_id_and_group_id\"\n    t.index [\"host_machine_id\"], name: \"index_host_access_groups_on_host_machine_id\"\n  end\n\n  create_table \"host_machines\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.string \"name\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.string \"api_key\"\n    t.string \"access_key\"\n    t.boolean \"default_admins\", default: true\n    t.index [\"access_key\"], name: \"index_host_machines_on_access_key\"\n  end\n\n  create_table \"hosts\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.string \"host_pattern\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.integer \"user_id\"\n    t.datetime \"deleted_at\", precision: nil\n    t.integer \"deleted_by\"\n    t.index [\"deleted_at\"], name: \"index_hosts_on_deleted_at\"\n    t.index [\"deleted_by\"], name: \"index_hosts_on_deleted_by\"\n    t.index [\"host_pattern\"], name: \"index_hosts_on_host_pattern\"\n    t.index [\"user_id\"], name: \"index_hosts_on_user_id\"\n  end\n\n  create_table \"ip_addresses\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.string \"address\"\n    t.string \"mac_address\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.integer \"host_machine_id\"\n    t.index [\"address\"], name: \"index_ip_addresses_on_address\"\n    t.index [\"host_machine_id\"], name: \"index_ip_addresses_on_host_machine_id\"\n    t.index [\"mac_address\"], name: \"index_ip_addresses_on_mac_address\"\n  end\n\n  create_table \"organisations\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.string \"name\"\n    t.string \"website\"\n    t.string \"domain\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.string \"country\"\n    t.string \"state\"\n    t.string \"address\"\n    t.string \"unit_name\"\n    t.string \"admin_email_address\"\n    t.string \"slug\"\n    t.string \"cert_fingerprint\"\n    t.text \"cert_key\"\n    t.text \"cert_private_key\"\n  end\n\n  create_table \"saml_app_configs\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.integer \"group_id\"\n    t.string \"sso_url\"\n    t.text \"config\", size: :long, collation: \"utf8mb4_bin\"\n    t.integer \"organisation_id\"\n    t.string \"app_name\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.index [\"group_id\"], name: \"index_saml_app_configs_on_group_id\"\n    t.index [\"organisation_id\"], name: \"index_saml_app_configs_on_organisation_id\"\n    t.check_constraint \"json_valid(`config`)\", name: \"config\"\n  end\n\n  create_table \"sessions\", charset: \"utf8mb4\", force: :cascade do |t|\n    t.string \"session_id\", null: false\n    t.text \"data\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.index [\"session_id\"], name: \"index_sessions_on_session_id\", unique: true\n    t.index [\"updated_at\"], name: \"index_sessions_on_updated_at\"\n  end\n\n  create_table \"users\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.string \"email\", default: \"\", null: false\n    t.string \"encrypted_password\", default: \"\", null: false\n    t.string \"reset_password_token\"\n    t.datetime \"reset_password_sent_at\", precision: nil\n    t.datetime \"remember_created_at\", precision: nil\n    t.integer \"sign_in_count\", default: 0, null: false\n    t.datetime \"current_sign_in_at\", precision: nil\n    t.datetime \"last_sign_in_at\", precision: nil\n    t.string \"current_sign_in_ip\"\n    t.string \"last_sign_in_ip\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.string \"provider\"\n    t.string \"uid\"\n    t.string \"name\"\n    t.string \"auth_key\"\n    t.string \"provisioning_uri\"\n    t.boolean \"active\", default: true\n    t.boolean \"admin\", default: false\n    t.string \"home_dir\"\n    t.string \"shell\"\n    t.text \"public_key\"\n    t.string \"user_login_id\"\n    t.string \"product_name\"\n    t.string \"access_key\"\n    t.datetime \"deactivated_at\", precision: nil\n    t.string \"first_name\"\n    t.string \"last_name\"\n    t.string \"user_role\"\n    t.string \"mobile\"\n    t.string \"alternate_email\"\n    t.index [\"email\"], name: \"index_users_on_email\", unique: true\n    t.index [\"reset_password_token\"], name: \"index_users_on_reset_password_token\", unique: true\n    t.index [\"uid\"], name: \"index_users_on_uid\"\n    t.index [\"user_login_id\"], name: \"index_users_on_user_login_id\"\n  end\n\n  create_table \"versions\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.string \"item_type\"\n    t.string \"{:null=>false, :limit=>191}\"\n    t.integer \"item_id\", null: false\n    t.string \"event\", null: false\n    t.string \"whodunnit\"\n    t.text \"object\", size: :long\n    t.text \"object_changes\"\n    t.datetime \"created_at\", precision: nil\n    t.index [\"item_type\", \"item_id\"], name: \"index_versions_on_item_type_and_item_id\"\n  end\n\n  create_table \"vpn_domain_name_servers\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.integer \"vpn_id\"\n    t.string \"server_address\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n  end\n\n  create_table \"vpn_group_associations\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.integer \"group_id\"\n    t.integer \"vpn_id\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.index [\"group_id\"], name: \"fk_rails_67a460ac90\"\n    t.index [\"vpn_id\"], name: \"fk_rails_9be3690c1d\"\n  end\n\n  create_table \"vpn_group_user_associations\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.integer \"user_id\"\n    t.integer \"vpn_id\"\n    t.integer \"group_id\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.index [\"group_id\"], name: \"fk_rails_30de0bd58e\"\n    t.index [\"user_id\"], name: \"fk_rails_275419a627\"\n    t.index [\"vpn_id\"], name: \"fk_rails_dbd29a5c87\"\n  end\n\n  create_table \"vpn_search_domains\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.integer \"vpn_id\"\n    t.string \"search_domain\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n  end\n\n  create_table \"vpn_supplemental_match_domains\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.integer \"vpn_id\"\n    t.string \"supplemental_match_domain\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n  end\n\n  create_table \"vpns\", id: :integer, charset: \"utf8mb4\", force: :cascade do |t|\n    t.string \"name\"\n    t.string \"host_name\"\n    t.datetime \"created_at\", precision: nil, null: false\n    t.datetime \"updated_at\", precision: nil, null: false\n    t.string \"ip_address\"\n    t.string \"uuid\"\n  end\n\n  add_foreign_key \"access_tokens\", \"users\"\n  add_foreign_key \"api_resources\", \"groups\"\n  add_foreign_key \"api_resources\", \"users\"\n  add_foreign_key \"group_admins\", \"groups\"\n  add_foreign_key \"group_admins\", \"users\"\n  add_foreign_key \"group_endpoints\", \"endpoints\"\n  add_foreign_key \"group_endpoints\", \"groups\"\n  add_foreign_key \"hosts\", \"users\"\n  add_foreign_key \"ip_addresses\", \"host_machines\"\n  add_foreign_key \"saml_app_configs\", \"groups\"\n  add_foreign_key \"saml_app_configs\", \"organisations\"\n  add_foreign_key \"vpn_group_associations\", \"groups\"\n  add_foreign_key \"vpn_group_associations\", \"vpns\"\n  add_foreign_key \"vpn_group_user_associations\", \"groups\"\n  add_foreign_key \"vpn_group_user_associations\", \"users\"\n  add_foreign_key \"vpn_group_user_associations\", \"vpns\"\nend\n"
  },
  {
    "path": "db/seeds/development.rb",
    "content": "dev_user = User.create_user('dev', 'dev@a.c')\ndev_user.generate_two_factor_auth\n(1..6).each do |uid|\n  user = User.create_user(\"dev#{uid}\", \"dev#{uid}@a.c\")\n  user.generate_two_factor_auth\nend\ngroup = Group.create(name: 'people')\nUser.all.each do |user|\n  user.groups << group\n  user.save!\nend\nAccessToken.create(token: 'a')\n\nvpn = Vpn.create(name: 'dev-vpn', host_name: 'dev-vpn.example.com', ip_address: '1.2.3.4', uuid: 'FC29CB92-FC7E-4F0B-B938-7612DFDECC28')\nHostMachine.create(name: 'SampleHost1')\nHostMachine.create(name: 'SampleHost2')\nvsd = VpnSearchDomain.create(search_domain: 'dev-search.vpn.example.com')\nvdns = VpnDomainNameServer.create(server_address: '8.8.8.8')\nvsmd = VpnSupplementalMatchDomain.create(supplemental_match_domain: 'match.domains')\nvpn.vpn_search_domains << vsd\nvpn.vpn_domain_name_servers << vdns\nvpn.vpn_supplemental_match_domains << vsmd\nvpn.save!\n"
  },
  {
    "path": "db/seeds/production.rb",
    "content": "# This file should contain all the record creation needed to seed the database with its default values.\n# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).\n#\n# Examples:\n#\n#   cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])\n#   Mayor.create(name: 'Emanuel', city: cities.first)\n#\ngroup = Group.where(name: \"people\").first\ngroup = Group.create(name: \"people\") if group.blank?\n\ngroup = Group.where(name: \"devops\").first\ngroup = Group.create(name: \"devops\") if group.blank?\n"
  },
  {
    "path": "db/seeds/test.rb",
    "content": "# This file should contain all the record creation needed to seed the database with its default values.\n# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).\n#\n# Examples:\n#\n#   cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])\n#   Mayor.create(name: 'Emanuel', city: cities.first)\n#\n"
  },
  {
    "path": "db/seeds.rb",
    "content": "# This file should contain all the record creation needed to seed the database with its default values.\n# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).\n#\n#\n# this file loads seeds based on environments - so please be aware to run appropriate seed file\n#\nload(Rails.root.join( 'db', 'seeds', \"#{Rails.env.downcase}.rb\"))\n"
  },
  {
    "path": "docker-compose.yaml",
    "content": "version: \"2\"\nservices:\n  gate-sql:\n    image: mysql:5.7\n    volumes:\n      - ${PWD}/data:/var/lib/mysql\n    environment:\n      - MYSQL_ALLOW_EMPTY_PASSWORD=true\n  \n  gate:\n    image: gate\n    ports:\n      - \"3000:3000\"\n    depends_on: \n      - gate-sql\n    env_file:\n      - .env\n    command: sh ./setup.sh\n"
  },
  {
    "path": "docs/additional_setup.md",
    "content": "# Additional Setup\n\n### Setting-up Public Key Lookup\n\n* Ensure user has uploaded their public key into gate.\n* Add following lines to your sshd_config - It is located at `/etc/ssh/sshd_config` on most linux distros.\n\n  ```\n  AuthorizedKeysCommand /usr/bin/gate_ssh.sh\n  AuthorizedKeysCommandUser nobody\n  ```\n\n* Add file with following content to `/usr/bin/` with name `gate_ssh.sh` owned by root\n\n  ```\n  #!/bin/sh\n  /usr/bin/curl -k --silent \"https://<gate server name or IP>/profile/$1/key\"\n  ```\n\n> **Please Note:** Point URL to Gate server and test by executing `gate_ssh.sh <username>` to see if this prints the public key.\n"
  },
  {
    "path": "docs/administration.md",
    "content": "# Administration\n\n### Running Administrative Tasks\n\nYou can open rails console and give one user admin privilege by setting up `user.admin = true` in console. Then when you signed-in as that user, Gate will show you its administration UI. You can do the following with Gate admin UI:\n\n* Enable/disable user account\n* Make user administrator\n* Control what host user's are allowed to login via host patterns, by default they are allowed everyhost which starts with `s-*` (we use `s-` for staging and `p-` for production)\n* Make user part of group, by default they are part of 'people' group.\n\n> **DNS Alert** Please note that gate rely heavily on DNS and host supplied IP addresses, so it authenticates against host native IP address rather than NAT'd IP address. It does reverse name lookup on supplied IP address, if that fails then it will be looking at matching IP address itself.\n\n### Scheduler\n\nGate has some tasks that can be scheduled for maintenance purpose. Please see `config/scheduler.rb` to see the list of available tasks.\n\nYou can run `whenever --update-crontab` to update cronjob so that it run these tasks. Gate utilize `whenever` gem for maintaining scheduled tasks, which in turn utilize cronjob as its backend.\n\n### Logs\n\nLogs are stored here:\n\n* Puma logs\n  * `shared/log/puma.stdout.log`\n  * `shared/log/puma.stderr.log`\n* App logs\n  * `log/<env>.log`\n\nSome errors may be being written directly to stdout/stderr and may not be available in the application log file.\n"
  },
  {
    "path": "docs/dredd_setup.md",
    "content": "# Dredd Setup\n\nDredd is used to run test, to validate the API Blueprint against Backend Implementation.\n\n### Install npm \n\nOn Mac OS\n\n```\nbrew install node\n```\n\nOn Linux\n\n```\napt-get install npm\n```\n\n### Install dredd\n\nTo install dredd globally run \n```\nnpm install -g dredd\n```\n\nBefore you run the dredd please make sure you've run the `bundler install` and fill the `env` file\n\n### Running dredd\n\nRunning without debug\n```\ndredd\n```\n\nRunning with debug mode\n```\ndredd --loglevel debug\n```\n"
  },
  {
    "path": "docs/newrelic.md",
    "content": "# Newrelic Integration\n\nIf you want to enable Newrelic monitoring on your Gate deployment, you just need to fill these additional keys on your environment variables:\n\n```\nNEWRELIC_LICENSE_KEY                - Your Newrelic license key\nNEWRELIC_APP_NAME                   - Your application name (identifer) on Newrelic\nNEWRELIC_AGENT_ENABLED              - Set it true if you want Newrelic agent to runs\n```\n"
  },
  {
    "path": "docs/oauth_setup.md",
    "content": "# OAuth Setup\n\n* Ensure that you have a registered account in Google Cloud Platform.\n* Enable `plus.googleapis.com` API at the following URL:\n\n    https://console.developers.google.com/apis/api/plus.googleapis.com/overview?project=[YOUR-PROJECT-NAME]\n* Create OAuth Client ID credentials at the following URL (with type `Web Application`):\n\n    https://console.developers.google.com/apis/credentials?project=[YOUR-PROJECT-NAME]\n* Configure Restrictions (Origins & Redirect URIs). We cannot use localhost in this section, therefore you can specify any arbitrary domain then configure your computer `/etc/hosts` file.\n\n    Example if you are running on `http://example.com:4000`:\n    * http://example.com:4000\n    * http://example.com:4000/users/auth/google_oauth2/callback\n* Put client_id and client_secret on `GATE_OAUTH_CLIENT_ID` and `GATE_OAUTH_CLIENT_SECRET` respectively\n* Update your `GATE_SERVER_URL` to `http://example.com:4000`\n* Specify your e-mail domain on `GATE_HOSTED_DOMAIN` and `GATE_HOSTED_DOMAINS`, for instance if your e-mail address is  `test123@gmail.com` then the value should be `gmail.com`\n* Leave `SIGN_IN_TYPE` empty\n"
  },
  {
    "path": "dredd.yml",
    "content": "color: true\ndry-run: null\nhookfiles: 'api_blueprint/hooks/dredd_hooks.rb'\nlanguage: ruby\nrequire: null\nserver: './api_blueprint/bin/dredd_server.sh'\nserver-wait: 20\ninit: false\ncustom: {}\nnames: false\nonly: []\nreporter: []\noutput: []\nheader: []\nsorted: false\nuser: null\ninline-errors: false\ndetails: false\nmethod: []\nloglevel: warning\npath: []\nhooks-worker-timeout: 5000\nhooks-worker-connect-timeout: 15000\nhooks-worker-connect-retry: 500\nhooks-worker-after-connect-wait: 100\nhooks-worker-term-timeout: 5000\nhooks-worker-term-retry: 500\nhooks-worker-handler-host: 127.0.0.1\nhooks-worker-handler-port: 61321\nconfig: ./dredd.yml\nblueprint: 'api_blueprint/*.apib'\nendpoint: 'http://localhost:9865'\n"
  },
  {
    "path": "lib/assets/.keep",
    "content": ""
  },
  {
    "path": "lib/tasks/.keep",
    "content": ""
  },
  {
    "path": "lib/tasks/app.rake",
    "content": "namespace :app do\n  desc \"Common app related tasks\"\n\n  task :init do\n    cp '.env.example', '.env'\n    p 'Please read information @ README.md - Setting up Environment Variables'\n  end\n\n  task :setup do\n    sh \"bundle install\"\n    sh \"bundle exec rake db:drop db:create db:migrate\"\n    sh \"RAILS_ENV=test bundle exec rake db:migrate\"\n    sh \"RAILS_ENV=test bundle exec rspec\"\n  end\nend\n"
  },
  {
    "path": "lib/tasks/setup.rake",
    "content": "namespace :setup do\n  desc \"Setup open vpn config\"\n\n  task :openvpn => [:key_dir, :certificate_authority] do\n  end\n\n  task :key_dir do\n    key_dir = Net::Openvpn::Generators::Keys::Directory.new\n    key_dir.generate\n    puts \"#{key_dir} successfully generated\" if key_dir.exist?\n  end\n\n  task :certificate_authority do\n    ca = Net::Openvpn::Generators::Keys::Authority.new\n    ca.generate\n    ca.exist?\n    puts \"#{ca} successfully generated\" if ca.exist?\n  end\n\n  task :server do\n    keys = Net::Openvpn::Generators::Keys::Server.new(\"swzvpn04\")\n    keys.generate\n    keys.exist?\n    keys.valid?\n  end\n\n  # example rake setup:client['ajey']\n  task :client , [:name] => :environment do |task, args|\n    puts \"generating key for #{args.name}\"\n    keys = Net::Openvpn::Generators::Keys::Client.new(args.name)\n    keys.generate\n    keys.exist?\n    keys.valid?\n    puts \"key for #{args.name} successfully generated\" if keys.valid?\n  end\nend\n"
  },
  {
    "path": "lib/tasks/users.rake",
    "content": "namespace :users do\n  desc 'fill user_login_id with email domain prefix'\n  task migrate_user_login_id: :environment do\n    User.all.each do |user|\n      user.user_login_id = user.email.split('@').first\n      user.save!\n      puts \"Migrating #{user.email}\"\n    end\n  end\n\n  desc 'imports users and prints their keys'\n  task import_csv: :environment do\n    require 'csv'\n    CSV.foreach('users.csv') do |row|\n      # Name, UserName\n      key = User.add_temp_user(row[0], row[1])\n      puts \"#{key}, #{row[0]}, #{row[1]}, #{row[2]}\"\n    end\n  end\n\n  desc 'purge users whom have been deactivated for more than certain time'\n  task :purge_inactive, [:ignore_time] => :environment do |_t, args|\n    users = User.\n      joins(:group_associations).\n      where(active: false).\n      where('group_associations.id IS NOT NULL')\n\n    if args[:ignore_time] != 'true'\n      users = users.where('deactivated_at <= :time_ago', time_ago: Time.now - 15.days)\n    end\n\n    users.each do |user|\n      puts \"Purging #{user.name} - #{user.email}\" if !Rails.env.test?\n      user.purge!\n    end\n  end\n\n  desc 'revoke expired group membership'\n  task revoke_expired_membership: :environment do\n    GroupAssociation.revoke_expired\n  end\n\n  task add_level1: :environment do\n    require 'csv'\n    group = Group.where(name: 'gopay_kyc_lvl_1').first\n    CSV.foreach('users.csv') do |row|\n      # Name, UserName\n      user_name = row[1].strip!\n      user = User.get_user user_name\n      puts user.get_user_unix_name + ' ' + group.name\n      user.groups << group\n      user.save!\n    end\n\n    @response = Group.get_all_response.to_json\n    REDIS_CACHE.set(GROUP_ALL_RESPONSE, @response)\n    REDIS_CACHE.expire(GROUP_ALL_RESPONSE, REDIS_KEY_EXPIRY)\n    @response = User.get_all_shadow_response.to_json\n    REDIS_CACHE.set(SHADOW_ALL_RESPONSE, @response)\n    REDIS_CACHE.expire(SHADOW_ALL_RESPONSE, REDIS_KEY_EXPIRY)\n    @response = User.get_all_passwd_response.to_json\n    REDIS_CACHE.set(PASSWD_ALL_RESPONSE, @response)\n    REDIS_CACHE.expire(PASSWD_ALL_RESPONSE, REDIS_KEY_EXPIRY)\n  end\n\n  task add_level2: :environment do\n    require 'csv'\n    group = Group.where(name: 'gopay_kyc_lvl_2').first\n    CSV.foreach('users.csv') do |row|\n      # Name, UserName\n      user_name = row[1].strip!\n      user = User.get_user user_name\n      puts user.get_user_unix_name + ' ' + group.name\n      user.groups << group\n      user.save!\n    end\n\n    @response = Group.get_all_response.to_json\n    REDIS_CACHE.set(GROUP_ALL_RESPONSE, @response)\n    REDIS_CACHE.expire(GROUP_ALL_RESPONSE, REDIS_KEY_EXPIRY)\n    @response = User.get_all_shadow_response.to_json\n    REDIS_CACHE.set(SHADOW_ALL_RESPONSE, @response)\n    REDIS_CACHE.expire(SHADOW_ALL_RESPONSE, REDIS_KEY_EXPIRY)\n    @response = User.get_all_passwd_response.to_json\n    REDIS_CACHE.set(PASSWD_ALL_RESPONSE, @response)\n    REDIS_CACHE.expire(PASSWD_ALL_RESPONSE, REDIS_KEY_EXPIRY)\n  end\nend\n"
  },
  {
    "path": "lib/tasks/vpn.rake",
    "content": "namespace :users do\n\n  desc \"migrate vpn user from old group to new groups\"\n  task migrate_vpns: :environment do\n\n    vpns = Vpn.all\n\n    vpns.each do |vpn|\n      admin_groups = vpn.groups\n      #create a new group for VPN\n      group = Group.create(name: \"#{vpn.name}_access_group\", description: \"VPN Access group\")\n      #Assign VPN to group\n      #get all vpn administrators\n      admins = []\n      admin_groups.each do |admin_group|\n        admin_group.group_admins.each do |group_admin|\n          admins << group_admin.user\n        end\n      end\n\n      #add all vpn administrators to new group as admin\n      admins.each do |user|\n        group.add_admin user\n      end\n      #get all vpn users\n      #add all vpn users to new group\n      vpn.users.each do |user|\n        user.groups << group\n      end\n      group.save!\n\n      @vpn.groups << group\n    end\n  end\nend\n\n"
  },
  {
    "path": "lib/vpn/mobileconfig.erb",
    "content": "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\n  <!-- Set the name to whatever you like, it is used in the profile list on the device -->\n  <key>PayloadDisplayName</key>\n  <string><%= organization_name %></string>\n\n  <!-- This is a reverse-DNS style unique identifier used to detect duplicate profiles -->\n  <key>PayloadIdentifier</key>\n  <string><%= reverse_vpn_url %></string>\n\n  <!-- A globally unique identifier, use uuidgen on Linux/Mac OS X to generate it -->\n  <key>PayloadUUID</key>\n  <string><%= organization_static %></string>\n\n  <key>PayloadType</key>\n  <string>Configuration</string>\n\n  <key>PayloadVersion</key>\n  <integer>1</integer>\n\n  <key>PayloadContent</key>\n  <array>\n\n    <!-- It is possible to add multiple VPN payloads with different identifiers/UUIDs and names -->\n    <% for vpn in payload_content %>\n\n      <dict>\n\n        <!-- This is an extension of the identifier given above -->\n        <key>PayloadIdentifier</key>\n        <string><%= vpn['payload_identifier'] %></string>\n\n        <!-- A globally unique identifier for this payload -->\n        <key>PayloadUUID</key>\n        <string><%= vpn['payload_uuid'] %></string>\n\n        <key>PayloadType</key>\n        <string>com.apple.vpn.managed</string>\n\n        <key>PayloadVersion</key>\n        <integer>1</integer>\n\n        <!-- This is the name of the VPN connection as seen in the VPN application later -->\n        <key>UserDefinedName</key>\n        <string><%= vpn['user_defined_name'] %></string>\n\n        <key>VPNType</key>\n        <string>IKEv2</string>\n\n        <key>IKEv2</key>\n        <dict>\n\n          <!-- This is the hostname or IP address of VPN server. Chosing IP address can avoid issues with client DNS resolvers and speed up connection process. -->\n          <key>RemoteAddress</key>\n          <string><%= vpn['ikev2']['remote_address'] %></string>\n\n          <!-- leftid in ipsec.conf -->\n          <key>RemoteIdentifier</key>\n          <string><%= vpn['ikev2']['remote_identifier'] %></string>\n\n          <key>DeadPeerDetectionRate</key>\n          <string>High</string>\n\n          <key>AuthenticationMethod</key>\n          <string>Username</string>\n\n          <key>NATKeepAliveInterval</key>\n          <integer>30</integer>\n\n          <key>NATKeepAliveOffloadEnable</key>\n          <true/>\n\n          <key>OnDemandEnabled</key>\n          <integer>0</integer>\n\n          <key>OnDemandRules</key>\n          <array>\n\n            <dict>\n\n             <key>Action</key>\n             <string>Connect</string>\n\n            </dict>\n          </array>\n\n          <key>ExtendedAuthEnabled</key>\n          <integer>1</integer>\n\n          <!-- Username and password from ipsec.secrets -->\n          <key>AuthName</key>\n          <string><%= vpn['ikev2']['auth_name'] %></string>\n\n        </dict>\n\n        <key>DNS</key>\n        <dict>\n\n          <key>SearchDomains</key>\n          <array>\n            <% for search_domain in vpn['dns']['search_domains'] %>\n              <string><%= search_domain %></string>\n            <% end %>\n          </array>\n\n          <key>ServerAddresses</key>\n          <array>\n            <% for server_address in vpn['dns']['server_addresses'] %>\n              <string><%= server_address %></string>\n            <% end %>\n          </array>\n\n          <key>SupplementalMatchDomains</key>\n          <array>\n            <% for supplemental_match_domain in vpn['dns']['supplemental_match_domains'] %>\n              <string><%= supplemental_match_domain %></string>\n            <% end %>\n          </array>\n\n        </dict>\n\n      </dict>\n    <% end %>\n  </array>\n</dict>\n</plist>\n"
  },
  {
    "path": "lib/vpn/mobileconfig.rb",
    "content": "require 'erb'\nrequire 'vpn/namespace'\nrequire 'openssl'\n\nclass Mobileconfig\n  def generate (vpns, user)\n    mobileconfig_template = File.read(\"#{Rails.root}/lib/vpn/mobileconfig.erb\")\n\n    vpn_hash = []\n    vpns.each do |vpn|\n      vpn_hash << {\n        \"payload_identifier\" => \"#{vpn.host_name.split('.').reverse.join('.')}.conf\",\n        \"payload_uuid\" => vpn.uuid,\n        \"user_defined_name\" => vpn.name,\n        \"ikev2\" => {\n          \"remote_address\" => vpn.host_name,\n          \"remote_identifier\" => vpn.host_name,\n          \"auth_name\" => user.email.split('@').first\n        },\n        \"dns\" => {\n          \"server_addresses\" => vpn.vpn_domain_name_servers.collect{ |vdns| vdns.server_address },\n          \"search_domains\" => vpn.vpn_search_domains.collect{ |vsd| vsd.search_domain },\n          \"supplemental_match_domains\" => vpn.vpn_supplemental_match_domains.collect{ |vsmd| vsmd.supplemental_match_domain },\n        }\n      }\n    end\n\n    confighash = {\n      organization_name: ENV['GATE_ORGANIZATION_NAME'] + ' IKEv2 VPN Configuration',\n      reverse_vpn_url: ENV['GATE_SERVER_URL'].split('.').reverse.join('.'),\n      organization_static: ENV['GATE_ORGANIZATION_STATIC'],\n      payload_content: vpn_hash,\n    }\n\n    namespace = Namespace.new(confighash)\n\n    mobileconfig_unsigned = ERB.new(mobileconfig_template).result(namespace.get_binding)\n\n    sign_mobileconfig(mobileconfig_unsigned)\n  end\n\n  private\n\n  def sign_mobileconfig(mobileconfig)\n    private_key = Base64.decode64(ENV['GATE_VPN_SSL_PVTKEY'])\n    signing_cert = Base64.decode64(ENV['GATE_VPN_SSL_CERT'])\n    cross_signed_cert = Base64.decode64(ENV['GATE_VPN_SSL_XSIGNED'])\n\n    key = OpenSSL::PKey::RSA.new private_key\n    cert = OpenSSL::X509::Certificate.new signing_cert\n    cross_signed = OpenSSL::X509::Certificate.new cross_signed_cert\n\n    OpenSSL::PKCS7.sign(cert, key, mobileconfig, [cross_signed]).to_der\n  end\nend\n"
  },
  {
    "path": "lib/vpn/namespace.rb",
    "content": "class Namespace\n  def initialize(hash)\n    hash.each do |key, value|\n      singleton_class.send(:define_method, key) { value }\n    end\n  end\n  def get_binding\n    binding\n  end\nend\n"
  },
  {
    "path": "public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The page you were looking for doesn't exist (404)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  body {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/404.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>The page you were looking for doesn't exist.</h1>\n      <p>You may have mistyped the address or the page may have moved.</p>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "public/422.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The change you wanted was rejected (422)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  body {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/422.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>The change you wanted was rejected.</h1>\n      <p>Maybe you tried to change something you didn't have access to.</p>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "public/500.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>We're sorry, but something went wrong (500)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  body {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/500.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>We're sorry, but something went wrong.</h1>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "public/robots.txt",
    "content": "# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file\n#\n# To ban all spiders from the entire site uncomment the next two lines:\nUser-agent: *\nDisallow: /\n"
  },
  {
    "path": "scripts/gen-client-conf",
    "content": "#!/bin/bash\n\nif [[ $# -lt 1 ]]; then\n  echo \"$0 <user-name>\"\n  exit 1\nfi\n\ncd /etc/openvpn/keys\ncp /opt/vpnkeys/$1.tar.gz ./\ntar -xf $1.tar.gz\nrm $1.ovpn\ntouch $1.ovpn\n\ncat > /etc/openvpn/keys/$1.ovpn <<EOF\nclient\ndev tun\nproto udp\nremote <<your server name>> 1194\nresolv-retry infinite\nnobind\npersist-key\npersist-tun\nca ca.crt\ncert $1.crt\nkey $1.key\ncomp-lzo\nverb 3\nauth-user-pass\nauth-nocache\ntun-mtu 1400\nmssfix 1400\n\n#add your routes here\nroute <<x.1.0.0 255.255.0.0>>\nroute <<x.2.0.0 255.255.0.0>>\nEOF\n\ntar zcf $1.tar.gz ca.crt $1.crt $1.key $1.ovpn\nchmod 0600 $1.tar.gz\n\nmkdir -p  /opt/vpnkeys/\ncp $1.tar.gz /opt/vpnkeys/\nrm $1.*\n"
  },
  {
    "path": "scripts/gen-client-keys",
    "content": "#!/bin/bash\n\nset -e\n\nif [[ $# -lt 1 ]]; then\n  echo \"$0 <user-name>\"\n  exit 1\nfi\n\nsource vars\n\ntouch /etc/openvpn/keys/$1.ovpn\n\ncat > /etc/openvpn/keys/$1.ovpn <<EOF\nclient\ndev tun\nproto udp\nremote <<your server name>> 1194\nresolv-retry infinite\nnobind\npersist-key\npersist-tun\nca ca.crt\ncert $1.crt\nkey $1.key\ncomp-lzo\nverb 3\nauth-user-pass\nauth-nocache\ntun-mtu 1400\nmssfix 1400\n#Add your routes\nroute <<x.1.0.0 255.255.0.0>>\nroute <<x.4.0.0 255.255.0.0>>\nEOF\n\n./pkitool $1\ncd /etc/openvpn/keys/ && tar zcf $1.tar.gz ca.crt $1.crt $1.key $1.ovpn\nchmod 0600 $1.tar.gz\n\nmkdir -p  /opt/vpnkeys/\ncp $1.tar.gz /opt/vpnkeys/\nrm $1.*\n"
  },
  {
    "path": "setup.sh",
    "content": "rake db:setup && rails s\n"
  },
  {
    "path": "spec/clients/data_dog_client_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe DataDogClient do\n\n  let(:email) { Faker::Internet.email }\n  let(:org) { create(:organisation) }\n  let(:app_key) { Faker::Internet.password(min_length: 8) }\n  let(:api_key) { Faker::Internet.password(min_length: 8) }\n  let(:base_url) { 'https://api.datadoghq.com/api/v1' }\n  let(:client) { DataDogClient.new(app_key, api_key) }\n  let(:auth_str) do\n    \"api_key=#{api_key}&application_key=#{app_key}\"\n  end\n  let(:headers) do\n    {\n      'Accept' => 'application/json',\n      'Content-Type' => 'application/json',\n    }\n  end\n\n  describe 'get_user' do\n    it 'get user details for registered email address' do\n      stub_request(:get, \"#{base_url}/user/#{email}?#{auth_str}\").\n        to_return(status: 200, body: { user: { handle: email } }.to_json)\n      expect(client.get_user(email)['handle']).to eq(email)\n    end\n\n    it 'get a blank hash for unregistered email address' do\n      stub_request(:get, \"#{base_url}/user/#{email}?#{auth_str}\").\n        with(headers: headers).\n        to_return(status: 401)\n      expect(client.get_user(email)).to eq({})\n    end\n  end\n\n  describe 'new_user' do\n    let(:params) { { handle: email }.to_json }\n    it 'create a new user' do\n      stub_request(:post, \"#{base_url}/user?#{auth_str}\").\n        with(body: params, headers: headers).\n        to_return(status: 200, body: { user: { handle: email } }.to_json)\n      expect(client.new_user(email)['handle']).to eq(email)\n    end\n\n    it 'shouldn\\'t create user if already exists' do\n      stub_request(:post, \"#{base_url}/user?#{auth_str}\").\n        with(body: params, headers: headers).\n        to_return(status: 409)\n      expect(client.new_user(email)).to eq({})\n    end\n  end\n\n  describe 'activate_user' do\n    let(:params) { { email: email, disabled: false }.to_json }\n    it 'activates user and returns user object if email is found' do\n      stub_request(:put, \"#{base_url}/user/#{email}?#{auth_str}\").\n        with(body: params, headers: headers).\n        to_return(status: 200, body: { user: { handle: email } }.to_json)\n      expect(client.activate_user(email)['handle']).to eq(email)\n    end\n\n    it 'returns empty object if email is not found' do\n      stub_request(:put, \"#{base_url}/user/#{email}?#{auth_str}\").\n        with(body: params, headers: headers).\n        to_return(status: 500)\n      expect(client.activate_user(email)).to eq({})\n    end\n  end\n\n  describe 'deactivate_user' do\n    let(:params) { { email: email, disabled: true }.to_json }\n    it 'deactivates user and returns user object if email is found' do\n      stub_request(:put, \"#{base_url}/user/#{email}?#{auth_str}\").\n        with(body: params, headers: headers).\n        to_return(status: 200, body: { user: { handle: email } }.to_json)\n      expect(client.deactivate_user(email)['handle']).to eq(email)\n    end\n\n    it 'returns empty object if email is not found' do\n      stub_request(:put, \"#{base_url}/user/#{email}?#{auth_str}\").\n        with(body: params, headers: headers).\n        to_return(status: 500)\n      expect(client.deactivate_user(email)).to eq({})\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/admin_controller_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe AdminController, type: :controller do\n\nend\n"
  },
  {
    "path": "spec/controllers/api/v1/api_controller_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe ::Api::V1::BaseController, type: :controller do\n\n  controller(::Api::V1::BaseController) do\n    def index\n      head :ok\n    end\n  end\n\n  describe 'Authentication' do\n    before(:each) do\n      @user = build(:user)\n      @user.access_token = build(:access_token)\n      @user.save\n      @token = @user.access_token.token\n    end\n    it 'gives 200 when access token is valid' do\n      get :index, params: { access_token: @token }\n      expect(response.status).to eq(200)\n    end\n\n    it 'gives 401 when access token is in valid' do\n      get :index, params: { access_token: \"foo\" }\n      expect(response.status).to eq(401)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/api/v1/endpoints_controller_spec.rb",
    "content": "require 'rails_helper'\n\ndescribe ::Api::V1::EndpointsController, type: :controller do\n  before(:each) do\n    @admin = build(:admin_user)\n    @admin.access_token = build(:access_token)\n    @admin.save!\n    @admin_token = @admin.access_token.token\n  end\n\n  let(:user) do\n    user = build(:user, admin: false)\n    user.access_token = build(:access_token)\n    user.save!\n    user\n  end\n\n  let(:valid_attributes) do\n    {\n      path: '/',\n      method: 'GET',\n    }\n  end\n\n  describe '#create' do\n    context 'authenticated as admin' do\n      context 'given valid param' do\n        it 'should return http status 200' do\n          post :create, params: { endpoint: valid_attributes, access_token: @admin_token }\n          expect(response).to have_http_status(200)\n        end\n\n        it 'should create endpoint' do\n          post :create, params: { endpoint: valid_attributes, access_token: @admin_token }\n          endpoint = Endpoint.find_by(valid_attributes)\n          expect(endpoint).not_to be nil\n        end\n\n        it 'should return proper response' do\n          post :create, params: { endpoint: valid_attributes, access_token: @admin_token }\n          endpoint = Endpoint.find_by(valid_attributes)\n          expect(response.body).to eq({\n                                        id: endpoint.id,\n                                        path: endpoint.path,\n                                        method: endpoint.method,\n                                      }.to_json)\n        end\n      end\n\n      context 'given invalid param' do\n        it 'should return http status 422' do\n          invalid_attributes = valid_attributes\n          invalid_attributes[:method] = 'JUMP'\n          post :create, params: { endpoint: invalid_attributes, access_token: @admin_token }\n          expect(response).to have_http_status(422)\n        end\n\n        it 'should return proper response' do\n          invalid_attributes = valid_attributes\n          invalid_attributes[:method] = 'JUMP'\n          invalid_attributes[:path] = 'not-a-path'\n          post :create, params: { endpoint: invalid_attributes, access_token: @admin_token }\n          response_body = JSON.parse response.body\n          expect(response_body['status']).to include('method', 'path')\n        end\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should return http status 403' do\n        post :create, params: { endpoint: valid_attributes, access_token: user.access_token.token }\n        expect(response).to have_http_status(403)\n      end\n    end\n\n    context 'unauthenticated' do\n      it 'should return http status 401' do\n        post :create, params: { endpoint: valid_attributes }\n        expect(response.status).to eq(401)\n      end\n    end\n  end\n\n  describe '#add_group' do\n    let(:endpoint) { create(:endpoint) }\n          require 'pry'; binding.pry\n    let(:group) { create(:group) }\n          require 'pry'; binding.pry\n    context 'authenticated as admin' do\n      context 'given valid group id' do\n        it 'should return http status 200' do\n          require 'pry'; binding.pry\n          post :add_group, params: { id: endpoint.id,\n                                     group: { id: group.id },\n                                     access_token: @admin_token }\n          require 'pry'; binding.pry\n          expect(response).to have_http_status 200\n        end\n\n        it 'should add group to endpoint' do\n          post :add_group, params: { id: endpoint.id,\n                                     group: { id: group.id },\n                                     access_token: @admin_token }\n          group_ids = endpoint.groups.map &:id\n          expect(group_ids).to include group.id\n        end\n\n        context 'twice with same value' do\n          it 'should return http status 422' do\n            post :add_group, params: { id: endpoint.id,\n                                       group: { id: group.id },\n                                       access_token: @admin_token }\n            post :add_group, params: { id: endpoint.id,\n                                       group: { id: group.id },\n                                       access_token: @admin_token }\n            expect(response).to have_http_status 422\n          end\n        end\n      end\n\n      context 'given invalid endpoint id' do\n        it 'should return http status 404' do\n          post :add_group, params: { id: 777,\n                                     group: { id: group.id },\n                                     access_token: @admin_token }\n          expect(response).to have_http_status 404\n        end\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should return http status 403' do\n        post :add_group, params: { id: endpoint.id,\n                                   group: { id: group.id },\n                                   access_token: user.access_token.token }\n        expect(response).to have_http_status 403\n      end\n    end\n\n    context 'unauthenticated' do\n      it 'should return http status 401' do\n        post :add_group, params: { id: endpoint.id, group: { id: group.id } }\n        expect(response.status).to eq(401)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/api/v1/groups_controller_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe ::Api::V1::GroupsController, type: :controller do\n  let(:valid_attributes) do\n    { name: 'jumbo' }\n  end\n\n  before(:each) do\n    @admin = build(:admin_user)\n    @admin.access_token = build(:access_token)\n    @admin.save\n    @admin_token = @admin.access_token.token\n\n    @user = build(:user, admin: false)\n    @user.access_token = build(:access_token)\n    @user.save\n    @user_token = @user.access_token.token\n  end\n\n  describe '#create' do\n    context 'authenticated as admin' do\n      context 'with valid_attributes' do\n        it 'should create groups' do\n          post :create, params: { group: valid_attributes, access_token: @admin_token }\n          group = Group.where(name: valid_attributes[:name]).first\n          expect(group.blank?).to eq(false)\n          expect(group.name).to eq(valid_attributes[:name])\n        end\n\n        it 'should return proper response' do\n          post :create, params: { group: valid_attributes, access_token: @admin_token }\n          expect(response.status).to eq(200)\n          group = Group.where(name: valid_attributes[:name]).first\n          obj = JSON.parse(response.body)\n          expect(obj['id']).to eq group.id\n          expect(obj['name']).to eq group.name\n        end\n      end\n\n      context 'group already exist' do\n        it 'should return existing group id' do\n          existing_group = create(:group, name: valid_attributes[:name])\n          post :create,  params: { group: valid_attributes, access_token: @admin_token }\n          expect(response.status).to eq(422)\n          expect(response.body).to eq({\n            status: 'group already exist',\n            id: existing_group.id,\n            name: existing_group.name,\n          }.to_json)\n        end\n      end\n    end\n\n    context 'unauthenticated' do\n      it 'should return 401 http status' do\n        post :create, params: { group: valid_attributes, access_token: 'foo' }\n        expect(response.status).to eq(401)\n      end\n    end\n  end\n\n  describe '#add_user' do\n    before(:each) do\n      @group = create(:group)\n      @new_user = create(:user, admin: false)\n    end\n\n    context 'invalid param' do\n      context 'group id not found' do\n        it 'should return 404 http status' do\n          post :add_user, params: {\n            id: 666,\n            user_id: @new_user.id,\n            access_token: @admin_token,\n          }\n          expect(response.status).to eq 404\n        end\n      end\n\n      context 'invalid user id' do\n        it 'should return 422 http status' do\n          post :add_user, params: {\n            id: @group.id,\n            user_id: 'rand',\n            access_token: @admin_token,\n          }\n          expect(response.status).to eq 422\n        end\n      end\n    end\n\n    context 'authenticated as admin' do\n      context 'valid param' do\n        it 'should return proper response' do\n          post :add_user, params: {\n            id: @group.id,\n            user_id: @new_user.id,\n            access_token: @admin_token,\n          }\n          expect(response.status).to eq 204\n        end\n\n        it 'should add user to group' do\n          post :add_user, params: {\n            id: @group.id,\n            user_id: @new_user.id,\n            access_token: @admin_token,\n          }\n          expect(@group.users).to contain_exactly @new_user\n        end\n\n        it 'should set membership expiration date' do\n          post :add_user, params: {\n            id: @group.id,\n            user_id: @new_user.id,\n            expiration_date: '2019-10-10',\n            access_token: @admin_token,\n          }\n          membership = @group.group_associations.find_by(user_id: @new_user.id)\n          expect(membership.expiration_date).to eq Date.parse('2019-10-10')\n        end\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should not add user to group' do\n        post :add_user, params: {\n          id: @group.id,\n          user_id: @new_user.id,\n          access_token: @user_token,\n        }\n        expect(@group.users).to be_empty\n      end\n\n      it 'should return 401 http status' do\n        post :add_user, params: {\n          id: @group.id,\n          user_id: @new_user.id,\n          access_token: @user_token,\n        }\n        expect(response.status).to eq 401\n      end\n    end\n\n    context 'authenticated as group admin' do\n      before(:each) do\n        @group.add_admin @user\n      end\n      it 'should add user to group' do\n        post :add_user, params: {\n          id: @group.id,\n          user_id: @new_user.id,\n          access_token: @user_token,\n        }\n        expect(@group.users).to contain_exactly @new_user\n      end\n    end\n\n    context 'unauthenticated' do\n      it 'should return 401 http status' do\n        post :add_user, params: {\n          id: @group.id,\n          user_id: @new_user.id,\n          access_token: 'foo',\n        }\n        expect(response.status).to eq 401\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/api/v1/users_controller_spec.rb",
    "content": "require 'rails_helper'\n\ndescribe ::Api::V1::UsersController, type: :controller do\n  let(:valid_attributes) do\n    { name: 'jumbo', email: 'jumbo.aux' }\n  end\n\n  before(:each) do\n    @admin = build(:user)\n    @admin.access_token = build(:access_token)\n    @admin.save\n    @admin_token = @admin.access_token.token\n\n    @user = build(:user, admin: false)\n    @user.access_token = build(:access_token)\n    @user.save\n    @user_token = @user.access_token.token\n  end\n\n  describe '#create' do\n    describe 'authenticated as admin' do\n      context 'given valid attributes' do\n        it 'should create user' do\n          post :create, params: { user: valid_attributes, access_token: @admin_token }\n          expect(response.status).to eq(200)\n          user = User.where(name: valid_attributes[:name]).first\n          expect(user.blank?).to eq(false)\n          expect(user.name).to eq(valid_attributes[:name])\n        end\n      end\n    end\n\n    describe 'authenticated as non admin' do\n      it 'should return http status 403' do\n        post :create, params: { user: valid_attributes, access_token: @user_token }\n        expect(response).to have_http_status 403\n      end\n    end\n\n    describe 'unauthenticated' do\n      it 'should return http status 401' do\n        post :create, params: { user: valid_attributes, access_token: 'foo' }\n        expect(response.status).to eq(401)\n      end\n    end\n  end\n\n  describe '#deactivate' do\n    context 'authenticated as admin' do\n      context 'given valid params' do\n        it 'should return http status 204' do\n          target_user = create(:user, admin: false)\n          patch :deactivate, params: { id: target_user.id, access_token: @admin_token }\n          expect(response).to have_http_status 204\n        end\n\n        it 'should deactivate user' do\n          target_user = create(:user, admin: false)\n          patch :deactivate, params: { id: target_user.id, access_token: @admin_token }\n          target_user.reload\n          expect(target_user.active).to eq false\n        end\n      end\n\n      context 'given invalid user id' do\n        it 'should return http status 404' do\n          patch :deactivate, params: { id: 777, access_token: @admin_token }\n          expect(response).to have_http_status 404\n        end\n      end\n\n      context 'given last admin id' do\n        it 'should return http status 422' do\n          patch :deactivate, params: { id: @admin.id, access_token: @admin_token }\n          expect(response).to have_http_status 422\n        end\n      end\n    end\n\n    context 'authenticated as user that has endpoint access' do\n      it 'should return http status 204' do\n        group = create(:group)\n        endpoint = create(:endpoint, path: '/api/v1/users/:id/deactivate', method: 'PATCH')\n        group.endpoints << endpoint\n        group.users << @user\n        target_user = create(:user, admin: false)\n        patch :deactivate, params: { id: target_user.id, access_token: @user_token }\n        expect(response).to have_http_status 204\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should return http status 403' do\n        target_user = create(:user, admin: false)\n        patch :deactivate, params: { id: target_user.id, access_token: @user_token }\n        expect(response).to have_http_status 403\n      end\n    end\n  end\n\n  describe 'Update Profile' do\n    before(:each) do\n      require 'openssl'\n      rsa_key = OpenSSL::PKey::RSA.new(2048)\n      @public_key = rsa_key.public_key.to_pem\n    end\n\n    context 'authenticated as admin' do\n      it 'should return 200 http status code' do\n        name = 'test_name'\n        product_name = 'test_product'\n        post :update, params: {\n          access_token: @admin_token,\n          public_key: @public_key,\n          product_name: product_name,\n          name: name,\n          email: @admin.email,\n        }\n        expect(response.status).to eq(200)\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should return 401 http status code' do\n        target_user = create(:user)\n        name = 'test_name'\n        product_name = 'test_product'\n        post :update, params: {\n          access_token: @user_token,\n          public_key: @public_key,\n          product_name: product_name,\n          name: name,\n          email: target_user.email,\n        }\n        expect(response.status).to eq(401)\n      end\n    end\n\n    context 'authenticated as itself' do\n      it 'should return 200 http status code' do\n        name = 'test_name'\n        product_name = 'test_product'\n        post :update, params: {\n          access_token: @user_token,\n          public_key: @public_key,\n          product_name: product_name,\n          name: name,\n          email: @user.email,\n        }\n        expect(response.status).to eq(200)\n      end\n    end\n  end\n\n  describe 'User Details' do\n    context 'success' do\n      it 'should return 200 http status code' do\n        user = create(\n          :user,\n          name: 'foo',\n          user_login_id: 'foob',\n          email: 'foo@foobar.com',\n          reset_password_token: 'test1'\n        )\n        get :show, params: { email: user.email, access_token: @admin_token, format: :json }\n        expect(response.status).to eq(200)\n      end\n\n      it 'should return the user details' do\n        user = create(\n          :user,\n          name: 'foo',\n          user_login_id: 'foob',\n          email: 'foo@foobar.com',\n          reset_password_token: 'test1',\n          product_name: 'fooproduct'\n        )\n        get :show, params: {\n          email: user.email,\n          access_token: @admin_token,\n          format: :json,\n        }\n        reponse_json = JSON(response.body)\n        expect(reponse_json['product_name']).to eq(user.product_name)\n      end\n\n      it 'should not display user secrets' do\n        user = create(\n          :user,\n          name: 'foo',\n          user_login_id: 'foob',\n          email: 'foo@foobar.com',\n          reset_password_token: 'test1',\n          product_name: 'fooproduct',\n          created_at: '2018-01-11T21:30:41.000Z',\n          updated_at: '2018-01-11T21:30:41.000Z'\n        )\n        get :show, params: { email: user.email, access_token: @admin_token, format: :json }\n        response_json = JSON(response.body)\n        %w(\n          email uid name active admin home_dir shell public_key user_login_id\n          product_name groups\n        ).each do |col|\n          expect(response_json.keys.include?(col)).to eq(true)\n        end\n      end\n\n      it 'should display active user' do\n        create(:user, name: 'foo', active: 0, user_login_id: 'foo', email: 'foo@foobar.com')\n        active_user = create(:user, name: 'foo', user_login_id: 'foo', email: 'foo@bar.com')\n        get :show, params: { username: 'foo', active: 1, 'access_token': @admin_token, format: :json }\n        response_json = JSON(response.body)\n        expect(response_json['email']).to eq(active_user.email)\n      end\n\n      it 'should display active user when query parameter active is true' do\n        create(:user, name: 'foo', active: 0, user_login_id: 'foo', email: 'foo@foobar.com')\n        active_user = create(:user, name: 'foo', user_login_id: 'foo', email: 'foo@bar.com')\n        get :show, params: { username: 'foo', active: true, 'access_token': @admin_token, format: :json }\n        response_json = JSON(response.body)\n        expect(response_json['email']).to eq(active_user.email)\n      end\n\n      it 'should display inactive user when query parameter active is false' do\n        inactive_user = create(\n          :user,\n          name: 'foo',\n          active: 0,\n          user_login_id: 'foo',\n          email: 'foo@foobar.com'\n        )\n        create(:user, name: 'foo', user_login_id: 'foo', email: 'foo@bar.com')\n        get :show, params: { username: 'foo', active: false, 'access_token': @admin_token, format: :json }\n        response_json = JSON(response.body)\n        expect(response_json['email']).to eq(inactive_user.email)\n      end\n    end\n\n    context 'failure' do\n      it 'should return http status code 404' do\n        get :show, params: { email: 'test@test.com', access_token: @admin_token, format: :json }\n        expect(response.status).to eq(404)\n      end\n\n      it 'should not authenticate user for invalid acces token' do\n        get :show, params: { email: 'test@test.com', access_token: 'invalid_access_token' }\n        expect(response.status).to eq(401)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/api/v1/vpns_controller_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe ::Api::V1::VpnsController, type: :controller do\n  let(:valid_attributes) {\n    {name: 'jumbo'}\n  }\n\n  before(:each) do\n    @user = build(:user)\n    @user.access_token = build(:access_token)\n    @user.save\n    @token = @user.access_token.token\n  end\n\n  describe 'Authenticated' do\n    describe 'Create Vpn' do\n      context 'with valid_attributes' do\n        it 'should create vpns' do\n          post :create,  params: {vpn: valid_attributes, access_token: @token}\n          vpn = Vpn.where(name: valid_attributes[:name]).first\n          expect(vpn.blank?).to eq(false)\n          expect(vpn.name).to eq(valid_attributes[:name])\n          expect(UUID.validate(vpn.uuid)).to eq(true)\n        end\n\n        it 'should return proper response' do\n          post :create,  params: {vpn: valid_attributes, access_token: @token}\n          expect(response.status).to eq(200)\n          vpn = Vpn.where(name: valid_attributes[:name]).first\n          obj = JSON.parse(response.body)\n          expect(obj['id']).to eq vpn.id\n          expect(obj['name']).to eq vpn.name\n          expect(obj['host_name']).to eq vpn.host_name\n          expect(obj['ip_address']).to eq vpn.ip_address\n        end\n      end\n    end\n\n    describe 'Assign Group to VPN' do\n      it 'should replace existing vpn group with new group' do\n        vpn = create(:vpn)\n        group_1 = create(:group)\n        group_2 = create(:group)\n        vpn.groups << group_1\n        vpn.groups << group_2\n        group_3 = create(:group)\n        post :assign_group,  params: {access_token: @token, id: vpn.id, group_id: group_3.id}\n        expect(vpn.groups.count).to eq 1\n        expect(vpn.groups.first).to eq group_3\n      end\n    end\n  end\n\n  describe 'Unauthenticated' do\n    it 'gives 401 when access token is invalid' do\n      post :create,  params: {vpn: valid_attributes, access_token: 'foo'}\n      expect(response.status).to eq(401)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/api_resources_controller_spec.rb",
    "content": "require 'rails_helper'\n\n\nRSpec.describe ApiResourcesController, type: :controller do\n\n  # This should return the minimal set of attributes required to create a valid\n  # ApiResource. As you add validations to ApiResource, be sure to\n  # adjust the attributes here as well.\n  let(:user) { FactoryBot.create(:user, name: \"foobar\", admin: true, user_login_id: \"foobar\", email: \"foobar@foobar.com\")  }\n  let(:group) { FactoryBot.create(:group, name: \"foobar_group\") }\n  let(:valid_attributes) do\n    {\n      name: \"sample_api\",\n      description: \"sample_api_description\",\n      access_key: \"xcz\",\n      user_id: user,\n      group_id: group\n    }\n  end\n\n  let(:invalid_attributes) {\n    { name: \"non sample api\", description: 100}\n  }\n\n  # This should return the minimal set of values that should be in the session\n  # in order to pass any filters (e.g. authentication) defined in\n  # ApiResourcesController. Be sure to keep this updated too.\n  let(:valid_session) { {} }\n\n  before(:each) do\n    sign_in user\n  end\n  describe \"GET #index\" do\n    it \"returns a success response\" do\n      api_resource = ApiResource.create! valid_attributes\n      get :index, params: {}, session: valid_session\n      expect(response).to be_success\n    end\n  end\n\n  describe \"GET #show\" do\n    it \"returns a success response\" do\n      api_resource = ApiResource.create! valid_attributes\n      get :show, params: {:id => api_resource.to_param}, session: valid_session\n      expect(response).to be_success\n    end\n  end\n\n  describe \"GET #new\" do\n    it \"returns a success response\" do\n      get :new, params: {}, session: valid_session\n      expect(response).to be_success\n    end\n  end\n\n  describe \"GET #edit\" do\n    it \"returns a success response\" do\n      api_resource = ApiResource.create! valid_attributes\n      get :edit, params: {:id => api_resource.to_param}, session: valid_session\n      expect(response).to be_success\n    end\n  end\n\n  describe \"POST #create\" do\n    context \"with valid params\" do\n      it \"creates a new ApiResource\" do\n        sign_in user\n\n        expect {\n          post :create, params: {:api_resource => valid_attributes}, session: valid_session\n        }.to change(ApiResource, :count).by(1)\n      end\n\n      it \"redirects to the created api_resource\" do\n        post :create, params: {:api_resource => valid_attributes}, session: valid_session\n        expect(response).to redirect_to(api_resource_path(assigns[:api_resource]))\n      end\n    end\n\n    context \"with invalid params\" do\n      it \"returns a success response (i.e. to display the 'new' template)\" do\n        post :create, params: {:api_resource => invalid_attributes}, session: valid_session\n        expect(response).to be_success\n      end\n    end\n  end\n\n  describe 'PUT #update' do\n    let(:new_attributes) { { name: 'new_name', access_key: 'xyz' } }\n    context 'authenticated as owner' do\n      it 'should updates the requested api_resource' do\n        api_resource = ApiResource.create! valid_attributes\n        api_resource.update(user: create(:user, admin: false))\n        sign_in api_resource.user\n        put :update, params: { id: api_resource.to_param, api_resource: new_attributes }\n        api_resource.reload\n        expect(api_resource.name).to eq('new_name')\n      end\n    end\n\n    context 'authenticated as admin' do\n      context 'with valid params' do\n        it 'updates the requested api_resource' do\n          api_resource = ApiResource.create! valid_attributes\n          put :update, params: { id: api_resource.to_param, api_resource: new_attributes }\n          api_resource.reload\n          expect(api_resource.name).to eq('new_name')\n        end\n\n        it 'redirects to the api_resource' do\n          api_resource = ApiResource.create! valid_attributes\n          put :update, params: { id: api_resource.to_param, api_resource: valid_attributes }\n          expect(response).to redirect_to(api_resources_url)\n        end\n      end\n\n      context 'with invalid params' do\n        it 'returns a success response (i.e. to display the \"edit\" template)' do\n          api_resource = ApiResource.create! valid_attributes\n          put :update, params: { id: api_resource.to_param, api_resource: invalid_attributes }\n          expect(response).to be_success\n        end\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should not update api resource' do\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        api_resource = ApiResource.create! valid_attributes\n        put :update, params: { id: api_resource.to_param, api_resource: new_attributes }\n        updated_api_resource = ApiResource.find(api_resource.to_param)\n        expect(updated_api_resource.to_json).to eq(api_resource.to_json)\n      end\n\n      context 'html response' do\n        it 'should return notice unauthorized access' do\n          non_admin = create(:user, admin: false)\n          sign_in non_admin\n          api_resource = ApiResource.create! valid_attributes\n          put :update, params: { id: api_resource.to_param, api_resource: new_attributes }\n          expect(flash[:notice]).to eq('Unauthorized access')\n        end\n      end\n\n      context 'json response' do\n        it 'should return 401 http status' do\n          non_admin = create(:user, admin: false)\n          sign_in non_admin\n          api_resource = ApiResource.create! valid_attributes\n          put :update, params: {\n            format: 'json',\n            id: api_resource.to_param,\n            api_resource: new_attributes,\n          }\n          expect(response).to have_http_status(401)\n        end\n      end\n    end\n  end\n\n  describe 'DELETE #destroy' do\n    context 'authenticated as admin' do\n      it 'destroys the requested api_resource' do\n        api_resource = ApiResource.create! valid_attributes\n        expect { delete :destroy, params: { id: api_resource.to_param } }.\n          to change(ApiResource, :count).by(-1)\n      end\n\n      it 'redirects to the api_resources list' do\n        api_resource = ApiResource.create! valid_attributes\n        delete :destroy, params: { id: api_resource.to_param }\n        expect(response).to redirect_to(api_resources_url)\n      end\n\n      it 'should flash notice success' do\n        api_resource = ApiResource.create! valid_attributes\n        delete :destroy, params: { id: api_resource.to_param }\n        expect(flash[:notice]).to eq('Api resource was successfully destroyed.')\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should flash notice unauthorized access' do\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        api_resource = ApiResource.create! valid_attributes\n        delete :destroy, params: { id: api_resource.to_param }\n        expect(flash[:notice]).to eq('Unauthorized access')\n      end\n\n      context 'json response' do\n        it 'should return 401 http status' do\n          non_admin = create(:user, admin: false)\n          sign_in non_admin\n          api_resource = ApiResource.create! valid_attributes\n          delete :destroy, params: { format: 'json', id: api_resource.to_param }\n          expect(response).to have_http_status(401)\n        end\n      end\n    end\n  end\n\n  describe 'Search for API Resources' do\n    it \"should return API Resources according to supplied search string\" do\n      api_resources = create_list(:api_resource, 3)\n      get :search, params: { q: \"API\" }\n      expect(JSON.parse(response.body)).to eq(api_resources.map{|m| {\"id\" => m.id, \"name\" => m.name}})\n    end\n  end\n\n  describe 'GET #regenerate_access_key' do\n    context 'authenticated as admin' do\n      it 'should regenerates access_key of the requested api_resource' do\n        api_resource = ApiResource.create! valid_attributes\n        old_hashed_access_key = api_resource.hashed_access_key\n        get :regenerate_access_key, params: { id: api_resource.to_param }\n        api_resource.reload\n        expect(api_resource.hashed_access_key).to_not eq old_hashed_access_key\n      end\n\n      it 'should redirects to the api_resource' do\n        api_resource = ApiResource.create! valid_attributes\n        get :regenerate_access_key, params: { id: api_resource.to_param }\n        expect(response).to redirect_to(api_resource_path(api_resource.id))\n      end\n    end\n\n    context 'authenticated as owner' do\n      it 'should regenerates access_key of the requested api_resource' do\n        api_resource = ApiResource.create! valid_attributes\n        api_resource.update(user: create(:user, admin: false))\n        sign_in api_resource.user\n        old_hashed_access_key = api_resource.hashed_access_key\n        get :regenerate_access_key, params: { id: api_resource.to_param }\n        api_resource.reload\n        expect(api_resource.hashed_access_key).to_not eq old_hashed_access_key\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should flash notice unauthorized access' do\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        api_resource = ApiResource.create! valid_attributes\n        get :regenerate_access_key, params: { id: api_resource.to_param }\n        expect(flash[:notice]).to eq('Unauthorized access')\n      end\n\n      context 'json response' do\n        it 'should return 401 http status' do\n          non_admin = create(:user, admin: false)\n          sign_in non_admin\n          api_resource = ApiResource.create! valid_attributes\n          get :regenerate_access_key, params: { format: :json, id: api_resource.to_param }\n          expect(response).to have_http_status(401)\n        end\n      end\n    end\n  end\n\n  describe \"Authenticate\" do\n    it \"should not authenticate if a user is NOT a member of API group\" do\n      user = create :user\n      access_token = create :access_token\n      user.access_token = access_token\n      user.save!\n      user.reload\n      group = create :group\n      api_resource = ApiResource.create! valid_attributes\n      api_resource.group = group\n      api_resource.save!\n      get :authenticate, params: { access_key: valid_attributes[:access_key], access_token: access_token.token }, session: valid_session\n      expect(response).not_to be_success\n      body = JSON.parse(response.body)\n      expect(body[\"result\"]).to eq 1\n    end\n\n    it \"should authenticate if a user is member of API group\" do\n      user = create :user\n      access_token = create :access_token\n      user.access_token = access_token\n      user.save!\n      user.reload\n      group = create :group\n      api_resource = ApiResource.create! valid_attributes\n      api_resource.group = group\n      group.users << user\n      api_resource.save!\n      get :authenticate, params: { access_key: valid_attributes[:access_key], access_token: access_token.token }, session: valid_session\n      expect(response).to be_success\n      body = JSON.parse(response.body)\n      expect(body[\"result\"]).to eq 0\n\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/groups_controller_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe GroupsController, type: :controller do\n  let(:product_name) { 'product-name'  }\n  let!(:admin) { create(:admin_user) }\n  let(:user) do\n    create(:user, name: 'foobar', user_login_id: 'foobar', email: 'foobar@foobar.com', admin: false)\n  end\n\n  describe 'GET #index' do\n    context 'unauthenticated' do\n      it 'should return 302' do\n        get :index\n\n        expect(response).to have_http_status(302)\n      end\n    end\n\n    context 'authenticated as admin' do\n      context 'without search param' do\n        it 'should not return any group' do\n          sign_in admin\n          create_list(:group, 3)\n\n          get :index\n\n          expect(assigns(:groups).size).to eq(0)\n        end\n      end\n\n      context 'with search param' do\n        it 'should return correct groups' do\n          sign_in admin\n          group_foo = create(:group, name: 'GroupFoo')\n          group_foobar = create(:group, name: 'GroupFoobar')\n          create(:group, name: 'GroupBar')\n\n          get :index, params: { group_search: 'Foo' }\n\n          expect(assigns(:groups)).to contain_exactly(group_foo, group_foobar)\n        end\n      end\n    end\n\n    context 'authenticated as group admin' do\n      it 'should return its groups only' do\n        sign_in user\n        group_foo = create(:group, name: 'GroupFoo')\n        group_foobar = create(:group, name: 'GroupFoobar')\n        group_foo.add_admin user\n        group_foobar.add_admin user\n        create(:group, name: 'GroupBar')\n\n        get :index\n\n        expect(assigns(:groups)).to contain_exactly(group_foo, group_foobar)\n      end\n    end\n  end\n\n  describe 'GET #show' do\n    context 'unauthenticated' do\n      it 'should return 302' do\n        get :index\n\n        expect(response).to have_http_status(302)\n      end\n    end\n\n    context 'authenticated as admin' do\n      context '' do\n        it 'should return specified group' do\n          sign_in admin\n          group = create(:group)\n          get :show, params: { id: group.id }\n          expect(response).to have_http_status(200)\n        end\n\n        it 'should populate group_users instance variable' do\n          sign_in admin\n          user = create(:user)\n          group = create(:group)\n          group_association = create(:group_association, group_id: group.id, user_id: user.id, expiration_date: '2020-01-01')\n          get :show, params: { id: group.id }\n          expect(assigns(:group_users).first.to_json).to eq(\n            {\n              id: user.id,\n              email: user.email,\n              name: user.name,\n              active: user.active,\n              join_date: group_association.created_at,\n              group_expiration_date: group_association.expiration_date,\n            }.to_json\n          )\n        end\n      end\n    end\n  end\n\n  describe 'POST #add_user' do\n    context 'unauthenticated' do\n      it 'should return 302' do\n        group = create(:group)\n\n        post :add_user, params: { id: group.id }\n\n        expect(response).to have_http_status(302)\n      end\n    end\n\n    context 'authenticated as admin' do\n      it 'should add user to group' do\n        sign_in admin\n        group = create(:group)\n\n        post :add_user, params: { id: group.id, user_id: user.id }\n\n        expect(group.users).to include(user)\n      end\n\n      it 'should add user to group with expiration date' do\n        sign_in admin\n        group = create(:group)\n        date = '2019-07-10'\n\n        post :add_user, params: { id: group.id, user_id: user.id, expiration_date: date }\n\n        group_association = group.group_associations.where(user_id: user.id).take\n        expect(group_association.expiration_date).to eq(Date.parse(date))\n      end\n\n      context 'wrong expiration date param' do\n        it 'should flash error message' do\n          sign_in admin\n          group = create(:group)\n          date = 'this is not a date'\n\n          post :add_user, params: { id: group.id, user_id: user.id, expiration_date: date }\n\n          expect(flash[:notice]).to eq('Expiration date is wrong')\n        end\n      end\n\n      context 'empty expiration date' do\n        it 'should add user to group without expiration' do\n          sign_in admin\n          group = create(:group)\n          date = ''\n\n          post :add_user, params: { id: group.id, user_id: user.id, expiration_date: date }\n\n          group_association = group.group_associations.where(user_id: user.id).take\n          expect(group_association.expiration_date).to eq(nil)\n        end\n      end\n    end\n\n    context 'authenticated as group admin' do\n      it 'should add new user to group' do\n        sign_in user\n        group = create(:group)\n        group.add_admin(user)\n        new_user = create(:user)\n\n        post :add_user, params: { id: group.id, user_id: new_user.id }\n\n        expect(group.users).to include(new_user)\n      end\n    end\n  end\n\n  describe 'POST #add_group' do\n    context 'unauthenticated' do\n      it 'should return 302' do\n        group = create(:group)\n\n        post :add_group, params: { id: user.id, group_id: group.id }\n\n        expect(response).to have_http_status(302)\n      end\n    end\n\n    context 'authenticated as admin' do\n      it 'should add user to group' do\n        sign_in admin\n        group = create(:group)\n\n        post :add_group, params: { id: user.id, group_id: group.id }\n\n        expect(group.users).to include(user)\n      end\n\n      it 'should redirect to user path once user added to group' do\n        sign_in admin\n        group = create(:group)\n\n        post :add_group, params: { id: user.id, group_id: group.id }\n\n        expect(response).to redirect_to(user_path)\n      end\n\n      it 'should add user with expiration date' do\n        sign_in admin\n        group = create(:group)\n        date = '2019-06-24'\n\n        post :add_group, params: { id: user.id, group_id: group.id, expiration_date: date }\n\n        group_association = group.group_associations.where(user_id: user.id).take\n        expect(group_association.expiration_date).to eq(Date.parse(date))\n      end\n\n      context 'wrong expiration date param' do\n        it 'should flash error message' do\n          sign_in admin\n          group = create(:group)\n          date = 'this is not a date'\n\n          post :add_group, params: { id: user.id, group_id: group.id, expiration_date: date }\n\n          expect(flash[:notice]).to eq('Expiration date is wrong')\n        end\n\n        it 'should not add user to group' do\n          sign_in admin\n          group = create(:group)\n          date = 'this is not a date'\n\n          post :add_group, params: { id: user.id, group_id: group.id, expiration_date: date }\n\n          expect(group.users).not_to include(user)\n        end\n      end\n    end\n  end\n\n  describe 'Search for Groups' do\n    it 'should return groups according to supplied search string' do\n      sign_in user\n      groups = create_list(:group, 3)\n      get :search, params: { q: 'People' }\n      expect(JSON.parse(response.body)).to eq(groups.map { |m| { 'id' => m.id, 'name' => m.name } })\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/home_controller_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe HomeController, type: :controller do\n\nend\n"
  },
  {
    "path": "spec/controllers/host_machine_groups_controller_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe HostMachineGroupsController, type: :controller do\n\nend\n"
  },
  {
    "path": "spec/controllers/host_machines_controller_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe HostMachinesController, type: :controller do\n  let(:user) do\n    create(:user, name: 'foobar', user_login_id: 'foobar', email: 'foobar@foobar.com')\n  end\n  describe 'Search for Hosts' do\n    it 'should return hosts according to supplied search string' do\n      sign_in user\n      host_machines = create_list(:host_machine, 3)\n      get :search, params: { q: 'host' }\n      expect(response.body).to eq(host_machines.map { |m| { id: m.id, name: m.name } }.to_json)\n    end\n  end\n\n  describe 'PATCH #update' do\n    context 'authenticated as admin' do\n      it 'should update requested host machine' do\n        host_machine = create(:host_machine, default_admins: true)\n        sign_in user\n        patch :update, params: { id: host_machine.id, host_machine: { default_admins: false } }\n        host_machine.reload\n        expect(host_machine.default_admins). to be false\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should not update requested host machine' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        host_machine = create(:host_machine, default_admins: true)\n        sign_in non_admin\n        patch :update, params: { id: host_machine.id, host_machine: { default_admins: false } }\n        host_machine.reload\n        expect(host_machine.default_admins). to be true\n      end\n\n      it 'should redirect to host machines path' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        host_machine = create(:host_machine, default_admins: true)\n        sign_in non_admin\n        patch :update, params: { id: host_machine.id, host_machine: { default_admins: false } }\n        expect(response).to redirect_to(host_machines_path)\n      end\n\n      it 'should flash message unathorized access' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        host_machine = create(:host_machine, default_admins: true)\n        sign_in non_admin\n        patch :update, params: { id: host_machine.id, host_machine: { default_admins: false } }\n        expect(flash[:notice]).to eq('Unauthorized access')\n      end\n    end\n  end\n\n  describe 'DELETE #delete_group' do\n    context 'authenticated as admin' do\n      it 'should delete group from host machine' do\n        admin = create(:user)\n        host_machine = create(:host_machine)\n        group = create(:group)\n        host_machine.groups << group\n        sign_in admin\n        delete :delete_group, params: { id: host_machine.id, group_id: group.id }\n        host_machine.reload\n        expect(host_machine.groups.count).to eq(0)\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should not delete group from host machine' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        host_machine = create(:host_machine)\n        group = create(:group)\n        host_machine.groups << group\n        sign_in non_admin\n        delete :delete_group, params: { id: host_machine.id, group_id: group.id }\n        host_machine.reload\n        expect(host_machine.groups.count).to eq(1)\n      end\n\n      it 'should redirect to host machines path' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        host_machine = create(:host_machine)\n        group = create(:group)\n        host_machine.groups << group\n        sign_in non_admin\n        delete :delete_group, params: { id: host_machine.id, group_id: group.id }\n        expect(response).to redirect_to(host_machines_path)\n      end\n\n      it 'should flash notice message unauthorized access' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        host_machine = create(:host_machine)\n        group = create(:group)\n        host_machine.groups << group\n        sign_in non_admin\n        delete :delete_group, params: { id: host_machine.id, group_id: group.id }\n        expect(flash[:notice]).to eq('Unauthorized access')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/nss_controller_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe NssController, type: :controller do\n  let(:access_token) { SecureRandom.uuid }\n  let(:email) { Faker::Internet.email }\n  let(:user) { FactoryBot.create(:user, name: \"foobar\", user_login_id: \"foobar\", email: \"foobar@foobar.com\")  }\n\n  before(:each) do\n    group = create(:group)\n  end\n\n  it \"should return false for invalid token\" do\n    get :groups_list, params: { token: access_token, email: email }\n    data = JSON.parse(response.body)\n    expect(data[\"success\"]).to eq(false)\n  end\n\n  it \"should return false for not registered email\" do\n    create(:access_token, token: access_token)\n    get :groups_list, params: { token: access_token, email: email }\n    data = JSON.parse(response.body)\n    expect(data[\"success\"]).to eq(false)\n  end\n\n  it \"should return group list for registered email\" do\n    create(:access_token, token: access_token)\n    user_test = create(:user, email: email)\n    get :groups_list, params: { token: access_token, email: email }\n    data = JSON.parse(response.body)\n    expect(data[\"success\"]).to eq(true)\n  end\n\n  it 'should not return sysadmins for invalid token' do\n    json = { token: '', name: 'random_host', group_name: '', format: :json }\n    post 'add_host', params: json\n    body = response.body\n    expect(JSON.parse(body)['success']).to eq(false)\n  end\n\n  it \"should not create groups and admins again for the same host name\" do\n    create(:access_token, token: access_token)\n    json = { token: access_token, name: 'random_host', group_name: 'duplicate_group', format: :json }\n    post 'add_host', params: json\n    body = response.body\n    expect(JSON.parse(body)['success']).to eq(true)\n    expect(JSON.parse(body)['groups'].count).to eq 2\n\n    json = { token: access_token, name: 'random_host', group_name: 'duplicate_group', format: :json }\n    post 'add_host', params: json\n    body = response.body\n    expect(JSON.parse(body)['success']).to eq(true)\n    expect(JSON.parse(body)['groups'].count).to eq 2\n  end\n\n  it \"should return sysadmins for that host\" do\n    sign_in user\n    access_token = create(:access_token)\n    json =  { token: access_token.token, name: \"random_host_01\" }\n    post \"add_host\", params: { token: access_token.token, name: \"random_host_01\", group_name: \"random_group_01\", format: :json}\n    body = response.body\n    access_key = JSON.parse(body)[\"access_key\"]\n\n    host = HostMachine.find_by(name: \"random_host_01\")\n    expect(access_key).to eq host.access_key\n\n    group = create(:group)\n    user = create(:user)\n    group.host_machines << host\n    if !group.member? user\n      group.users << user\n    end\n    user = create(:user)\n    if !group.member? user\n      group.users << user\n    end\n    group.save!\n    host.reload\n    group.reload\n\n    expect(host.sysadmins.count).to eq 2\n    get \"host\", params: { token: access_key, format: json }\n    body = JSON.parse(response.body)\n\n    expect(body.count).to eq 3\n    expect(body[2][\"gr_mem\"].count).to eq 2\n\n    get \"group\", params: { token: access_key, format: json }\n    body = JSON.parse(response.body)\n\n    expect(body.count).to eq 3\n    expect(body[0][\"gr_mem\"].count).to eq 1\n    expect(body[1][\"gr_mem\"].count).to eq 1\n    expect(body[2][\"gr_mem\"].count).to eq 2\n\n    expect(Group.find_by(name: \"random_group_01\")).not_to eq nil\n    expect(Group.find_by(name: \"random_host_01_host_group\")).not_to eq nil\n  end\n\n  it \"should return members of sysadmins if no other group exists\" do\n    access_token = create(:access_token)\n    group = create(:group, name: \"sysadmins\")\n    user = create(:user)\n    user.groups << group\n\n    post \"add_host\", params: { token: access_token.token, name: \"random_host_01\", group_name: \"random_group_01\", format: :json}\n    host = HostMachine.first\n    expect(host.name).to eq \"random_host_01\"\n    host.groups << group\n\n    get \"group\", params: { token: host.access_key, format: :json }\n    body = JSON.parse(response.body)\n\n    expect(body.count).to eq 2\n    expect(body[0][\"gr_mem\"].count).to eq 1\n    expect(body[1][\"gr_mem\"].count).to eq 1\n\n    expect(body[0][\"gr_mem\"][0]).to eq user.user_login_id\n    expect(body[1][\"gr_mem\"][0]).to eq user.user_login_id\n  end\n\n  it \"should not return sysadmins if inheritance is off\" do\n    access_token = create(:access_token)\n    group = create(:group, name: \"sysadmins\")\n    group_2 = create(:group, name: \"random\")\n    user = create(:user)\n    user.groups << group_2\n    user_2 = create(:user)\n    user_2.groups << group\n\n    post \"add_host\", params: { token: access_token.token, name: \"random_host_01\", group_name: \"random_group_01\", default_admins: false, format: :json}\n    host = HostMachine.first\n    expect(host.name).to eq \"random_host_01\"\n    host.groups << group_2\n\n    get \"group\", params: { token: host.access_key, format: :json }\n    body = JSON.parse(response.body)\n\n    expect(body.count).to eq 2\n    expect(body[0][\"gr_mem\"].count).to eq 1\n    expect(body[1][\"gr_mem\"].count).to eq 1\n\n    expect(body[0][\"gr_mem\"][0]).to eq user.user_login_id\n    expect(body[1][\"gr_mem\"][0]).to eq user.user_login_id\n  end\n\n  it \"should return all the users for the host\" do\n    access_token = create(:access_token)\n    group = create(:group, name: \"sysadmins\")\n    user = create(:user)\n    user.groups << group\n\n    post \"add_host\", params: { token: access_token.token, name: \"random_host_01\", group_name: \"random_group_01\", format: :json}\n    host = HostMachine.first\n    expect(host.name).to eq \"random_host_01\"\n    host.groups << group\n\n    host.reload\n    group.reload\n    user.reload\n\n    get \"passwd\", params: { token: host.access_key, format: :json }\n    body = JSON.parse(response.body)\n    expect(body.count).to eq 1\n    get \"passwd\", params: { token: host.access_key, format: :json }\n    body = JSON.parse(response.body)\n    expect(body.count).to eq 1\n  end\n\n  it \"should burst host machine cache if member is added or removed\" do\n    group = create(:group)\n    user = create(:user)\n    host_machine = create(:host_machine)\n    host_machine.groups << group\n    user.groups << group\n    host_machine.save!\n\n    Group.all.each do |group|\n      group.burst_host_cache\n    end\n\n    cache_count_bfr = REDIS_CACHE.keys(\"#{GROUP_RESPONSE}*\").count\n    get \"group\", params: { token: host_machine.access_key, format: :json }\n    expect(REDIS_CACHE.keys(\"#{GROUP_RESPONSE}*\").count).to eq cache_count_bfr + 1\n\n    group.burst_host_cache\n    expect(REDIS_CACHE.keys(\"#{GROUP_RESPONSE}*\").count).to eq cache_count_bfr\n  end\nend\n"
  },
  {
    "path": "spec/controllers/omniauth_callbacks_controller_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe Users::OmniauthCallbacksController, type: :controller do\n\nend\n"
  },
  {
    "path": "spec/controllers/organisations_controller_spec.rb",
    "content": "require 'rails_helper'\n\ndescribe OrganisationsController, type: :controller do\n  let(:valid_attributes) do\n    {\n      name: 'name',\n      website: 'sample.com',\n      domain: 'sample.com',\n      country: 'ID',\n      state: 'DKI Jakarta',\n      address: 'Jl Iskandarsyah II',\n      admin_email_address: 'admin@gate.com',\n      unit_name: 'system'\n    }\n  end\n\n  let(:new_attributes) do\n    {\n      name: 'new_name',\n      website: 'new_sample.com',\n      domain: 'new_sample.com',\n      country: 'new_ID',\n      state: 'new_DKI Jakarta',\n      address: 'new Jl Iskandarsyah II',\n      admin_email_address: 'new_admin@gate.com',\n      unit_name: 'new_system'\n    }\n  end\n\n  describe 'GET #index' do\n    context 'authenticated as non admin' do\n      it 'should respond with 200' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        organisation = create(:organisation, valid_attributes)\n        get :index\n        expect(response.status).to eq(200)\n      end\n    end\n  end\n\n  describe 'PATCH #update' do\n    context 'authenticated as admin' do\n      it 'should update requested organisations' do\n        organisation = create(:organisation, valid_attributes)\n        admin = create(:user)\n        sign_in admin\n        patch :update, params: { id: organisation.id, organisation: new_attributes }\n        organisation.reload\n        updated_organisation_data = {\n          name: organisation.name,\n          website: organisation.website,\n          domain: organisation.domain,\n          country: organisation.country,\n          state: organisation.state,\n          address: organisation.address,\n          admin_email_address: organisation.admin_email_address,\n          unit_name: organisation.unit_name\n        }\n        expect(updated_organisation_data.to_json). to eq(new_attributes.to_json)\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should not update requested organisation' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        organisation = create(:organisation, valid_attributes)\n        patch :update, params: { id: organisation.id, organisation: new_attributes }\n        organisation.reload\n        updated_organisation_data = {\n          name: organisation.name,\n          website: organisation.website,\n          domain: organisation.domain,\n          country: organisation.country,\n          state: organisation.state,\n          address: organisation.address,\n          admin_email_address: organisation.admin_email_address,\n          unit_name: organisation.unit_name\n        }\n        expect(updated_organisation_data.to_json).to eq(valid_attributes.to_json)\n      end\n\n      it 'should redirect to organisations path' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        organisation = create(:organisation, valid_attributes)\n        patch :update, params: { id: organisation.id, organisation: new_attributes }\n        expect(response).to redirect_to(organisations_path)\n      end\n\n      it 'should flash unauthorized access' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        organisation = create(:organisation, valid_attributes)\n        patch :update, params: { id: organisation.id, organisation: new_attributes }\n        expect(flash[:notice]).to eq('Unauthorized access')\n      end\n    end\n  end\n\n  describe 'GET #config_saml_app' do\n    context 'authenticated as non admin' do\n      it 'should redirect to organisations path' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        organisation = create(:organisation, valid_attributes)\n        get :config_saml_app, params: { organisation_id: organisation.id, app_name: 'datadog' }\n        expect(response).to redirect_to(organisations_path)\n      end\n\n      it 'should flash unauthorized access' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        organisation = create(:organisation, valid_attributes)\n        get :config_saml_app, params: { organisation_id: organisation.id, app_name: 'datadog' }\n        expect(flash[:notice]).to eq('Unauthorized access')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/profile_controller_spec.rb",
    "content": "describe ProfileController, type: :controller do\n  let!(:admin) { create(:admin_user) }\n  let(:user) { create(:user, admin: false) }\n\n  describe '#update' do\n    context 'unauthenticated' do\n      it 'should return status 302' do\n        post :update, params: { id: user.id }\n\n        expect(response).to have_http_status(302)\n      end\n    end\n\n    context 'authenticated as admin' do\n      it 'should update user' do\n        new_user = create(:user, active: true, admin: true)\n        sign_in admin\n\n        post :update, params: { id: new_user.id, user: { admin: false, active: false } }\n        new_user.reload\n\n        expect(new_user).to have_attributes(active: false, admin: false)\n      end\n\n      it 'should revoke admin when deactivate user' do\n        new_user = create(:user, active: true, admin: true)\n        sign_in admin\n\n        post :update, params: { id: new_user.id, user: { active: false } }\n        new_user.reload\n\n        expect(new_user).to have_attributes(active: false, admin: false)\n      end\n\n      it 'should redirect to user_path after update' do\n        sign_in admin\n\n        post :update, params: { id: user.id, user: { admin: false, active: false } }\n\n        expect(response).to redirect_to(user_path)\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should not update user' do\n        new_user = create(:user, active: true, admin: true)\n        sign_in user\n\n        post :update, params: { id: new_user.id, user: { admin: false, active: false } }\n        new_user.reload\n\n        expect(new_user).to have_attributes(active: true, admin: true)\n      end\n    end\n  end\n\n  describe '#download_vpn_for_ios_and_mac' do\n    context \"user doesn't have vpn\" do\n      it 'should return status 200' do\n        sign_in user\n\n        get :download_vpn_for_ios_and_mac\n\n        expect(response).to have_http_status(200)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/users/auth_controller_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe Users::AuthController, type: :controller do\n  let(:user) { FactoryBot.create(:user, name: 'foobar', email: 'foobar@foobar.com') }\n\n  context 'sign in' do\n    before(:each) do\n      @cached_domain_env = ENV['GATE_HOSTED_DOMAINS']\n      @cached_sign_in_type = ENV['SIGN_IN_TYPE']\n      ENV['GATE_HOSTED_DOMAINS'] = 'foobar.com'\n      ENV['SIGN_IN_TYPE'] = 'form'\n    end\n\n    after(:each) do\n      ENV['GATE_HOSTED_DOMAINS'] = @cached_domain_env\n      ENV['SIGN_IN_TYPE'] = @cached_sign_in_type\n    end\n\n    it 'should redirect to home when success' do\n      post :log_in, params: { name: user.name, email: user.email }\n\n      expect(response).to redirect_to(root_path)\n    end\n\n    it 'should produce error and unauthorized status when email domain is unsupported' do\n      post :log_in, params: { name: user.name, email: 'foobar@notfoobar.com' }\n\n      expect(response).to have_http_status(401)\n      expect(response.body).to eq('Your domain is unauthorized')\n    end\n\n    it 'should generate two factor auth when success' do\n      post :log_in, params: { name: user.name, email: user.email }\n\n      user.reload\n      expect(user.auth_key).not_to be_nil\n    end\n\n    it 'should set user session when success' do\n      post :log_in, params: { name: user.name, email: user.email }\n\n      expect(subject.current_user).to eq(user)\n    end\n\n    it 'should redirect to home when sign in type is not form' do\n      ENV['SIGN_IN_TYPE'] = 'not_form'\n\n      post :log_in, params: { name: user.name, email: user.email }\n\n      expect(response).to redirect_to(root_path)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/users_controller_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe UsersController, type: :controller do\n  let(:product_name) { \"product-name\"  }\n  let!(:group) { FactoryBot.create(:group)  }\n  let(:user) { FactoryBot.create(:user, name: \"foobar\", user_login_id: \"foobar\", email: \"foobar@foobar.com\")  }\n\n  describe 'GET #show' do\n    context 'unauthenticated' do\n      it 'should return 302' do\n        get :index\n\n        expect(response).to have_http_status(302)\n      end\n    end\n\n    context 'authenticated' do\n      it 'should return specified user' do\n        sign_in user\n        get :show, params: { id: user.id }\n        expect(response).to have_http_status(200)\n      end\n\n      it 'should populate user_groups instance variable' do\n        sign_in user\n        create(:group_association, group_id: group.id, user_id: user.id, expiration_date: '2020-01-01')\n        get :show, params: { id: user.id }\n        expected_group = assigns(:user_groups).select { |user_group| user_group.id == group.id }\n        expect(expected_group.first.to_json).to eq(\n          {\n            id: group.id,\n            name: group.name,\n            gid: group.gid,\n            deleted_at: group.deleted_at,\n            group_expiration_date: '2020-01-01',\n          }.to_json\n        )\n      end\n\n      context 'user not found' do\n        it 'should return 404 page' do\n          sign_in user\n          get :show, params: { id: 999 }\n          expect(response).to have_http_status(404)\n        end\n      end\n    end\n  end\n\n  describe 'POST #create' do\n    context 'unauthenticated' do\n      it 'should return 302 http status' do\n        post :create\n\n        expect(response).to have_http_status(302)\n      end\n    end\n\n    context 'authenticated as admin' do\n      it 'should successfully create a new user' do\n        admin = create(:user)\n        sign_in admin\n        post :create, params: {\n          user: {\n            first_name: 'firstname',\n            last_name: 'lastname',\n            user_role: 'employee',\n          },\n          user_domain: 'test.com',\n        }\n        created_user = User.find_by_first_name('firstname')\n        expect(created_user.email).to eq 'firstname.lastname@test.com'\n      end\n\n      it 'should redirect to user path' do\n        admin = create(:user)\n        sign_in admin\n        post :create, params: {\n          user: {\n            first_name: 'firstname',\n            last_name: 'lastname',\n            user_role: 'employee',\n          },\n          user_domain: 'test.com',\n        }\n        created_user = User.find_by_first_name('firstname')\n        expect(response).to redirect_to(user_path(created_user.id))\n      end\n\n      context 'fail to create new user' do\n        it 'should redirect to new user path' do\n          admin = create(:user)\n          allow_any_instance_of(User).to receive(:save).and_return(false)\n          allow_any_instance_of(User).\n            to receive_message_chain(:errors, :full_messages).\n            and_return(['error'])\n          sign_in admin\n          post :create, params: {\n            user: {\n              first_name: 'firstname',\n              last_name: 'lastname',\n              user_role: 'employee',\n            },\n            user_domain: 'test.com',\n          }\n          expect(response).to redirect_to(new_user_path)\n        end\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should redirect to profile path' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        post :create, params: {\n          user: {\n            first_name: 'firstname',\n            last_name: 'lastname',\n            user_role: 'employee',\n          },\n          user_domain: 'test.com',\n        }\n        expect(response).to redirect_to(profile_path)\n      end\n\n      it 'should not create new user' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        post :create, params: {\n          user: {\n            first_name: 'firstname',\n            last_name: 'lastname',\n            user_role: 'employee',\n          },\n          user_domain: 'test.com',\n        }\n        created_user = User.find_by_first_name('firstname')\n        expect(created_user).to be nil\n      end\n\n      it 'should give flash message' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        post :create, params: {\n          user: {\n            first_name: 'firstname',\n            last_name: 'lastname',\n            user_role: 'employee',\n          },\n          user_domain: 'test.com',\n        }\n        expect(flash[:errors]).to eq('unauthorized access')\n      end\n    end\n  end\n\n  context 'update user profile' do\n    context 'unauthenticated' do\n      it 'should return 302 http status' do\n        patch :update, params: { id: user.id, product_name: product_name }\n\n        expect(response).to have_http_status(302)\n      end\n    end\n\n    context 'authenticated as non admin' do\n      it 'should redirect to profile path' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        patch :update, params: { id: user.id, product_name: product_name }\n        expect(response).to redirect_to(profile_path)\n      end\n\n      it 'should give flash message' do\n        create(:user)\n        non_admin = create(:user, admin: false)\n        sign_in non_admin\n        patch :update, params: { id: user.id, product_name: product_name }\n        expect(flash[:errors]).to eq('unauthorized access')\n      end\n    end\n\n    it \"should update profile with product name\" do\n      sign_in user\n\n      patch :update, params: { id: user.id, product_name: product_name }\n\n      user.reload\n      expect(user.product_name).to eq(product_name)\n    end\n\n    it \"should return 302\" do\n      sign_in user\n\n      patch :update, params: { id: user.id, product_name: product_name }\n\n      expect(response).to have_http_status(302)\n    end\n\n    it \"should redirect to same page once the profile is updated\" do\n      sign_in user\n\n      patch :update, params: { id: user.id, product_name: product_name }\n\n      expect(response).to redirect_to(user_path)\n    end\n\n    context \"for invalid request\" do\n      it \"should return params missing message on flash\" do\n        sign_in user\n\n        patch :update, params: { id: user.id }\n\n        expect(flash[:notice]).to eq(\"Params are missing\")\n      end\n    end\n  end\n\n  describe 'Search for Users' do\n    it \"should return only active users by default according to query\" do\n      sign_in user\n      users = create_list(:user, 3)\n      users.last.update(active: false)\n      get :search, params: { q: users.first.name }\n      returned_ids = JSON.parse(response.body).collect{|c| c['id']}\n      expect(returned_ids).to eq([users.first.id])\n    end\n\n    it \"should return users according to query, if we supplied include_inactive params\" do\n      sign_in user\n      users = create_list(:user, 3)\n      users.last.update(active: false)\n      get :search, params: { q: users.last.name, include_inactive: 'true' }\n      returned_ids = JSON.parse(response.body).collect{|c| c['id']}\n      expect(returned_ids).to eq([users.last.id])\n    end\n  end\n\n  describe 'GET #regenerate_token' do\n    context 'authenticated as owner' do\n      let(:owner) { create(:user, admin: false) }\n      before(:each) do\n        create(:user)\n        access_token = AccessToken.new\n        access_token.token = ROTP::Base32.random_base32\n        access_token.user = owner\n        access_token.save!\n\n        sign_in owner\n      end\n\n      it 'regenerates access token of the requested user' do\n        old_hashed_token = owner.access_token.hashed_token\n        get :regenerate_token, params: { id: owner.to_param }\n        owner.reload\n        expect(owner.access_token.hashed_token).to_not eq old_hashed_token\n      end\n\n      it 'redirects to the user' do\n        get :regenerate_token, params: { id: owner.to_param }\n        expect(response).to redirect_to(user_path(owner.id))\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/vpns_controller_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe VpnsController, type: :controller do\n\n  let(:user) { FactoryBot.create(:user, name: \"foobar\", admin: true, user_login_id: \"foobar\", email: \"foobar@foobar.com\")  }\n  context \"vpn operations\" do\n    it \"should return sorted vpns\" do\n\n      #we should choose to stub the authentication with the method given here\n      #https://github.com/plataformatec/devise/wiki/How-To:-Stub-authentication-in-controller-specs\n      #but this requires to hand post create call in users and breaks some old tests.\n      #we need to fix those.\n      sign_in user\n\n      @vpn01 = Vpn.create(name: \"z\")\n      @vpn02 = Vpn.create(name: \"a\")\n\n\n      vpns = [ @vpn02, @vpn01]\n\n      get :index\n\n      expect(response).to render_template(\"index\")\n      expect(response.status).to eq(200)\n      expect(assigns(:vpns)).to eq(vpns)\n\n\n      @vpn03 = Vpn.create(name: \"c\")\n      vpns = [ @vpn02, @vpn03, @vpn01]\n      get :index\n      expect(assigns(:vpns)).to eq(vpns)\n\n      @vpn04 = Vpn.create(name: \"b\")\n\n      vpns = [ @vpn02, @vpn04, @vpn03, @vpn01]\n      get :index\n      expect(assigns(:vpns)).to eq(vpns)\n\n    end\n\n    it \"should add or remove properties\" do\n      sign_in user\n\n      @vpn01 = Vpn.create(name: \"z\")\n\n      post :add_dns_server, params: { id: @vpn01.id, server_address: '1.1.1.1'}\n      expect(@vpn01.vpn_domain_name_servers.count).to eq(1)\n\n      post :remove_dns_server, params: { id: @vpn01.id, vpn_domain_name_server_id: @vpn01.vpn_domain_name_servers.first.id}\n      expect(@vpn01.vpn_domain_name_servers.count).to eq(0)\n\n      post :add_search_domain, params: { id: @vpn01.id, search_domain: 'xyz.com'}\n      expect(@vpn01.vpn_search_domains.count).to eq(1)\n\n      post :remove_search_domain, params: { id: @vpn01.id, vpn_search_domain_id: @vpn01.vpn_search_domains.first.id}\n      expect(@vpn01.vpn_search_domains.count).to eq(0)\n\n      post :add_supplemental_match_domain, params: { id: @vpn01.id, supplemental_match_domain: 'abc.co.id'}\n      expect(@vpn01.vpn_supplemental_match_domains.count).to eq(1)\n\n      post :remove_supplemental_match_domain, params: { id: @vpn01.id, vpn_supplemental_match_domain_id: @vpn01.vpn_supplemental_match_domains.first.id}\n      expect(@vpn01.vpn_supplemental_match_domains.count).to eq(0)\n\n    end\n  end\n\n  describe 'Search for Vpns' do\n    it \"should return vpns according to supplied search string\" do\n      sign_in user\n      vpns = create_list(:vpn, 3)\n      get :search, params: { q: \"VPN\" }\n      expect(JSON.parse(response.body)).to eq(vpns.map{|m| {\"id\" => m.id, \"name\" => m.name}})\n    end\n  end\nend\n\n"
  },
  {
    "path": "spec/factories/access_tokens.rb",
    "content": "FactoryBot.define do\n  factory :access_token do\n    token { SecureRandom.uuid }\n  end\nend\n"
  },
  {
    "path": "spec/factories/api_resources.rb",
    "content": "FactoryBot.define do\n  factory :api_resource do\n    sequence(:name, 1000) { |n| \"API#{n}\" }\n    description { \"MyString\" }\n    access_key { \"MyString\" }\n  end\nend\n"
  },
  {
    "path": "spec/factories/endpoints.rb",
    "content": "FactoryBot.define do\n  factory :endpoint do\n    path { '/' }\n    add_attribute(:method) { ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].sample }\n  end\nend\n"
  },
  {
    "path": "spec/factories/group_associations.rb",
    "content": "FactoryBot.define do\n  factory :group_association do\n    user { \"\" }\n    group { \"\" }\n  end\nend\n"
  },
  {
    "path": "spec/factories/groups.rb",
    "content": "FactoryBot.define do\n  factory :group do\n    sequence(:name, 1000) { |n| \"People#{n}\" }\n  end\nend\n"
  },
  {
    "path": "spec/factories/host_access_groups.rb",
    "content": "FactoryBot.define do\n  factory :host_access_group do\n    references { \"\" }\n    references { \"\" }\n  end\nend\n"
  },
  {
    "path": "spec/factories/host_machine_groups.rb",
    "content": "FactoryBot.define do\n  factory :host_machine_group do\n    user { nil }\n    host_machine { nil }\n  end\nend\n"
  },
  {
    "path": "spec/factories/host_machines.rb",
    "content": "FactoryBot.define do\n  factory :host_machine do\n    sequence(:name) { |n| \"host-#{n}\" }\n  end\nend\n"
  },
  {
    "path": "spec/factories/ip_addresses.rb",
    "content": "FactoryBot.define do\n  factory :ip_address do\n    address { \"MyString\" }\n    mac_address { \"MyString\" }\n  end\nend\n"
  },
  {
    "path": "spec/factories/organisations.rb",
    "content": "FactoryBot.define do\n  #country = Country.find_country_by_name(Country.all.map(&:name).sort.sample)\n  factory :organisation do\n    sequence(:slug) { |n| \"#{Faker::Lorem.word}_#{n}\" }\n    name { Faker::Lorem.word }\n    website { Faker::Internet.url }\n    domain { Faker::Internet.email.split('@').last }\n    country { country.gec }\n    state { Faker::Address.state }\n    address { Faker::Lorem.words(number: 3).join(' ') }\n    unit_name { 'IT' }\n    admin_email_address { Faker::Internet.email }\n  end\nend\n"
  },
  {
    "path": "spec/factories/saml_app_configs.rb",
    "content": "FactoryBot.define do\n  factory :saml_app_config do\n    sso_url { Faker::Internet.url }\n    group { create(:group, name: \"saml_#{Faker::Lorem.word}_datadog_users\") }\n    config { { app_key: Faker::Internet.password(min_length: 8), api_key: Faker::Internet.password(min_length: 8) } }\n    organisation { build(:organisation) }\n    app_name { 'datadog' }\n  end\nend\n"
  },
  {
    "path": "spec/factories/user_host_access_groups.rb",
    "content": "FactoryBot.define do\n  factory :user_host_access_group do\n    user { nil }\n    host_access_group { nil }\n  end\nend\n"
  },
  {
    "path": "spec/factories/users.rb",
    "content": "FactoryBot.define do\n  factory :user do\n    transient do\n      user_roles_list { ENV['USER_ROLES'].split(',') }\n      hosted_domains_list { ENV['GATE_HOSTED_DOMAINS'].split(',') }\n    end\n    first_name { Faker::Name.first_name.gsub(/[^A-Za-z]/, '') }\n    last_name { Faker::Name.last_name.gsub(/[^A-Za-z]/, '') }\n    user_role { user_roles_list.sample }\n    mobile { Faker::PhoneNumber.cell_phone }\n    email { \"#{first_name.downcase}.#{last_name.downcase}@#{hosted_domains_list.sample}\" }\n    alternate_email { Faker::Internet.email }\n    name { \"#{first_name} #{last_name}\" }\n    active { true }\n    admin { true }\n    sequence(:reset_password_token) { |n| \"user_secret#{n}\" }\n    after(:create) do |user, _evaluator|\n      user.assign_attributes(\n        user_login_id: user.generate_login_id, uid: user.generate_uid\n      )\n      user.initialise_host_and_group\n      user.save!\n    end\n  end\n\n  factory :group_admin, class: User do\n    transient do\n      user_roles_list { ENV['USER_ROLES'].split(',') }\n      hosted_domains_list { ENV['GATE_HOSTED_DOMAINS'].split(',') }\n    end\n    first_name { Faker::Name.first_name.gsub(/[^A-Za-z]/, '') }\n    last_name { Faker::Name.last_name.gsub(/[^A-Za-z]/, '') }\n    user_role { user_roles_list.sample }\n    alternate_email { Faker::Internet.email }\n    mobile { Faker::PhoneNumber.cell_phone }\n    active { true }\n    admin { true }\n    email { \"#{first_name.downcase}.#{last_name.downcase}@#{hosted_domains_list.sample}\" }\n    name { \"#{first_name} #{last_name}\" }\n    sequence(:reset_password_token) { |n| \"secret#{n}\" }\n    after(:create) do |user, _evaluator|\n      user.assign_attributes(\n        user_login_id: user.generate_login_id, uid: user.generate_uid\n      )\n      user.initialise_host_and_group\n      user.save!\n    end\n  end\n\n  factory :admin_user, class: User do\n    transient do\n      user_roles_list { ENV['USER_ROLES'].split(',') }\n      hosted_domains_list { ENV['GATE_HOSTED_DOMAINS'].split(',') }\n    end\n    first_name { Faker::Name.first_name.gsub(/[^A-Za-z]/, '') }\n    last_name { Faker::Name.last_name.gsub(/[^A-Za-z]/, '') }\n    user_role { user_roles_list.sample }\n    alternate_email { Faker::Internet.email }\n    mobile { Faker::PhoneNumber.cell_phone }\n    active { true }\n    admin { true }\n    email { \"#{first_name.downcase}.#{last_name.downcase}@#{hosted_domains_list.sample}\" }\n    name { \"#{first_name} #{last_name}\" }\n    sequence(:reset_password_token) { |n| \"secret#{n}\" }\n    after(:create) do |user, _evaluator|\n      user.assign_attributes(\n        user_login_id: user.generate_login_id, uid: user.generate_uid\n      )\n      user.initialise_host_and_group\n      user.save!\n    end\n  end\nend\n"
  },
  {
    "path": "spec/factories/vpns.rb",
    "content": "FactoryBot.define do\n  factory :vpn do\n    sequence(:name) { |n| \"VPN#{n}\" }\n  end\nend\n"
  },
  {
    "path": "spec/features/layout_spec.rb",
    "content": "require 'rails_helper'\nRSpec.feature 'Layout', type: :feature do\n  let(:user) { create(:user) }\n  let(:group_admin) { create(:group_admin) }\n  let(:admin) { create(:admin_user) }\n  scenario 'Access to user links' do\n    sign_in(user)\n    visit profile_path\n    links = {\n      'APIs' => api_resources_path,\n      'VPNs' => vpns_path,\n      'API' => new_api_resource_path,\n    }\n    links.each do |link_text, link_href|\n      expect(page).to have_xpath(\n        \"//div[@id='main-navigation']//a[@href='#{link_href}' and .='#{link_text}']\"\n      )\n    end\n  end\n  scenario 'Access to group admin links' do\n    sign_in(group_admin)\n    visit profile_path\n    links = {\n      'APIs' => api_resources_path,\n      'Groups' => groups_path,\n      'VPNs' => vpns_path,\n      'API' => new_api_resource_path,\n    }\n    links.each do |link_text, link_href|\n      expect(page).to have_xpath(\n        \"//div[@id='main-navigation']//a[@href='#{link_href}' and .='#{link_text}']\"\n      )\n    end\n  end\n  scenario 'Access to admin links' do\n    sign_in(admin)\n    visit root_path\n    links = {\n      'Users' => users_path,\n      'Hosts' => host_machines_path,\n      'Organisations' => organisations_path,\n      'Groups' => groups_path,\n      'APIs' => api_resources_path,\n      'VPNs' => vpns_path,\n      'API' => new_api_resource_path,\n      'Group' => new_group_path,\n      'VPN' => new_vpn_path,\n      'Organisation' => new_organisation_path,\n    }\n    links.each do |link_text, link_href|\n      expect(page).to have_xpath(\n        \"//div[@id='main-navigation']//a[@href='#{link_href}' and .='#{link_text}']\"\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "spec/features/organisations/add_user_saml_app_spec.rb",
    "content": "require 'rails_helper'\nRSpec.feature 'Config Saml App', type: :feature do\n  let(:user) { create(:user) }\n  let(:users) { create_list(:user, 10) }\n  let(:org) { create(:organisation) }\n\n  before do\n    sign_in(user)\n  end\n\n  scenario 'Add user to group' do\n    create(\n      :saml_app_config,\n      organisation: org,\n      group: Group.new(name: \"#{org.slug}_saml_datadog_users\")\n    )\n    allow_any_instance_of(Group).to receive(:users).and_return(users)\n    home_path = organisation_config_saml_app_path(\n      organisation_id: org.id, app_name: 'datadog'\n    )\n    visit home_path\n    page.find('#manage-users-tab').click\n    page.find_field('email').set(user.email)\n    expect_any_instance_of(Datadog).\n      to receive(:add_user).with(user.email)\n    page.find('#add_user_to_app').click\n    expect(current_path).to eq(home_path)\n  end\nend\n"
  },
  {
    "path": "spec/features/organisations/config_saml_app_spec.rb",
    "content": "require 'rails_helper'\nRSpec.feature 'Config Saml App', type: :feature do\n  let(:org) { create(:organisation) }\n  let(:user) { create(:user) }\n  let(:saml_apps) { ENV['SAML_APPS'].split(',') }\n\n  before do\n    sign_in(user)\n  end\n\n  scenario 'Redirect to organisation detail if app not found' do\n    visit organisation_config_saml_app_path(\n      organisation_id: org.id, app_name: Faker::Lorem.word\n    )\n    expect(current_path).to eq(organisation_path(id: org.id))\n  end\n\n  scenario 'Redirect to list organisations if organisation not found' do\n    visit organisation_config_saml_app_path(\n      organisation_id: Faker::Number.number(digits: 1),\n      app_name: Faker::Lorem.word\n    )\n    expect(current_path).to eq(organisations_path)\n  end\n\n  scenario 'View saml app configuration options' do\n    saml_app = saml_apps.sample\n    config_saml_app_path = organisation_config_saml_app_path(\n      organisation_id: org.id,\n      app_name: saml_app\n    )\n    visit config_saml_app_path\n    expect(current_path).to eq(config_saml_app_path)\n    expect(page).to have_xpath(\n      \"//a[@id='instruction-tab' and @href='#instruction' and .='Instructions']\"\n    )\n    expect(page).to have_xpath(\n      \"//a[@id='settings-tab' and @href='#settings' and .='Settings']\"\n    )\n    expect(page).to have_xpath(\n      \"//a[@id='manage-users-tab' and @href='#manage-users' and .='Manage Users']\"\n    )\n  end\n\n  scenario 'Instrutions for DataDog and Gate Configuration Steps, Manage Users on Gate' do\n    saml_app = saml_apps.sample\n    config_saml_app_path = organisation_config_saml_app_path(\n      organisation_id: org.id,\n      app_name: saml_app\n    )\n    visit config_saml_app_path\n    expect(current_path).to eq(config_saml_app_path)\n    expect(page).to have_xpath(\n      \"//h6[@id='configureDataDog' and .='Configuration Steps on #{saml_app.titleize}']\"\n    )\n    expect(page).to have_xpath(\n      \"//h6[@id='configureGate' and .='Configuration Steps on Gate']\"\n    )\n    expect(page).to have_xpath(\n      \"//h6[@id='manageUsersGate' and .='Manage Users on Gate']\"\n    )\n  end\nend\n"
  },
  {
    "path": "spec/features/organisations/create_spec.rb",
    "content": "require 'rails_helper'\nRSpec.feature 'Create Organisation', type: :feature do\n  let(:org_data) { attributes_for(:organisation) }\n  let(:user) { create(:user) }\n  scenario 'Create an organisation successfully' do\n    sign_in(user)\n    visit new_organisation_path\n    select Country.all.sample.name, from: 'organisation_country'\n    %w(name website domain state address admin_email_address slug unit_name).each do |key|\n      fill_in \"organisation_#{key}\", with: org_data[key.to_sym]\n    end\n    click_button('Create Organisation')\n    expect(current_path).to eq(organisations_path)\n    expect(page).to have_xpath(\n      \"//div[@id='organisation_form_success' and .='Successfully created organisation']\"\n    )\n  end\n  scenario 'Display Errors on Creating Organisation' do\n    sign_in(user)\n    visit new_organisation_path\n    fill_in 'organisation_name', with: org_data[:name]\n    click_button('Create Organisation')\n    expect(current_path).to eq(organisations_path)\n    expect(page).to have_xpath(\n      \"//div[@id='organisation_form_errors']\"\n    )\n  end\nend\n"
  },
  {
    "path": "spec/features/organisations/list_spec.rb",
    "content": "require 'rails_helper'\nRSpec.feature 'List Organisations', type: :feature do\n  let!(:orgs) { create_list(:organisation, 5) }\n  let(:user) { create(:user) }\n  scenario 'Can create an organisation' do\n    sign_in(user)\n    visit organisations_path\n    expect(page).to have_xpath(\"//a[@id='new_organisation_btn']\")\n  end\n  scenario 'View list of organisations' do\n    sign_in(user)\n    visit organisations_path\n    table_xpath = \"//div[@id='organisation_list']/table\"\n    orgs.each do |org|\n      expect(page).to have_xpath(\n        \"#{table_xpath}//td/a[@href='#{organisation_path(org)}' and .='#{org.name}']\"\n      )\n      expect(page).to have_xpath(\n        \"#{table_xpath}//td/a[@href='#{org.website}' and .='#{org.website}']\"\n      )\n      expect(page).to have_xpath(\n        \"#{table_xpath}//td[.='#{org.domain}']\"\n      )\n      expect(page).to have_xpath(\n        \"#{table_xpath}//td/a[@href='#{organisation_setup_saml_path(org)}' and .='Setup SAML']\"\n      )\n    end\n  end\n  scenario 'Ability to see organsiation details' do\n    sign_in(user)\n    visit organisations_path\n    table_xpath = \"//div[@id='organisation_list']/table\"\n    orgs.each do |org|\n      expect(page).to have_xpath(\n        \"#{table_xpath}//td/a[@href='#{organisation_path(org)}' and .='#{org.name}']\"\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "spec/features/organisations/list_user_saml_app_spec.rb",
    "content": "require 'rails_helper'\nRSpec.feature 'Config Saml App', type: :feature do\n  let(:user) { create(:user) }\n  let(:users) { create_list(:user, 10) }\n  let(:org) { create(:organisation) }\n\n  before do\n    sign_in(user)\n  end\n\n  scenario 'Display list of users for the saml app' do\n    create(\n      :saml_app_config,\n      organisation: org,\n      group: Group.new(name: \"#{org.slug}_saml_datadog_users\")\n    )\n    allow_any_instance_of(Group).to receive(:users).and_return(users)\n    visit organisation_config_saml_app_path(\n      organisation_id: org.id, app_name: 'datadog'\n    )\n    page.find('#manage-users-tab').click\n    users.each do |user|\n      expect(page).to have_xpath(\n        \"//td[.='#{user.email}']\"\n      )\n      expect(page).to have_xpath(\n        \"//td/a[.='#{user.name}']\"\n      )\n    end\n  end\n\n  scenario 'Display link for user profile' do\n    create(\n      :saml_app_config,\n      organisation: org,\n      group: Group.new(name: \"#{org.slug}_saml_datadog_users\")\n    )\n    allow_any_instance_of(Group).to receive(:users).and_return(users)\n    visit organisation_config_saml_app_path(\n      organisation_id: org.id, app_name: 'datadog'\n    )\n    page.find('#manage-users-tab').click\n    users.each do |user|\n      expect(page).to have_xpath(\n        \"//td/a[@href='#{user_path(id: user.id)}' and contains(string(), '#{user.name}')]\"\n      )\n    end\n  end\n\n  scenario 'Display remove link for users' do\n    create(\n      :saml_app_config,\n      organisation: org,\n      group: Group.new(name: \"#{org.slug}_saml_datadog_users\")\n    )\n    allow_any_instance_of(Group).to receive(:users).and_return(users)\n    visit organisation_config_saml_app_path(\n      organisation_id: org.id, app_name: 'datadog'\n    )\n    page.find('#manage-users-tab').click\n    users.each do |user|\n      remove_path = organisation_remove_user_saml_app_path(\n        organisation_id: org.id,\n        app_name: 'datadog',\n        email: user.email\n      )\n      expect(page).to have_xpath(\n        \"//td/a[.='Remove' and @href='#{remove_path}']\"\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "spec/features/organisations/remove_user_saml_app_spec.rb",
    "content": "require 'rails_helper'\nRSpec.feature 'Config Saml App', type: :feature do\n  let(:user) { create(:user) }\n  let(:users) { create_list(:user, 10) }\n  let(:org) { create(:organisation) }\n\n  before do\n    sign_in(user)\n  end\n\n  scenario 'Remove user from group' do\n    create(\n      :saml_app_config,\n      organisation: org,\n      group: Group.new(name: \"#{org.slug}_saml_datadog_users\")\n    )\n    allow_any_instance_of(Group).to receive(:users).and_return(users)\n    home_path = organisation_config_saml_app_path(\n      organisation_id: org.id, app_name: 'datadog'\n    )\n    visit home_path\n    page.find('#manage-users-tab').click\n    expect_any_instance_of(Datadog).\n      to receive(:remove_user).with(users.first.email)\n    page.find(\"#saml_user_remove_#{users.first.id}\").click\n    expect(current_path).to eq(home_path)\n  end\nend\n"
  },
  {
    "path": "spec/features/organisations/save_config_saml_app_spec.rb",
    "content": "require 'rails_helper'\nRSpec.feature 'Save Config Saml App', type: :feature do\n  let(:org) { create(:organisation) }\n  let(:user) { create(:user) }\n  let(:app_name) { 'datadog' }\n\n  before do\n    sign_in(user)\n  end\n\n  scenario 'Load Present Configuration' do\n    url = Faker::Internet.url\n    saml_app = Datadog.new(org.id)\n    app_key = Faker::Internet.password(min_length: 8)\n    api_key = Faker::Internet.password(min_length: 8)\n    saml_app.save_config(url, app_key: app_key, api_key: api_key)\n    visit organisation_config_saml_app_path(\n      organisation_id: org.id, app_name: app_name\n    )\n    page.find('#settings-tab').click\n    expect(page.find_field('saml_app_config_sso_url').value).to eq(url)\n    expect(page.find_field('config_app_key').value).to eq(app_key)\n    expect(page.find_field('config_api_key').value).to eq(api_key)\n  end\n\n  scenario 'Load New Configuration If Not Saved' do\n    visit organisation_config_saml_app_path(\n      organisation_id: org.id, app_name: app_name\n    )\n    page.find('#settings-tab').click\n    expect(page.find_field('saml_app_config_sso_url').value.blank?).to eq(true)\n    expect(page.find_field('config_app_key').value.blank?).to eq(true)\n    expect(page.find_field('config_api_key').value.blank?).to eq(true)\n  end\n\n  scenario 'Save Configuration' do\n    url = Faker::Internet.url\n    app_key = Faker::Internet.password(min_length: 8)\n    api_key = Faker::Internet.password(min_length: 8)\n    path = organisation_config_saml_app_path(\n      organisation_id: org.id, app_name: app_name\n    )\n    visit path\n    page.find('#settings-tab').click\n    page.find_field('saml_app_config_sso_url').set(url)\n    page.find_field('config_app_key').set(app_key)\n    page.find_field('config_api_key').set(api_key)\n    page.find('#new_saml_app_config').click\n    expect(current_path).to eq(path)\n  end\nend\n"
  },
  {
    "path": "spec/features/organisations/setup_saml_spec.rb",
    "content": "require 'rails_helper'\nRSpec.feature 'Setup SAML', type: :feature do\n  let!(:org) { create(:organisation) }\n  let(:user) { create(:user) }\n  scenario 'Should show a success message if SAML is setup' do\n    sign_in(user)\n    allow_any_instance_of(Organisation).to receive(:setup_saml_certs).and_return(true)\n    visit organisation_setup_saml_path(org)\n    expect(current_path).to eq(organisations_path)\n    expect(page).to have_xpath(\n      \"//div[@id='organisation_form_success' and .='Successfully setup SAML Certificates']\"\n    )\n  end\nend\n"
  },
  {
    "path": "spec/features/organisations/update_spec.rb",
    "content": "require 'rails_helper'\nRSpec.feature 'Update Organisation', type: :feature do\n  let!(:org) { create(:organisation) }\n  let(:org_data) { attributes_for(:organisation) }\n  let(:user) { create(:user) }\n  scenario 'Create an organisation successfully' do\n    sign_in(user)\n    visit organisation_path(org)\n    select Country.all.sample.name, from: 'organisation_country'\n    %w(name website domain state address admin_email_address slug unit_name).each do |key|\n      fill_in \"organisation_#{key}\", with: org_data[key.to_sym]\n    end\n    click_button('Update Organisation')\n    expect(current_path).to eq(organisations_path)\n    expect(page).to have_xpath(\n      \"//div[@id='organisation_form_success' and .='Successfully updated organisation']\"\n    )\n  end\n  scenario 'Display Errors on Creating Organisation' do\n    sign_in(user)\n    visit organisation_path(org)\n    fill_in 'organisation_name', with: ''\n    click_button('Update Organisation')\n    expect(current_path).to eq(organisation_path(org))\n    expect(page).to have_xpath(\n      \"//div[@id='organisation_form_errors']\"\n    )\n  end\nend\n"
  },
  {
    "path": "spec/features/saml/show_spec.rb",
    "content": "require 'rails_helper'\nRSpec.feature 'Config Saml App', type: :feature do\n  let(:org) { create(:organisation) }\n  let(:user) { create(:user) }\n  let(:saml_apps) { ENV['SAML_APPS'].split(',') }\n  let(:xml_content) { Nokogiri::XML::Builder.new { |xml| xml.foo_bar 'hello' }.to_xml }\n\n  before do\n    sign_in(user)\n    allow(SamlIdp.metadata).to receive(:signed).and_return(xml_content)\n  end\n\n  scenario 'Show metadata when no download flag' do\n    visit metadata_path(slug: org.slug, app: saml_apps.sample)\n    expect(page.body).to eq(xml_content)\n  end\n\n  scenario 'Download metadata with download flag' do\n    visit metadata_path(slug: org.slug, app: saml_apps.sample, download: true)\n    expect(page.response_headers['Content-Type']).to eq('text/xml')\n    expect(page.response_headers['Content-Disposition']).to include('metadata.xml')\n  end\nend\n"
  },
  {
    "path": "spec/features/users/create_user_spec.rb",
    "content": "require 'rails_helper'\nRSpec.feature 'Create User', type: :feature do\n  let(:user) { create(:user, admin: true) }\n\n  before(:each) do\n    sign_in user\n  end\n\n  scenario 'Main Navigation should have link to create user' do\n    visit new_user_path\n    expect(page).to have_xpath(\"//div[@id='main-navigation']//a[@href='#{new_user_path}']\")\n  end\n\n  scenario 'If admin view user creation form' do\n    roles = ENV['USER_ROLES'].split(',').map(&:titleize).insert(0, 'Select A Role')\n    domains = ENV['GATE_HOSTED_DOMAINS'].split(',').insert(0, 'Select A Domain')\n    visit new_user_path\n    expect(page.find_field('user_first_name').value.blank?).to eq(true)\n    expect(page.find_field('user_last_name').value.blank?).to eq(true)\n    expect(page.find_field('user_mobile').value.blank?).to eq(true)\n    expect(page.find_field('user_alternate_email').value.blank?).to eq(true)\n    expect(page).to have_select('user_user_role', options: roles)\n    expect(page).to have_select('user_domain', options: domains)\n    expect(page).to have_button('Create User')\n  end\n\n  scenario 'if not admin redirect to root path' do\n    user.update_attribute(:admin, false)\n    sign_in user\n    visit new_user_path\n    expect(current_path).to eq(profile_path)\n  end\n\n  scenario 'Show success message if required fields are present' do\n    new_user = create(:user)\n    domain = new_user.email.split('@').last\n    expect(User).to receive(:add_user).with(\n      new_user.first_name, new_user.last_name, new_user.user_role, domain\n    ).and_return(new_user)\n    visit new_user_path\n    page.find_field('user_first_name').set(new_user.first_name)\n    page.find_field('user_last_name').set(new_user.last_name)\n    page.find_field('user_mobile').set(new_user.mobile)\n    page.find_field('user_alternate_email').set(new_user.alternate_email)\n    page.select(domain, from: 'user_domain')\n    page.select(new_user.user_role.titleize, from: 'user_user_role')\n    # page.find_field('user_user_role').set(new_user.user_role)\n    # page.find_field('user_domain').set(domain)\n    page.find_button('Create User').click\n    expect(current_path).to eq(user_path(id: new_user.id))\n    expect(page).to have_xpath(\n      \"//div[@class='alert alert-success'  and contains(string(), 'Successfully Created User')]\"\n    )\n  end\n\n  scenario 'Show error message if required fields are not present' do\n    new_user = build(:user)\n    domain = new_user.email.split('@').last\n    expect(User).to receive(:add_user).with(\n      new_user.first_name, new_user.last_name, new_user.user_role, domain\n    ).and_return(new_user)\n    new_user.errors.add(:first_name, 'Cannot be blank')\n    visit new_user_path\n    page.find_field('user_first_name').set(new_user.first_name)\n    page.find_field('user_last_name').set(new_user.last_name)\n    page.find_field('user_mobile').set(new_user.mobile)\n    page.find_field('user_alternate_email').set(new_user.alternate_email)\n    page.select(domain, from: 'user_domain')\n    page.select(new_user.user_role.titleize, from: 'user_user_role')\n    # page.find_field('user_user_role').set(new_user.user_role)\n    # page.find_field('user_domain').set(domain)\n    page.find_button('Create User').click\n    expect(current_path).to eq(new_user_path)\n    expect(page).to have_xpath(\n      \"//div[@class='alert alert-danger' and contains(string(), 'Issue Creating User')]\"\n    )\n  end\nend\n"
  },
  {
    "path": "spec/features/users/regenerate_auth_spec.rb",
    "content": "require 'rails_helper'\nRSpec.feature 'Rengenerate Auth Token', type: :feature do\n  let(:user) { create(:user) }\n  let(:rotp_key) { ROTP::Base32.random_base32 }\n  before(:each) do\n    allow(ROTP::Base32).to receive(:random_base32).and_return(rotp_key)\n  end\n  scenario 'Create an organisation successfully' do\n    sign_in user\n    expect(user).to receive(:generate_two_factor_auth).with(true)\n    visit regenerate_authentication_path\n    expect(current_path).to eq(profile_path)\n  end\nend\n"
  },
  {
    "path": "spec/helpers/application_helper_spec.rb",
    "content": "require 'rails_helper'\n\ndescribe ApplicationHelper, type: :helper do\n  describe '.add_placeholder_to_list' do\n    let (:placeholder) { 'placeholder' }\n    let(:list) { (1..5).map { |ix| \"row#{ix}\" } }\n\n    it 'returns list with first element of every array titleized and adds placeholder' do\n      titleized_list = list.map { |row| [row.titleize, row] }.insert(0, [placeholder, ''])\n      expect(helper.add_placeholder_to_list(list, placeholder)).to eq(titleized_list)\n    end\n\n    it 'returns list with first element of every array with string function and adds placeholder' do\n      titleized_list = list.map { |row| [row.capitalize, row] }.insert(0, [placeholder, ''])\n      expect(helper.add_placeholder_to_list(list, placeholder, string_convert: 'capitalize')).to eq(titleized_list)\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/lib/datadog_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe Datadog do\n\n  let(:org) { create(:organisation) }\n  let(:app_name) { 'datadog' }\n\n  describe 'initialize' do\n    it 'initialize datadog client if already saved' do\n      config = create(:saml_app_config, organisation: org)\n      expect(DataDogClient).to receive(:new).with(\n        config.config['app_key'], config.config['api_key']\n      )\n      Datadog.new(org.id)\n    end\n  end\n\n  describe 'save_config' do\n    let(:config) { build(:saml_app_config, organisation: org) }\n    let(:saml_app) { Datadog.new(org.id) }\n\n    it 'should update the configuration of the application' do\n      saml_app.save_config(config.sso_url, config.config)\n      expect(saml_app.config.config).to eq(config.config)\n    end\n\n    it 'should call the parent class to update url and initialize group for the app' do\n      expect_any_instance_of(SamlApp).\n        to receive(:save_config).with(\n          config.sso_url,\n          config.config\n        )\n      saml_app.save_config(config.sso_url, config.config)\n    end\n  end\n\n  describe 'add_user' do\n    let(:email) { Faker::Internet.email }\n    let(:saml_app) { Datadog.new(org.id) }\n\n    before do\n      create(:saml_app_config, organisation: org)\n    end\n\n    it 'adds a user when a user doesn\\'t exist' do\n      allow_any_instance_of(DataDogClient).\n        to receive(:get_user).and_return({})\n      expect_any_instance_of(DataDogClient).to receive(:new_user)\n      saml_app.add_user(email)\n    end\n\n    it 'activates a user when a user exists' do\n      allow_any_instance_of(DataDogClient).\n        to receive(:get_user).and_return(handle: email)\n      expect_any_instance_of(DataDogClient).to receive(:activate_user)\n      saml_app.add_user(email)\n    end\n\n    it 'calls the parent class to add user to a group for a valid response' do\n      allow_any_instance_of(DataDogClient).\n        to receive(:get_user).and_return(handle: email)\n      allow_any_instance_of(DataDogClient).to receive(:activate_user).and_return(handle: email)\n      expect_any_instance_of(SamlApp).to receive(:add_user).\n        with(email)\n      saml_app.add_user(email)\n    end\n\n    it 'doesn\\'t call the parent class to add user to a group for a invalid response' do\n      allow_any_instance_of(DataDogClient).\n        to receive(:get_user).and_return(handle: email)\n      allow_any_instance_of(DataDogClient).to receive(:activate_user).and_return({})\n      expect_any_instance_of(SamlApp).not_to receive(:add_user).\n        with(email)\n      saml_app.add_user(email)\n    end\n  end\n\n  describe 'remove_user' do\n    let(:email) { Faker::Internet.email }\n    let(:saml_app) { Datadog.new(org.id) }\n\n    before do\n      create(:saml_app_config, organisation: org)\n    end\n\n    it 'deactivates a user for a valid email address' do\n      expect_any_instance_of(DataDogClient).\n        to receive(:deactivate_user)\n      saml_app.remove_user(email)\n    end\n\n    it 'call the parent class to remove user from group for valid email' do\n      allow_any_instance_of(DataDogClient).\n        to receive(:deactivate_user).and_return(handle: email)\n      expect_any_instance_of(SamlApp).to receive(:remove_user)\n      saml_app.remove_user(email)\n    end\n\n    it 'not call the parent class to remove user from group for valid email' do\n      allow_any_instance_of(DataDogClient).\n        to receive(:deactivate_user).and_return({})\n      expect_any_instance_of(SamlApp).not_to receive(:remove_user)\n      saml_app.remove_user(email)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/saml_app_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe Datadog do\n\n  let(:org) { create(:organisation) }\n  let(:app_name) { 'datadog' }\n  let(:user) { create(:user) }\n\n  describe 'initialize' do\n    it 'fetches configuration if already saved' do\n      config = create(:saml_app_config, organisation: org)\n      saml_app = Datadog.new(org.id)\n      expect(saml_app.config).to eq(config)\n    end\n\n    it 'initializes configuration if not created' do\n      saml_app = Datadog.new(org.id)\n      expect(saml_app.config.persisted?).to eq(false)\n    end\n  end\n\n  describe 'save_config' do\n    let(:config) { build(:saml_app_config, organisation: org) }\n    let(:saml_app) { Datadog.new(org.id) }\n\n    it 'update the url' do\n      saml_app.save_config(config.sso_url, config.config)\n      expect(saml_app.config.sso_url).to eq(config.sso_url)\n    end\n\n    it 'create a group if a group doesn\\'t exist' do\n      org_name = \"#{org.slug}_saml_#{saml_app.app_name}_users\"\n      expect(Group).to receive(:find_or_create_by).\n        with(name: org_name)\n      saml_app.save_config(config.sso_url, config.config)\n    end\n\n    it 'creates a group name with slug_saml_app_name_users' do\n      org_name = \"#{org.slug}_saml_#{saml_app.app_name}_users\"\n      saml_app.save_config(config.sso_url, config.config)\n      expect(saml_app.config.group.name).to eq(org_name)\n    end\n  end\n\n  describe 'add_user' do\n    let(:saml_app) { Datadog.new(org.id) }\n\n    before do\n      create(\n        :saml_app_config,\n        organisation: org,\n        group: Group.new(name: \"#{org.slug}_saml_datadog_users\")\n      )\n      allow_any_instance_of(DataDogClient).\n        to receive(:get_user).and_return(handle: user.email)\n      allow_any_instance_of(DataDogClient).\n        to receive(:activate_user).and_return(handle: user.email)\n    end\n\n    it 'add user to the group' do\n      expect_any_instance_of(Group).\n        to receive(:add_user).with(user.id)\n      saml_app.add_user(user.email)\n    end\n  end\n\n  describe 'remove_user' do\n    let(:saml_app) { Datadog.new(org.id) }\n\n    before do\n      create(\n        :saml_app_config,\n        organisation: org,\n        group: Group.new(name: \"#{org.slug}_saml_datadog_users\")\n      )\n      allow_any_instance_of(DataDogClient).\n        to receive(:deactivate_user).and_return(handle: user.email)\n    end\n\n    it 'remove user from the group' do\n      expect_any_instance_of(Group).\n        to receive(:remove_user).with(user.id)\n      saml_app.remove_user(user.email)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/tasks/users_rake_spec.rb",
    "content": "require 'rails_helper'\n\ndescribe \"users:purge_inactive\" do\n  before(:each) do\n    create(:user)\n    @user = create(:user)\n    @user.update!(active: false)\n  end\n\n  it \"purge users whom have been deactivated for more than certain time\" do\n    Gate::Application.load_tasks\n    @user.update_column(:deactivated_at, Time.now - 16.days)\n    Rake::Task['users:purge_inactive'].invoke\n    @user.reload\n    expect(@user.group_associations.length).to eq 0\n  end\nend\n"
  },
  {
    "path": "spec/models/access_token_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe AccessToken, type: :model do\n  let(:user) {\n    FactoryBot.create(:user,\n      name: \"foobar\",\n      admin: true,\n      user_login_id: \"foobar\",\n      email: \"foobar@foobar.com\"\n    )\n  }\n\n  before(:each) do\n    @access_token = AccessToken.new\n    @access_token.token = ROTP::Base32.random_base32\n    @access_token.user = user\n    @access_token.save!\n  end\n\n  describe \"self.find_token\" do\n    it \"should return access token object if it finds matching token\" do\n      expect(AccessToken.find_token(@access_token.token)).to eq @access_token\n    end\n  end\n\n  describe \"self.valid_token\" do\n    it \"should return true if it finds matching token\" do\n      expect(AccessToken.valid_token(@access_token.token)).to eq true\n    end\n  end\n\n  describe \"hash_token! before_save\" do\n    it \"should hash token and put it into hashed_token\" do\n      expect(@access_token.hashed_token).to eq(\n        Digest::SHA512.hexdigest(@access_token.token))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/models/api_resource_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe ApiResource, type: :model do\n  let(:user) {\n    FactoryBot.create(:user,\n      name: \"foobar\",\n      admin: true,\n      user_login_id: \"foobar\",\n      email: \"foobar@foobar.com\"\n    )\n  }\n  let(:group) { FactoryBot.create(:group, name: \"foobar_group\") }\n  let(:valid_attributes) do\n    {\n      name: \"sample_api\",\n      description: \"sample_api_description\",\n      access_key: \"xcz\",\n      user_id: user.id,\n      group_id: group.id\n    }\n  end\n\n  describe \"self.authenticate\" do\n    it \"should return true if it finds matching access_key and the user is member of the group\" do\n      api_resource = ApiResource.create(valid_attributes)\n      group.users << user\n      access_token = AccessToken.new\n      access_token.token = ROTP::Base32.random_base32\n      access_token.user = user\n      access_token.save!\n      expect(ApiResource.authenticate(valid_attributes[:access_key], access_token.token)).to eq true\n    end\n  end\n\n  describe \"hash_access_key! before_save\" do\n    it \"should hash access_key and put it into hashed_access_key\" do\n      api_resource = ApiResource.create(valid_attributes)\n      expect(api_resource.hashed_access_key).to eq(\n        Digest::SHA512.hexdigest(valid_attributes[:access_key]))\n    end\n\n    it \"shouldn't change hashed_access_key if access_key isn't supplied\" do\n      api_resource = ApiResource.create(valid_attributes)\n      api_resource.update(description: \"Change Description\")\n      expect(api_resource.hashed_access_key).to eq(\n        Digest::SHA512.hexdigest(valid_attributes[:access_key]))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/models/endpoint_spec.rb",
    "content": "require 'rails_helper'\n\ndescribe Endpoint, type: :model do\n  describe 'validations' do\n    describe 'path' do\n      context 'when given nil path' do\n        it 'should not valid' do\n          endpoint = build(:endpoint, path: nil)\n          expect(endpoint).not_to be_valid\n        end\n      end\n\n      context 'when given path with parameter' do\n        it 'should valid' do\n          endpoint = build(:endpoint, path: '/users/:id')\n          expect(endpoint).to be_valid\n        end\n      end\n\n      context 'when given invalid path' do\n        it 'should not valid' do\n          endpoint = build(:endpoint, path: '/users/::id')\n          expect(endpoint).not_to be_valid\n        end\n      end\n\n      context 'when given path ended with /' do\n        it 'should not valid' do\n          endpoint = build(:endpoint, path: '/users/')\n          expect(endpoint).not_to be_valid\n        end\n      end\n    end\n\n    describe 'method' do\n      context 'when given nil method' do\n        it 'should not valid' do\n          endpoint = build(:endpoint, method: nil)\n          expect(endpoint).not_to be_valid\n        end\n      end\n\n      context 'when given unknown method' do\n        it 'should not valid' do\n          endpoint = build(:endpoint, method: 'JUMP')\n          expect(endpoint).not_to be_valid\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/models/group_association_spec.rb",
    "content": "require 'rails_helper'\n\ndescribe GroupAssociation, type: :model do\n  describe '.revoke_expired' do\n    let(:user) { create(:user) }\n    let(:group) { create(:group) }\n\n    context 'when found expired associations' do\n      it 'should revoke associations' do\n        group.add_user_with_expiration(user, Date.today - 1)\n\n        GroupAssociation.revoke_expired\n\n        expired_association = GroupAssociation.where('expiration_date < ?', Date.today).count\n        expect(expired_association).to eq 0\n      end\n\n      it 'should create paper trail with event destroy' do\n        group_association = group.add_user_with_expiration(user, Date.today - 1)\n\n        GroupAssociation.revoke_expired\n\n        versions = PaperTrail::Version.\n          with_item_keys(GroupAssociation.name, group_association.id).\n          where(event: 'destroy')\n        expect(versions.length).to eq(1)\n      end\n    end\n\n    it 'should not revoke associations that not expired yet' do\n      group.add_user_with_expiration(user, Date.today)\n\n      GroupAssociation.revoke_expired\n\n      unexpired_association = GroupAssociation.where('expiration_date >= ?', Date.today).count\n      expect(unexpired_association).to eq 1\n    end\n  end\nend\n"
  },
  {
    "path": "spec/models/group_endpoint_spec.rb",
    "content": "require 'rails_helper'\n\ndescribe GroupEndpoint, type: :model do\n  let(:group) { create(:group) }\n  let(:endpoint) { create(:endpoint) }\n\n  describe 'validations' do\n    context 'given duplicate group with endpoint' do\n      it 'should not valid' do\n        GroupEndpoint.create(group: group, endpoint: endpoint)\n        group_endpoint = GroupEndpoint.new(group: group, endpoint: endpoint)\n        expect(group_endpoint).not_to be_valid\n      end\n    end\n\n    context 'given nil group' do\n      it 'should not valid' do\n        group_endpoint = GroupEndpoint.new(group: nil, endpoint: endpoint)\n        expect(group_endpoint).not_to be_valid\n      end\n    end\n\n    context 'given nil endpoint' do\n      it 'should not valid' do\n        group_endpoint = GroupEndpoint.new(group: group, endpoint: nil)\n        expect(group_endpoint).not_to be_valid\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/models/group_spec.rb",
    "content": "require 'rails_helper'\nGID_CONSTANT = 9000\nRSpec.describe Group, type: :model do\n  context 'validate uniqueness' do\n    subject { FactoryBot.create(:group) }\n    it { should validate_uniqueness_of(:name).ignoring_case_sensitivity }\n  end\n\n  it 'should save gid after create' do\n    group = create(:group)\n    expect(group.gid.to_i).to eq(group.id + GID_CONSTANT)\n  end\n\n  it 'should provide name response' do\n    user = create(:user)\n    group_response = Group.get_name_response user.groups.first.name\n    expect(group_response.count).to eq(4)\n    expect(group_response[:gr_mem].count).to eq(1)\n    expect(group_response[:gr_mem][0]).to eq(user.user_login_id)\n  end\n\n  it 'should provide gid response' do\n    group = create(:group)\n    group_response = Group.get_gid_response group.gid\n    expect(group_response.count).to eq(4)\n    expect(group_response[:gr_name]).to eq(group.name)\n  end\n\n  it 'should provide correct gid response even if we add a machine to group' do\n    group = create(:group)\n    user = create(:user)\n    user.groups << group\n    user.save\n    host_machine = create(:host_machine)\n    host_machine.groups << group\n    host_machine.save!\n    group_response = Group.get_name_response group.name\n    expect(group.host_machines.count).to eq(1)\n    expect(group_response.count).to eq(4)\n    expect(group_response[:gr_mem].count).to eq(1)\n    expect(group_response[:gr_mem][0]).to eq(user.user_login_id)\n\n    host_response = HostMachine.get_group_response host_machine.name\n    expect(host_machine.groups.count).to eq(1)\n    expect(host_response[:groups].count).to eq(1)\n    expect(host_response[:groups][0]).to eq(group.name)\n  end\n\n  describe 'get_sysadmins_and_groups' do\n    it 'If the group is empty, response should still be generated / not raising any exception' do\n      groups = []\n      user = create(:user)\n      groups << Group.generate_group_response(user.user_login_id, GroupAssociation.where(user_id: user.id).first.id, '')\n      GroupAssociation.where(user_id: user.id).each{ |x| x.destroy }\n      groups << Group.get_default_sysadmin_group_for_host([user.id])\n      expect{Group.get_sysadmins_and_groups([user.id])}.not_to raise_error\n    end\n  end\n\n  describe 'add_user' do\n    let(:user) { create(:user) }\n    let(:group) { create(:group) }\n\n    it 'add user to the group' do\n      group.add_user(user.id)\n      expect(group.users.map(&:id).include?(user.id)).to eq(true)\n    end\n\n    it 'not add user if already added to the group' do\n      group.add_user(user.id)\n      group.add_user(user.id)\n      expect(group.users.where(id: user.id).size).to eq(1)\n    end\n\n    it 'should burst host cache' do\n      expect(group).to receive(:burst_host_cache)\n\n      group.add_user(user.id)\n    end\n  end\n\n  describe 'add_user_with_expiration' do\n    let(:user) { create(:user) }\n    let(:group) { create(:group) }\n\n    it 'add user to the group with expiration date' do\n      expiration_date = Date.parse('2019-06-20')\n\n      group.add_user_with_expiration(user.id, expiration_date)\n\n      group_association = group.group_associations.where(user_id: user.id).take\n      expect(group_association.expiration_date).to eq(expiration_date)\n    end\n\n    it 'replace old expiration date if user already added to the group' do\n      old_expiration_date = Date.parse('2019-06-20')\n      new_expiration_date = Date.parse('2019-10-20')\n\n      group.add_user_with_expiration(user.id, old_expiration_date)\n      group.add_user_with_expiration(user.id, new_expiration_date)\n\n      group_association = group.group_associations.where(user_id: user.id).take\n      expect(group_association.expiration_date).to eq(new_expiration_date)\n    end\n  end\n\n  describe 'remove_user' do\n    let(:user) { create(:user) }\n    let(:group) { create(:group) }\n\n    it 'removes users from the group' do\n      group.add_user(user.id)\n      group.remove_user(user.id)\n      expect(group.users.map(&:id).include?(user.id)).to eq(false)\n    end\n\n    it 'should create paper trail with event destroy' do\n      group.add_user(user.id)\n      group_association_id = group.group_associations.where(user_id: user.id).first.id\n      group.remove_user(user.id)\n\n      versions = PaperTrail::Version.\n        with_item_keys(GroupAssociation.name, group_association_id).\n        where(event: 'destroy')\n      expect(versions.length).to eq(1)\n    end\n\n    it 'should burst host cache' do\n      group.add_user(user.id)\n\n      expect(group).to receive(:burst_host_cache)\n\n      group.remove_user(user.id)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/models/host_machine_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe HostMachine, type: :model do\n  context \"access\" do\n\n    it \"should compute overall groups and returns members in sysadmins group\" do\n      host_machine = create(:host_machine)\n      group = create(:group)\n      user = create(:user)\n\n\n      group = create(:group)\n      group.host_machines << host_machine\n      if !group.member? user\n        group.users << user\n      end\n      group.save!\n      group.reload\n\n      expect(host_machine.sysadmins.count).to eq 1\n\n      user = create(:user)\n      if !group.member? user\n        group.users << user\n      end\n      group.save!\n      host_machine.reload\n\n      host_machine.save #bad design, causing cache to burst\n\n      expect(host_machine.sysadmins.count).to eq 2\n      group = create(:group)\n      group.host_machines << host_machine\n      group.save!\n\n      host_machine.save #bad design, causing cache to burst\n\n      host_machine.reload\n\n      expect(host_machine.sysadmins.count).to eq 2\n      group.users << user\n      group.save!\n\n      host_machine.save #bad design, causing cache to burst\n\n      host_machine.reload\n      expect(host_machine.sysadmins.count).to eq 2\n\n      user = create :user\n\n      group.users << user\n      group.save!\n      host_machine.save #bad design, causing cache to burst\n\n      host_machine.reload\n      expect(host_machine.sysadmins.count).to eq 3\n    end\n  end\n\n  context \"sysadmin_group\" do\n    it \"should return sysadmins, their groups and sysadmin group container sysadmins\" do\n\n      host_machine = create(:host_machine)\n      group = create(:group)\n      user = create(:user)\n\n\n      group = create(:group)\n      group.host_machines << host_machine\n      if !group.member? user\n        group.users << user\n      end\n\n      user = create(:user)\n      if !group.member? user\n        group.users << user\n      end\n      group.save!\n      host_machine.save #bad design, causing cache to burst\n\n      host_machine.reload\n      group.reload\n\n      expect(host_machine.sysadmins.count).to eq 2\n      response = Group.get_sysadmins_and_groups host_machine.sysadmins\n      sysadmins_and_groups = JSON.parse(response)\n\n      expect(sysadmins_and_groups.count).to eq 3\n\n    end\n  end\n\n  context 'add_host_group' do\n    let(:host_machine) { HostMachine.find_or_create_by(name: 'machine')  }\n    it 'should create host group given valid name' do\n      host_machine.add_host_group(host_machine.name)\n      groups = host_machine.groups.map(&:name)\n      expect(groups.include?(\"#{host_machine.name}_host_group\")).to eq(true)\n      expect(host_machine.valid?).to eq(true)\n    end\n\n    it 'should create the group with all downcase' do\n      host_machine.add_host_group(host_machine.name.upcase)\n      groups = host_machine.groups.map(&:name)\n      expect(groups.include?(\"#{host_machine.name.downcase}_host_group\")).to eq(true)\n    end\n\n    it 'should not add the group if the name is invalid' do\n      host_machine.add_host_group('')\n      groups = host_machine.groups.map(&:name)\n      expect(groups.include?(\"\")).to eq(false)\n      expect(groups.include?(\"_host_group\")).to eq(false)\n    end\n  end\n\n  context 'add_group' do\n    let(:host_machine) { HostMachine.find_or_create_by(name: 'machine')  }\n    let(:group_name) { 'machine_group'  }\n    it 'should create host group given valid name' do\n      host_machine.add_group(group_name)\n      groups = host_machine.groups.map(&:name)\n      expect(groups.include?(group_name)).to eq(true)\n      expect(host_machine.valid?).to eq(true)\n    end\n\n    it 'should create the group with all downcase' do\n      host_machine.add_group(group_name.upcase)\n      groups = host_machine.groups.map(&:name)\n      expect(groups.include?(group_name.downcase)).to eq(true)\n    end\n\n    it 'should not add the group if the name is invalid' do\n      host_machine.add_group('')\n      groups = host_machine.groups.map(&:name)\n      expect(groups.include?(\"\")).to eq(false)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/models/host_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe Host, type: :model do\nend\n"
  },
  {
    "path": "spec/models/ip_address_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe IpAddress, type: :model do\nend\n"
  },
  {
    "path": "spec/models/organisation_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe Organisation, type: :model do\n  describe '.find_by_slug' do\n    let(:org) { create(:organisation) }\n    it 'should return organisation based on slug' do\n      expect(Organisation.find_by_slug(org.slug)).to eq(org)\n    end\n  end\n\n  describe '.setup' do\n    let(:org_data) { attributes_for(:organisation) }\n    it 'should create organisation' do\n      org = Organisation.setup(org_data)\n      org_data.each do |key, value|\n        expect(org.send(key.to_sym)).to eq(value)\n      end\n      expect(org.persisted?).to eq(true)\n      expect(org.valid?).to eq(true)\n    end\n\n    it 'should not create organisation if validations fail' do\n      org = Organisation.setup(name: org_data[:name])\n      expect(org.persisted?).to eq(false)\n      expect(org.valid?).to eq(false)\n      expect(org.errors.messages.key?(:website)).to eq(true)\n      expect(org.errors.messages.key?(:domain)).to eq(true)\n    end\n  end\n\n  describe '.update_profile' do\n    let(:org) { create(:organisation) }\n    let(:org_data) { attributes_for(:organisation) }\n    it 'should update organisation profile' do\n      org.update_profile(org_data)\n      org_data.each do |key, value|\n        expect(org.send(key.to_sym)).to eq(value)\n      end\n      expect(org.valid?).to eq(true)\n    end\n\n    it 'shouldn not update organisation profile if validations fail' do\n      org.update_profile(name: '')\n      expect(org.valid?).to eq(false)\n      expect(org.errors.messages.key?(:name)).to eq(true)\n    end\n  end\n\n  describe '.setup_saml_certs' do\n    let(:org) { create(:organisation) }\n    it 'should set the subject based on organisation profile' do\n      org.setup_saml_certs\n      subject = Hash[org.cert.subject.to_a.map { |i| [i[0].to_sym, i[1]] }]\n      expected_subject = {\n        C: org.country, ST: org.state, L: org.address, O: org.name, OU: org.unit_name,\n        CN: org.domain\n      }\n      expect(subject).to eq(expected_subject)\n    end\n\n    it 'should set the expiry of the certificate for 1 year' do\n      org.setup_saml_certs\n      Timecop.freeze(Time.now - 10.minutes)\n      expect(org.cert.not_before > Time.now).to eq(true)\n      expect(org.cert.not_after > Time.now + 365 * 24 * 60 * 60).to eq(true)\n    end\n\n    it 'should update the certificate for the organisation' do\n      org.setup_saml_certs\n      fingerprint = OpenSSL::Digest::SHA256.hexdigest(org.cert.to_der).scan(/../).join(':')\n      private_key = org.rsa_key.to_pem\n      cert = org.cert.to_pem\n      expect(org.cert_fingerprint).to eq(fingerprint)\n      expect(org.cert_private_key).to eq(private_key)\n      expect(org.cert_key).to eq(cert)\n    end\n  end\n\n  describe '.saml_setup?' do\n    let(:org) { create(:organisation) }\n    it 'should return true if saml is setup' do\n      org.setup_saml_certs\n      expect(org.saml_setup?).to eq(true)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/models/user_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe User, type: :model do\n  let(:uid_constant) { 5000 }\n  describe 'generate_login_id' do\n    it 'should generate login id' do\n      user = build(:user)\n      expect(user.generate_login_id).to eq(user.email.split('@').first)\n    end\n  end\n\n  describe 'find_and_validate_saml_user' do\n    let(:user) { create(:user) }\n    let(:group) { create(:group) }\n    it 'returns false if user is not active' do\n      user.update_attribute(:active, false)\n      expect(User.find_and_validate_saml_user(user.email, 123456, 'datadog')).to eq(false)\n    end\n\n    it 'returns false if the user doesn\\'t belong to app group' do\n      expect(User.find_and_validate_saml_user(user.email, 123456, 'datadog')).to eq(false)\n    end\n\n    it 'returns user if all credentials are valid' do\n      user.groups << group\n      allow_any_instance_of(User).to receive(:valid_otp?).and_return(true)\n      expect(User.find_and_validate_saml_user(user.email, 123456, group.name)).to eq(user)\n    end\n\n    it 'validates the user password' do\n      user.groups << group\n      expect_any_instance_of(User).to receive(:valid_otp?)\n      User.find_and_validate_saml_user(user.email, 123456, group.name)\n    end\n  end\n\n  describe 'valid_otp?' do\n    let(:user) { create(:user) }\n    before do\n      user.generate_two_factor_auth(true)\n      Timecop.freeze\n    end\n\n    it 'expires redis cache' do\n      user_key = \"#{user.id}:#{Time.now.hour}\"\n      expect(REDIS_CACHE).to receive(:expire).with(user_key, 3600)\n      user.valid_otp?(123456)\n    end\n\n    it 'return false if rate limit is exceeded' do\n      user_key = \"#{user.id}:#{Time.now.hour}\"\n      allow(REDIS_CACHE).to receive(:incrby).with(user_key, 1).and_return(RATE_LIMIT + 1)\n      expect(user.valid_otp?(123456)).to eq(false)\n    end\n\n    it 'validates otp token' do\n      allow_any_instance_of(ROTP::TOTP).to receive(:now).and_return(123456)\n      expect(user.valid_otp?(123456)).to eq(true)\n    end\n  end\n\n  describe 'generate_uid' do\n    let(:user) { build(:user) }\n    it 'should generate uid' do\n      expect(user.generate_uid).to eq(uid_constant)\n    end\n\n    it 'should generate uid as the uid buffer if there are no records' do\n      user_new = create(:user)\n      expect(user_new.generate_uid).to eq(User.last.id + uid_constant)\n    end\n\n    it 'should use configured uid buffer rather than the default value' do\n      cached_uid_buffer = ENV['UID_BUFFER']\n      ENV['UID_BUFFER'] = '6000'\n      expect(user.generate_uid).to eq(6000)\n      ENV['UID_BUFFER'] = cached_uid_buffer\n    end\n  end\n\n  describe 'initialize_host_and_group' do\n    let(:user) { build(:user) }\n    it 'should initialize host for the user' do\n      user.initialise_host_and_group\n      expect(user.hosts.size).to eq(1)\n    end\n\n    it 'should initialize group for the user' do\n      user.initialise_host_and_group\n      expect(user.groups.size).to eq(1)\n    end\n\n    it 'should set the host pattern if configured' do\n      cached_default_host_pattern = ENV['DEFAULT_HOST_PATTERN']\n      ENV['DEFAULT_HOST_PATTERN'] = 'S*'\n      user.initialise_host_and_group\n      expect(user.hosts.first.host_pattern).to eq('S*')\n      ENV['DEFAULT_HOST_PATTERN'] = cached_default_host_pattern\n    end\n  end\n\n  describe 'create_user' do\n    it 'shouldn\\'t create the user if email is already registered' do\n      user = create(:user)\n      User.create_user(user.name, user.email)\n      expect(User.where(email: user.email).size).to eq(1)\n    end\n\n    it 'should create admin user if its first user being created' do\n      user_data = attributes_for(:user)\n      user = User.create_user(user_data[:name], user_data[:email])\n      expect(user.admin).to eq(true)\n    end\n\n    it 'should generate login id' do\n      user_data = attributes_for(:user)\n      expect_any_instance_of(User).to receive(:generate_login_id)\n      User.create_user(user_data[:name], user_data[:email])\n    end\n\n    it 'should generate uid' do\n      user_data = attributes_for(:user)\n      expect_any_instance_of(User).to receive(:generate_uid)\n      User.create_user(user_data[:name], user_data[:email])\n    end\n\n    it 'should initialise host and group for user' do\n      user_data = attributes_for(:user)\n      expect_any_instance_of(User).to receive(:initialise_host_and_group)\n      User.create_user(user_data[:name], user_data[:email])\n    end\n  end\n\n  describe 'generate_two_factor_auth' do\n    let(:rotp_key) { ROTP::Base32.random_base32 }\n    let(:new_rotp_key) { ROTP::Base32.random_base32 }\n\n    before(:each) do |ex|\n      unless ex.metadata[:skip_before]\n        allow(ROTP::Base32).to receive(:random_base32).and_return(rotp_key)\n      end\n    end\n\n    it 'shouldn\\'t generate key if user is not created' do\n      user = build(:user)\n      user.generate_two_factor_auth\n      expect(user.auth_key.blank?).to eq(true)\n      expect(user.provisioning_uri.blank?).to eq(true)\n    end\n\n    it 'should generate auth_key' do\n      user = create(:user)\n      user.generate_two_factor_auth\n      expect(user.auth_key).to eq(rotp_key)\n    end\n\n    it 'should update provisioning url' do\n      user = create(:user)\n      user.generate_two_factor_auth\n      url = ROTP::TOTP.new(rotp_key).provisioning_uri \"GoJek-C #{user.email}\"\n      expect(user.provisioning_uri).to eq(url)\n    end\n\n    it 'shouldn\\'t generate the token if it\\'s already generated', skip_before: true do\n      user = create(:user)\n      allow(ROTP::Base32).to receive(:random_base32).and_return(new_rotp_key)\n      url = ROTP::TOTP.new(new_rotp_key).provisioning_uri \"GoJek-C #{user.email}\"\n      user.generate_two_factor_auth\n      allow(ROTP::Base32).to receive(:random_base32).and_return(rotp_key)\n      user.generate_two_factor_auth\n      user.reload\n      expect(user.auth_key).to eq(new_rotp_key)\n      expect(user.provisioning_uri).to eq(url)\n    end\n\n    it 'should generate the token if its already generated and force_generate is true',\n      skip_before: true do\n      user = create(:user)\n      allow(ROTP::Base32).to receive(:random_base32).and_return(new_rotp_key)\n      url = ROTP::TOTP.new(new_rotp_key).provisioning_uri \"GoJek-C #{user.email}\"\n      user.generate_two_factor_auth true\n      allow(ROTP::Base32).to receive(:random_base32).and_return(rotp_key)\n      user.generate_two_factor_auth\n      expect(user.auth_key).to eq(rotp_key)\n      expect(user.provisioning_uri).to eq(url)\n    end\n  end\n\n  describe 'add_temp_user' do\n    let(:user_data) { attributes_for(:user) }\n    let(:rotp_key) { ROTP::Base32.random_base32 }\n    let(:domain) { 'test.com' }\n    before(:each) do\n      @cached_gate_hosted_domain = ENV['GATE_HOSTED_DOMAIN']\n      ENV['GATE_HOSTED_DOMAIN'] = domain\n      allow(ROTP::Base32).to receive(:random_base32).and_return(rotp_key)\n    end\n\n    after(:each) do\n      ENV['GATE_HOSTED_DOMAIN'] = @cached_gate_hosted_domain\n    end\n\n    it 'the email should be appended with the configured hosted domain' do\n      User.add_temp_user(user_data[:name], user_data[:email])\n      user = User.where(email: \"#{user_data[:email]}@#{domain}\").first\n      expect(user.present?).to eq(true)\n    end\n\n    it 'should generate auth_key' do\n      expect_any_instance_of(User).to receive(:generate_two_factor_auth)\n      User.add_temp_user(user_data[:name], user_data[:email])\n    end\n  end\n\n  describe 'update_profile' do\n    let(:user) { create(:user) }\n    it 'should update user profile' do\n      public_key = OpenSSL::PKey::RSA.new(2048).public_key.to_pem\n      attrs = { name: Faker::Name.name, admin: true, active: true, public_key: public_key }\n      user.update_profile(attrs)\n      expect(user.name).to eq(attrs[:name])\n      expect(user.admin).to eq(attrs[:admin])\n      expect(user.active).to eq(attrs[:active])\n      expect(user.public_key).to eq(attrs[:public_key])\n    end\n\n    it 'should update the product name' do\n      name = 'test_product'\n\n      user.update_profile(product_name: name)\n\n      expect(user.product_name).to eq(name)\n    end\n\n    it 'should update the name' do\n      name = 'test_name'\n\n      user.update_profile(name: name)\n\n      expect(user.name).to eq(name)\n    end\n\n    it 'should update the public_key' do\n      rsa_key = OpenSSL::PKey::RSA.new(2048)\n      public_key = rsa_key.public_key.to_pem\n\n      user.update_profile(public_key: public_key)\n\n      expect(user.public_key).to eq(public_key)\n    end\n\n    it 'should set deactivation time when user is deactivated' do\n      user.update_profile(active: false)\n\n      expect(user.deactivated_at).not_to be nil\n    end\n\n    it 'should update user profile only for public_key, name, product_name, admin and active' do\n      auth_key = ROTP::Base32.random_base32\n      user.update_profile(auth_key: auth_key)\n      expect(user.auth_key).not_to eq(auth_key)\n    end\n\n    it 'should update the deactivated_at date if user is made inactive' do\n      Timecop.freeze(Time.current)\n      inactive_user = create(:user, admin: true)\n      inactive_user.update_profile(active: false)\n      expect(inactive_user.deactivated_at.to_s).to eq(Time.current.to_s)\n    end\n\n    it 'shouldn\\'t make the admin user a normal user if its only single admin user' do\n      user.update_profile(active: false)\n      expect(user.errors.messages.key?(:admin)).to eq(true)\n      expect(user.valid?).to eq(false)\n    end\n  end\n\n  describe '#purge!' do\n    let(:user) { create(:user) }\n    it 'should remove group associations for inactive user' do\n      create(:user)\n      user.update!(active: false)\n\n      user.purge!\n\n      user.reload\n      expect(user.group_associations.length).to eq 0\n    end\n\n    it 'should NOT remove group associations for active user' do\n      create(:user)\n\n      user.purge!\n\n      user.reload\n      expect(user.group_associations.length).not_to eq 0\n    end\n  end\n\n  describe '#update' do\n    context 'deactivate admin user' do\n      it 'should revoke admin status' do\n        create(:admin_user)\n        admin = create(:admin_user)\n\n        admin.update(active: false)\n        admin.reload\n\n        expect(admin.admin?).to be false\n      end\n    end\n\n    context 'deactivate user' do\n      it 'should set deactivated_at with current timestamp' do\n        Timecop.freeze(Time.current)\n        create(:admin_user)\n        user = create(:user, admin: false)\n        user.update(active: false)\n        expect(user.deactivated_at).to eq(Time.current.to_s)\n      end\n\n      it 'should not replace old deactivated_at' do\n        create(:admin_user)\n        user = create(:user, admin: false)\n        user.update(active: false)\n        old_deactivated_date = user.deactivated_at\n        Timecop.scale(3600)\n        sleep(1)\n        user.update(active: false)\n        expect(user.deactivated_at).to eq(old_deactivated_date)\n      end\n    end\n  end\n\n  describe '#uid' do\n    it 'should check uid creation with offset' do\n      user = create(:user)\n\n      expect(user.uid.to_i).to eq(user.id + uid_constant)\n    end\n  end\n\n  describe '#user_login_id' do\n    it 'should return _ for . in name' do\n      user = create(:user)\n      expect(user.user_login_id).to eq(user.email.split('@').first)\n      user = User.get_user(user.user_login_id)\n      expect(user).not_to be nil\n    end\n  end\n\n  describe '#permitted_hosts?' do\n    it 'should return false if user is not permitted' do\n      user = create(:user)\n\n      response = user.permitted_hosts? ['10.1.1.1.']\n\n      expect(response).to be false\n    end\n\n    it \"should fails host address if it's not permitted\" do\n      user = create(:user)\n      host = Host.new\n      host.user = user\n      host.host_pattern = 's*' # by default give host access to all staging instances\n      host.save!\n\n      expect(user.permitted_hosts?(['10.0.0.0'])).to be false\n    end\n\n    it \"should pass host address if it's permitted\" do\n      user = create(:user)\n      host = Host.new\n      host.user = user\n      host.host_pattern = '.*' # by default give host access to all staging instances\n      host.save!\n\n      expect(user.permitted_hosts?(['10.0.0.0'])).to be true\n    end\n  end\n\n  describe '#within_limits?' do\n    it 'login limits should pass' do\n      user = create(:user)\n      user.reset_login_limit\n      (RATE_LIMIT - 2).times do\n        user.within_limits?\n      end\n\n      expect(user.within_limits?).to be true\n    end\n\n    it 'login limits should fail' do\n      user = create(:user)\n      (RATE_LIMIT + 2).times do\n        user.within_limits?\n      end\n\n      expect(user.within_limits?).to be false\n    end\n  end\n\n  describe '#authenticate_ms_chap' do\n    it 'should authenticate ms chap' do\n      user = create(:user)\n      totp = '757364'\n      challenge_string = 'ee85e142eadfec52'\n      response_string = '0392a9e43edee3129f735b37fd9d0b0d3f66aa7a00f35440'\n\n      success_response = user.authenticate_ms_chap(totp, challenge_string, response_string)\n      unsuccessful_response = user.authenticate_ms_chap('78787', challenge_string, response_string)\n\n      expect(success_response).to eq('NT_KEY: 57247E8BAD1959F9544B2C5057F77AD8')\n      expect(unsuccessful_response).to eq('NT_STATUS_UNSUCCESSFUL: Failure (0xC0000001)')\n    end\n  end\n\n  describe '#authenticate_ms_chap_with_drift' do\n    it 'should authenticate ms chap with drift' do\n      user = create(:user)\n      challenge_string = 'ee85e142eadfec52'\n      response_string = '0392a9e43edee3129f735b37fd9d0b0d3f66aa7a00f35440'\n\n      totp = ['757364', '123456', '876543']\n      response = user.authenticate_ms_chap_with_drift(totp, challenge_string, response_string)\n      expect(response).to eq('NT_KEY: 57247E8BAD1959F9544B2C5057F77AD8')\n\n      totp = ['123456', '757364', '876543']\n      response = user.authenticate_ms_chap_with_drift(totp, challenge_string, response_string)\n      expect(response).to eq('NT_KEY: 57247E8BAD1959F9544B2C5057F77AD8')\n\n      totp = ['123456', '876543', '757364']\n      response = user.authenticate_ms_chap_with_drift(totp, challenge_string, response_string)\n      expect(response).to eq('NT_KEY: 57247E8BAD1959F9544B2C5057F77AD8')\n\n      totp = ['78787', '121212', '545454']\n      response = user.authenticate_ms_chap_with_drift(totp, challenge_string, response_string)\n      expect(response).to eq('NT_STATUS_UNSUCCESSFUL: Failure (0xC0000001)')\n    end\n  end\n\n  describe '#get_user_otp_at' do\n    it 'should return different otps for different times' do\n      user = create(:user)\n      user.auth_key = ROTP::Base32.random_base32\n      drift_interval = 30\n      t = Time.now\n\n      otp1 = user.get_user_otp_at(t)\n      otp2 = user.get_user_otp_at(t - drift_interval)\n      otp3 = user.get_user_otp_at(t + drift_interval)\n\n      expect(otp1).not_to equal(otp2)\n      expect(otp2).not_to equal(otp3)\n      expect(otp3).not_to equal(otp1)\n    end\n  end\n\n  describe '#permitted_endpoint?' do\n    context 'given permitted endpoint' do\n      it 'should return true' do\n        user = create(:user)\n        group = create(:group)\n        endpoint = create(:endpoint)\n        group.endpoints << endpoint\n        user.groups << group\n        expect(user.permitted_endpoint?(endpoint)).to be true\n      end\n    end\n\n    context 'given non permitted endpoint' do\n      it 'should return false' do\n        user = create(:user)\n        group = create(:group)\n        endpoint = create(:endpoint)\n        group.endpoints << endpoint\n        expect(user.permitted_endpoint?(endpoint)).to be false\n      end\n    end\n\n    context 'given nil endpoint' do\n      it 'should return false' do\n        user = create(:user)\n        expect(user.permitted_endpoint?(nil)).to be false\n      end\n    end\n  end\n\n  describe '.get_shadow_name_response' do\n    it 'should return response with sp_namp equal user_login_id' do\n      user = create(:user)\n\n      response = User.get_shadow_name_response user.name\n\n      expect(response[:sp_namp]).to eq(user.user_login_id)\n    end\n  end\n\n  describe '.get_all_passwd_response' do\n    it 'should get all users for passwd' do\n      create_list(:user, 2)\n\n      response = User.get_all_passwd_response\n\n      expect(response.count).to eq(2)\n    end\n  end\n\n  describe '.find_active_user_by_email' do\n    it 'should return user if email registered' do\n      email = Faker::Internet.email\n      create(:user, email: email)\n\n      user = User.find_active_user_by_email(email)\n\n      expect(user.email).to eq(email)\n    end\n\n    it 'should return nil if email not registered' do\n      email = Faker::Internet.email\n\n      user = User.find_active_user_by_email(email)\n\n      expect(user).to be nil\n    end\n\n    it 'should return registered groups list' do\n      email = Faker::Internet.email\n      create(:user, email: email)\n\n      user = User.find_active_user_by_email(email)\n\n      expect(user.group_names_list).to include user.user_login_id\n    end\n  end\n\n  describe '.valid_domain?' do\n    it 'should check valid hosted domain' do\n      allow(ENV).to receive(:[]).with('GATE_HOSTED_DOMAINS').and_return('alfa.com,beta.com')\n      expect(User.valid_domain?('alfa.com')).to be true\n      expect(User.valid_domain?('beta.com')).to be true\n      expect(User.valid_domain?('gama.com')).to be false\n\n      allow(ENV).to receive(:[]).with('GATE_HOSTED_DOMAINS').and_return('')\n      expect(User.valid_domain?('alfa.com')).to be false\n    end\n  end\n\n  describe '.ms_chap_auth' do\n    it 'should authenticate ms chap' do\n      user = create(:user)\n      vpn = Vpn.create(name: :X, ip_address: '10.240.0.1' )\n      user.groups.first.vpns << vpn\n      params = {}\n      params[:addresses] = '10.240.0.1'\n      params[:user] = user.user_login_id\n      params[:challenge] = 'ee85e142eadfec52'\n      params[:response] = '0392a9e43edee3129f735b37fd9d0b0d3f66aa7a00f35440'\n      allow_any_instance_of(User).to receive(:get_user_otp_at).and_return('757364')\n\n      expect(User.ms_chap_auth(params)).to eq('NT_KEY: 57247E8BAD1959F9544B2C5057F77AD8')\n    end\n  end\n\n  describe '.get_user_pass_attributes' do\n    context 'token and email is passed' do\n      it 'should return token and email' do\n        params = { email: Faker::Internet.email, token: SecureRandom.uuid, user: '', password: '' }\n        expect(User.get_user_pass_attributes(params)).to eq([params[:email], params[:token]])\n      end\n    end\n\n    context 'email and password is present, and token is not present' do\n      it 'should return password and email' do\n        params = { email: Faker::Internet.email, token: '', user: '', password: SecureRandom.uuid }\n        expect(User.get_user_pass_attributes(params)).to eq([params[:email], params[:password]])\n      end\n    end\n\n    context 'user and token is present, and email is not present' do\n      it 'should return user and token' do\n        params = { email: '', token: SecureRandom.uuid, user: Faker::Internet.email, password: '' }\n        expect(User.get_user_pass_attributes(params)).to eq([params[:user], params[:token]])\n      end\n    end\n\n    context 'user and password is present and email and token is not present' do\n      it 'should return user and password' do\n        params = { email: '', token: '', user: Faker::Internet.email, password: SecureRandom.uuid }\n        expect(User.get_user_pass_attributes(params)).to eq([params[:user], params[:password]])\n      end\n    end\n\n    context 'email and user is blank or password and token is blank' do\n      it 'should return nil and nil' do\n        params = { email: '', token: '', user: '', password: '' }\n        expect(User.get_user_pass_attributes(params)).to eq([nil, nil])\n      end\n    end\n  end\n\n  describe '.get_user' do\n    after(:each) do\n      User.destroy_all\n    end\n\n    context 'when two users exist with same login_id' do\n      let(:user_login_id) { 'same-id' }\n      subject(:user) { User.get_user(user_login_id) }\n\n      let(:first_user) { build(:user, user_login_id: user_login_id, name: 'Test1', email: \"#{user_login_id}@test.com\") }\n      let(:second_user) { build(:user, user_login_id: user_login_id, name: 'Test2', email: \"#{user_login_id}@aux.test.com\") }\n\n      it 'returns first found active user' do\n        first_user.save\n        second_user.save\n\n        expect(user.name).to eq(first_user.name)\n      end\n\n      it 'returns only active user' do\n        first_user.active = false\n        second_user.active = false\n        first_user.save\n        second_user.save\n\n        expect(user).to be_nil\n      end\n    end\n  end\n\n  describe '.add_user' do\n    it 'creates a new user' do\n      user_data = build(:user)\n      domain = user_data.email.split('@').last\n\n      user = User.add_user(user_data.first_name, user_data.last_name, user_data.user_role, domain)\n\n      expect(user.persisted?).to eq(true)\n    end\n\n    it 'creates a user with email in format first_name.last_name' do\n      user_data = build(:user)\n      domain = user_data.email.split('@').last\n\n      user = User.add_user(user_data.first_name, user_data.last_name, user_data.user_role, domain)\n\n      expect(user.persisted?).to eq(true)\n      expect(user.email).to eq(\"#{user.first_name.downcase}.#{user.last_name.downcase}@#{domain}\")\n    end\n\n    it 'generate uid' do\n      user_data = build(:user)\n      domain = user_data.email.split('@').last\n\n      expect_any_instance_of(User).to receive(:generate_uid)\n\n      User.add_user(user_data.first_name, user_data.last_name, user_data.user_role, domain)\n    end\n\n    it 'generates login id' do\n      user_data = build(:user)\n      domain = user_data.email.split('@').last\n\n      user = User.add_user(user_data.first_name, user_data.last_name, user_data.user_role, domain)\n\n      expect(user.user_login_id).to eq(\"#{user_data.first_name.downcase}.#{user_data.last_name.downcase}\")\n    end\n\n    it 'initializes host groups' do\n      user_data = build(:user)\n      domain = user_data.email.split('@').last\n\n      expect_any_instance_of(User).to receive(:initialise_host_and_group)\n\n      User.add_user(user_data.first_name, user_data.last_name, user_data.user_role, domain)\n    end\n\n    xit 'fails if required fields are not present' do\n      user_data = build(:user)\n      domain = user_data.email.split('@').last\n\n      user = User.add_user('', user_data.last_name, user_data.user_role, domain)\n\n      expect(user.persisted?).to eq(false)\n    end\n\n    xit 'fails if domain doesn\\'t exist in list of domains' do\n      user_data = build(:user)\n\n      user = User.add_user(user_data.first_name, user_data.last_name, user_data.user_role, Faker::Internet.domain_name)\n\n      expect(user.persisted?).to eq(false)\n    end\n\n    it 'fails if email is already taken' do\n      user_data = build(:user)\n      domain = user_data.email.split('@').last\n      user_data.save\n\n      user = User.add_user(user_data.first_name, user_data.last_name, user_data.user_role, domain)\n\n      expect(user.persisted?).to eq(false)\n    end\n  end\n\n  describe '.get_sysadmins' do\n    let(:user) { create(:user) }\n    context 'given user belongs to group name equal to user_login_id' do\n      it 'should return correct amount of sysadmins' do\n        sysadmins = User.get_sysadmins [user.id]\n        expect(sysadmins.size).to eq 1\n      end\n\n      it 'should include intended sysadmin' do\n        sysadmins = User.get_sysadmins [user.id]\n        pw_name_sysadmins = sysadmins.map { |sysadmin| sysadmin[:pw_name] }\n        expect(pw_name_sysadmins).to include user.user_login_id\n      end\n\n      it 'should return correct response without nil' do\n        sysadmins = User.get_sysadmins [user.id]\n        expect(sysadmins.first).to satisfy do |sysadmin|\n          !sysadmin[:pw_name].nil? &&\n            !sysadmin[:pw_passwd].nil? &&\n            !sysadmin[:pw_uid].nil? &&\n            !sysadmin[:pw_gid].nil? &&\n            !sysadmin[:pw_gecos].nil? &&\n            !sysadmin[:pw_dir].nil? &&\n            !sysadmin[:pw_shell].nil?\n        end\n      end\n    end\n\n    context 'given user which does not belongs to group name equal to user_login_id' do\n      before(:each) do\n        GroupAssociation.where(user_id: user.id).delete_all\n      end\n\n      it 'should return correct response with nil pw_gid' do\n        sysadmins = User.get_sysadmins [user.id]\n        expect(sysadmins.first).to satisfy do |sysadmin|\n          !sysadmin[:pw_name].nil? &&\n            !sysadmin[:pw_passwd].nil? &&\n            !sysadmin[:pw_uid].nil? &&\n            sysadmin[:pw_gid].nil? &&\n            !sysadmin[:pw_gecos].nil? &&\n            !sysadmin[:pw_dir].nil? &&\n            !sysadmin[:pw_shell].nil?\n        end\n      end\n    end\n\n    context 'given nonexistant user' do\n      it 'should return zero amount of sysadmins' do\n        sysadmins = User.get_sysadmins [0]\n        expect(sysadmins.size).to eq 0\n      end\n    end\n  end\n\n  describe '.check_email_address' do\n    it 'should check valid email address' do\n      email_address = 'satrya@gmail.com'\n      expect(User.check_email_address(email_address)).to eq(true)\n\n      email_address = 'satraya @gmail.com'\n      expect(User.check_email_address(email_address)).to eq(false)\n\n      email_address = 'satraya@-gmail.com'\n      expect(User.check_email_address(email_address)).to eq(false)\n\n      email_address = 'sat*raya@gmail.com'\n      expect(User.check_email_address(email_address)).to eq(false)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/models/vpn_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe Vpn, type: :model do\n  context \"vpn administration\" do\n    it \"should test user and vpn management\" do\n      vpn = Vpn.create(name: \"X\")\n      group = create(:group)\n      vpn.groups << group\n      vpn.save!\n\n      user = create(:user)\n\n      group.users << user\n      group.save!\n\n      expect(Vpn.administrator? user).to eq false\n\n      group.add_admin user\n\n      expect(Vpn.administrator? user).to eq true\n      expect(Vpn.managed_vpns(user).count).to eq 1\n      vpn = Vpn.create(name: :\"Y\")\n      vpn.groups << group\n      vpn.save!\n      expect(Vpn.managed_vpns(user).count).to eq 2\n      vpn = Vpn.create(name: :\"Z\")\n      expect(Vpn.managed_vpns(user).count).to eq 2\n      vpn = Vpn.create(name: :\"Z1\")\n      group = create(:group)\n      vpn.groups << group\n      vpn.save!\n      expect(Vpn.managed_vpns(user).count).to eq 2\n      group.users << user\n      group.save!\n      group.add_admin user\n      expect(Vpn.managed_vpns(user).count).to eq 3\n    end\n\n    it \"should list all vpns for given user\" do\n\n\n      vpn = Vpn.create(name: :\"X\")\n      group = create(:group)\n      vpn.groups << group\n      vpn.save!\n\n      user = create(:user)\n\n      if !group.member? user\n        group.users << user \n        group.save!\n      end\n\n      expect(Vpn.user_vpns(user).count).to eq 1\n      vpn = Vpn.create(name: :\"Y\")\n      vpn.groups << group\n      vpn.save!\n      expect(Vpn.user_vpns(user).count).to eq 2\n      vpn = Vpn.create(name: :\"Z\")\n      expect(Vpn.user_vpns(user).count).to eq 2\n      vpn = Vpn.create(name: :\"Z1\")\n      group = create(:group)\n      group.users << user\n      vpn.groups << group\n      vpn.save!\n      expect(Vpn.user_vpns(user).count).to eq 3\n\n\n    end\n  end\nend\n\n"
  },
  {
    "path": "spec/rails_helper.rb",
    "content": "# This file is copied to spec/ when you run 'rails generate rspec:install'\nENV['RAILS_ENV'] ||= 'test'\nrequire File.expand_path('../config/environment', __dir__)\n# Prevent database truncation if the environment is production\nabort('The Rails environment is running in production mode!') if Rails.env.production?\nrequire 'spec_helper'\nrequire 'rspec/rails'\n# Add additional requires below this line. Rails is not loaded until this point!\nrequire 'webmock/rspec'\nrequire 'database_cleaner'\n#require 'coveralls'\nrequire 'simplecov'\nrequire 'simplecov-console'\nrequire 'pry'\nrequire 'capybara/rspec'\nrequire 'pry'\nrequire 'redis'\nrequire 'mock_redis'\nDir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }\n\n# Requires supporting ruby files with custom matchers and macros, etc, in\n# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are\n# run as spec files by default. This means that files in spec/support that end\n# in _spec.rb will both be required and run as specs, causing the specs to be\n# run twice. It is recommended that you do not name files matching this glob to\n# end with _spec.rb. You can configure this pattern with the --pattern\n# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.\n#\n# The following line is provided for convenience purposes. It has the downside\n# of increasing the boot-up time by auto-requiring all files in the support\n# directory. Alternatively, in the individual `*_spec.rb` files, manually\n# require only the support files necessary.\n#\n# Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }\n\n# Checks for pending migration and applies them before tests are run.\n# If you are not using ActiveRecord, you can remove this line.\nActiveRecord::Migration.maintain_test_schema!\n\nRSpec.configure do |config|\n  config.include Devise::Test::ControllerHelpers, type: :controller\n  config.include Devise::Test::ControllerHelpers, type: :view\n  config.include Devise::Test::IntegrationHelpers, type: :feature\n  config.include FactoryBot::Syntax::Methods\n  #config.include CertificateHelper\n  #config.include Warden::Test::Helpers\n  config.render_views = true\n  config.fixture_path = \"#{::Rails.root}/spec/fixtures\"\n  config.use_transactional_fixtures = true\n  # Filter lines from Rails gems in backtraces.\n  #config.filter_rails_from_backtrace!\n  # arbitrary gems may also be filtered via:\n  # config.filter_gems_from_backtrace(\"gem name\")\n  # rspec-expectations config goes here. You can use an alternate\n  # assertion/expectation library such as wrong or the stdlib/minitest\n  # assertions if you prefer.\n  config.expect_with :rspec do |expectations|\n    # This option will default to `true` in RSpec 4. It makes the `description`\n    # and `failure_message` of custom matchers include text for helper methods\n    # defined using `chain`, e.g.:\n    #     be_bigger_than(2).and_smaller_than(4).description\n    #     # => \"be bigger than 2 and smaller than 4\"\n    # ...rather than:\n    #     # => \"be bigger than 2\"\n    expectations.include_chain_clauses_in_custom_matcher_descriptions = true\n  end\n  # rspec-mocks config goes here. You can use an alternate test double\n  # library (such as bogus or mocha) by changing the `mock_with` option here.\n  config.mock_with :rspec do |mocks|\n    # Prevents you from mocking or stubbing a method that does not exist on\n    # a real object. This is generally recommended, and will default to\n    # `true` in RSpec 4.\n    mocks.verify_partial_doubles = true\n  end\n  config.before(:each) do\n    mock_redis = MockRedis.new\n    allow(Redis).to receive(:new).and_return(mock_redis)\n  end\n  config.before(:suite) do\n    DatabaseCleaner.clean_with(:truncation)\n  end\n  config.before(:each) do\n    DatabaseCleaner.strategy = :transaction\n  end\n  config.before(:each, js: true) do\n    DatabaseCleaner.strategy = :truncation\n  end\n  config.before(:each) do\n    DatabaseCleaner.start\n  end\n  config.after(:each) do\n    DatabaseCleaner.clean\n  end\n  #Coveralls.wear!\n  SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([SimpleCov::Formatter::Console])\n  SimpleCov.start\nend\n\nShoulda::Matchers.configure do |config|\n  config.integrate do |with|\n    with.test_framework :rspec\n    with.library :rails\n  end\nend\n"
  },
  {
    "path": "spec/routing/api_resources_routing_spec.rb",
    "content": "require \"rails_helper\"\n\nRSpec.describe ApiResourcesController, type: :routing do\n  describe \"routing\" do\n\n    it \"routes to #index\" do\n      expect(:get => \"/api_resources\").to route_to(\"api_resources#index\")\n    end\n\n    it \"routes to #new\" do\n      expect(:get => \"/api_resources/new\").to route_to(\"api_resources#new\")\n    end\n\n    it \"routes to #show\" do\n      expect(:get => \"/api_resources/1\").to route_to(\"api_resources#show\", :id => \"1\")\n    end\n\n    it \"routes to #edit\" do\n      expect(:get => \"/api_resources/1/edit\").to route_to(\"api_resources#edit\", :id => \"1\")\n    end\n\n    it \"routes to #create\" do\n      expect(:post => \"/api_resources\").to route_to(\"api_resources#create\")\n    end\n\n    it \"routes to #update via PUT\" do\n      expect(:put => \"/api_resources/1\").to route_to(\"api_resources#update\", :id => \"1\")\n    end\n\n    it \"routes to #update via PATCH\" do\n      expect(:patch => \"/api_resources/1\").to route_to(\"api_resources#update\", :id => \"1\")\n    end\n\n    it \"routes to #destroy\" do\n      expect(:delete => \"/api_resources/1\").to route_to(\"api_resources#destroy\", :id => \"1\")\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/spec_helper.rb",
    "content": "# This file was generated by the `rails generate rspec:install` command.\n# Conventionally, all specs live under a `spec` directory, which RSpec adds to\n# the `$LOAD_PATH`.\n# The generated `.rspec` file contains `--require spec_helper` which will cause\n# this file to always be loaded, without a need to explicitly require it in any\n# files.\n#\n# Given that it is always loaded, you are encouraged to keep this file as\n# light-weight as possible. Requiring heavyweight dependencies from this file\n# will add to the boot time of your test suite on EVERY test run, even for an\n# individual file that may not need all of that loaded. Instead, consider making\n# a separate helper file that requires the additional dependencies and performs\n# the additional setup, and require it from the spec files that actually need\n# it.\n#\n# The `.rspec` file also contains a few flags that are not defaults but that\n# users commonly want.\n#\n# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration\nRSpec.configure do |config|\n  # rspec-expectations config goes here. You can use an alternate\n  # assertion/expectation library such as wrong or the stdlib/minitest\n  # assertions if you prefer.\n  config.expect_with :rspec do |expectations|\n    # This option will default to `true` in RSpec 4. It makes the `description`\n    # and `failure_message` of custom matchers include text for helper methods\n    # defined using `chain`, e.g.:\n    #     be_bigger_than(2).and_smaller_than(4).description\n    #     # => \"be bigger than 2 and smaller than 4\"\n    # ...rather than:\n    #     # => \"be bigger than 2\"\n    expectations.include_chain_clauses_in_custom_matcher_descriptions = true\n  end\n  # rspec-mocks config goes here. You can use an alternate test double\n  # library (such as bogus or mocha) by changing the `mock_with` option here.\n  config.mock_with :rspec do |mocks|\n    # Prevents you from mocking or stubbing a method that does not exist on\n    # a real object. This is generally recommended, and will default to\n    # `true` in RSpec 4.\n    mocks.verify_partial_doubles = true\n  end\nend\n"
  },
  {
    "path": "spec/support/helpers/x509_certificate_helper.rb",
    "content": "class CertificateHelper\n  def initialize\n    @key = OpenSSL::PKey::RSA.new(1024)\n    @public_key = @key.public_key\n\n    subject = \"/C=BE/O=Test/OU=Test/CN=Test\"\n\n    @cert = OpenSSL::X509::Certificate.new\n    @cert.subject = @cert.issuer = OpenSSL::X509::Name.parse(subject)\n    @cert.not_before = Time.now\n    @cert.not_after = Time.now + 1 * 24 * 60 * 60\n    @cert.public_key = @public_key\n    @cert.serial = 0x0\n    @cert.version = 2\n\n    @cert.sign @key, OpenSSL::Digest::SHA256.new\n  end\n\n  def get_cert\n    @cert.to_pem\n  end\n\n  def get_private_key\n    @key.to_s\n  end\n\n  def get_fingerprint\n    OpenSSL::Digest::SHA256.new(@cert.to_der).to_s\n  end\n\nend\n\n"
  },
  {
    "path": "spec/views/api_resources/edit.html.slim_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe \"api_resources/edit\", type: :view do\n  before(:each) do\n    @api_resource = assign(:api_resource, ApiResource.create!(\n      :name => \"MyString\",\n      :description => \"MyString\",\n      :access_key => \"MyString\"\n    ))\n  end\n\n  it \"renders the edit api_resource form\" do\n    render\n\n    assert_select \"form[action=?][method=?]\", api_resource_path(@api_resource), \"post\" do\n\n      assert_select \"input#api_resource_name[name=?]\", \"api_resource[name]\"\n\n      assert_select \"input#api_resource_description[name=?]\", \"api_resource[description]\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/views/api_resources/index.html.slim_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe \"api_resources/index\", type: :view do\n  before(:each) do\n    assign(:api_resources, [\n      ApiResource.create!(\n        :name => \"Name\",\n        :description => \"Description\",\n        :access_key => \"Access Key\"\n      ),\n      ApiResource.create!(\n        :name => \"Name2\",\n        :description => \"Description\",\n        :access_key => \"Access Key\"\n      )\n    ])\n  end\n\n  it \"renders a list of api_resources\" do\n    render\n    assert_select \"tr>td\", :text => \"Name\".to_s, :count => 1\n    assert_select \"tr>td\", :text => \"Description\".to_s, :count => 2\n    assert_select \"tr>td\", :text => \"Regenerate\".to_s, :count => 2\n  end\nend\n"
  },
  {
    "path": "spec/views/api_resources/new.html.slim_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe \"api_resources/new\", type: :view do\n  before(:each) do\n    assign(:api_resource, ApiResource.new(\n      :name => \"MyString\",\n      :description => \"MyString\",\n      :access_key => \"MyString\"\n    ))\n  end\n\n  it \"renders new api_resource form\" do\n    render\n\n    assert_select \"form[action=?][method=?]\", api_resources_path, \"post\" do\n\n      assert_select \"input#api_resource_name[name=?]\", \"api_resource[name]\"\n\n      assert_select \"input#api_resource_description[name=?]\", \"api_resource[description]\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/views/api_resources/show.html.slim_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe \"api_resources/show\", type: :view do\n  before(:each) do\n    @api_resource = assign(:api_resource, ApiResource.create!(\n      :name => \"Name\",\n      :description => \"Description\",\n      :access_key => \"Access Key\"\n    ))\n  end\n\n  it \"renders attributes in <p>\" do\n    render\n    expect(rendered).to match(/Name/)\n    expect(rendered).to match(/Description/)\n    expect(rendered).to match(/Access Key/)\n  end\nend\n"
  },
  {
    "path": "spec/views/groups/show.html.slim_spec.rb",
    "content": "RSpec.describe 'groups/show', type: :view do\n  let(:admin) { create(:admin_user) }\n  let(:group) { create(:group) }\n\n  context 'authorized as admin' do\n    it 'should renders form to add user to group' do\n      sign_in admin\n      assign(:group, group)\n      assign(:group_users, [])\n  \n      render\n  \n      assert_select 'form[action=?][method=?]', add_user_to_group_path(group.id), 'post' do\n        assert_select 'input#add_user_user_id[name=user_id]'\n        assert_select 'input#expiration_date[name=expiration_date][type=date]'\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/views/layouts/home.html.slim_spec.rb",
    "content": "require 'rails_helper'\n\nRSpec.describe 'layouts/home', type: :view do\n  before(:each) do\n    @cached_sign_in_type = ENV['SIGN_IN_TYPE']\n  end\n\n  after(:each) do\n    ENV['SIGN_IN_TYPE'] = @cached_sign_in_type\n  end\n\n  it 'should renders google auth link when use default sign in type' do\n    ENV['SIGN_IN_TYPE'] = ''\n\n    render\n\n    assert_select \"a[href$='#{user_google_oauth2_omniauth_authorize_path}']\"\n  end\n\n  it 'should not renders google auth link when sign in type form' do\n    ENV['SIGN_IN_TYPE'] = 'form'\n\n    render\n\n    assert_select \"a[href$='#{user_google_oauth2_omniauth_authorize_path}']\", 0\n  end\n\n  it 'renders sign in form' do\n    ENV['SIGN_IN_TYPE'] = 'form'\n\n    render\n\n    assert_select 'form[action=?][method=?]', user_sign_in_path, 'post' do\n      assert_select 'input#name[name=name]'\n      assert_select 'input#email[name=email]'\n    end\n  end\n\n  it 'should not renders sign in form when sign in type is not form' do\n    ENV['SIGN_IN_TYPE'] = 'not_form'\n\n    render\n\n    assert_select 'form[action=?][method=?]', user_sign_in_path, 'post', 0\n  end\nend\n"
  }
]