Full Code of gate-sso/gate for AI

master 958d5b920b07 cached
370 files
479.5 KB
139.5k tokens
488 symbols
1 requests
Download .txt
Showing preview only (563K chars total). Download the full file or copy to clipboard to get everything.
Repository: gate-sso/gate
Branch: master
Commit: 958d5b920b07
Files: 370
Total size: 479.5 KB

Directory structure:
gitextract_ouflf_22/

├── .deepsource.toml
├── .dockerignore
├── .gitignore
├── .rspec
├── .rubocop.yml
├── .ruby-version
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── VERSION
├── api_blueprint/
│   ├── bin/
│   │   └── dredd_server.sh
│   ├── group.apib
│   ├── hooks/
│   │   └── dredd_hooks.rb
│   ├── user.apib
│   └── vpns.apib
├── app/
│   ├── assets/
│   │   ├── config/
│   │   │   └── manifest.js
│   │   ├── images/
│   │   │   └── .keep
│   │   ├── javascripts/
│   │   │   ├── admin.coffee
│   │   │   ├── api_resources.coffee
│   │   │   ├── application.js
│   │   │   ├── bootstrap.js.coffee
│   │   │   ├── group.coffee
│   │   │   ├── groups.coffee
│   │   │   ├── home.coffee
│   │   │   ├── host_access_groups.coffee
│   │   │   ├── host_machine_groups.coffee
│   │   │   ├── host_machines.coffee
│   │   │   ├── nss.coffee
│   │   │   ├── omniauth_callbacks.coffee
│   │   │   ├── profile.coffee
│   │   │   ├── users.coffee
│   │   │   ├── utilities.coffee
│   │   │   └── viewport.js
│   │   └── stylesheets/
│   │       ├── application.scss
│   │       ├── bootstrap-social.scss
│   │       ├── general.css
│   │       ├── home.scss.erb
│   │       ├── profile.css
│   │       └── scaffolds.scss
│   ├── clients/
│   │   └── data_dog_client.rb
│   ├── controllers/
│   │   ├── admin_controller.rb
│   │   ├── api/
│   │   │   └── v1/
│   │   │       ├── base_controller.rb
│   │   │       ├── endpoints_controller.rb
│   │   │       ├── groups_controller.rb
│   │   │       ├── users_controller.rb
│   │   │       └── vpns_controller.rb
│   │   ├── api_resources_controller.rb
│   │   ├── application_controller.rb
│   │   ├── concerns/
│   │   │   └── .keep
│   │   ├── groups_controller.rb
│   │   ├── home_controller.rb
│   │   ├── host_controller.rb
│   │   ├── host_machine_groups_controller.rb
│   │   ├── host_machines_controller.rb
│   │   ├── nss_controller.rb
│   │   ├── organisations_controller.rb
│   │   ├── pings_controller.rb
│   │   ├── profile_controller.rb
│   │   ├── saml_idp_controller.rb
│   │   ├── users/
│   │   │   ├── auth_controller.rb
│   │   │   └── omniauth_callbacks_controller.rb
│   │   ├── users_controller.rb
│   │   ├── vpn_domain_name_servers_controller.rb
│   │   └── vpns_controller.rb
│   ├── helpers/
│   │   ├── admin_helper.rb
│   │   ├── api_resources_helper.rb
│   │   ├── application_helper.rb
│   │   ├── group_helper.rb
│   │   ├── groups_helper.rb
│   │   ├── home_helper.rb
│   │   ├── host_access_groups_helper.rb
│   │   ├── host_machine_groups_helper.rb
│   │   ├── host_machines_helper.rb
│   │   ├── nss_helper.rb
│   │   ├── omniauth_callbacks_helper.rb
│   │   ├── profile_helper.rb
│   │   └── users_helper.rb
│   ├── lib/
│   │   ├── datadog.rb
│   │   └── saml_app.rb
│   ├── mailers/
│   │   └── .keep
│   ├── models/
│   │   ├── .keep
│   │   ├── access_token.rb
│   │   ├── api_resource.rb
│   │   ├── application_record.rb
│   │   ├── concerns/
│   │   │   ├── .keep
│   │   │   └── ms_chap_auth.rb
│   │   ├── endpoint.rb
│   │   ├── group.rb
│   │   ├── group_admin.rb
│   │   ├── group_association.rb
│   │   ├── group_endpoint.rb
│   │   ├── host.rb
│   │   ├── host_access_group.rb
│   │   ├── host_machine.rb
│   │   ├── ip_address.rb
│   │   ├── organisation.rb
│   │   ├── saml_app_config.rb
│   │   ├── user.rb
│   │   ├── vpn.rb
│   │   ├── vpn_domain_name_server.rb
│   │   ├── vpn_group_association.rb
│   │   ├── vpn_group_user_association.rb
│   │   ├── vpn_search_domain.rb
│   │   └── vpn_supplemental_match_domain.rb
│   ├── validators/
│   │   └── email_validator.rb
│   └── views/
│       ├── admin/
│       │   └── index.html.slim
│       ├── api/
│       │   └── v1/
│       │       └── users/
│       │           └── show.json.jbuilder
│       ├── api_resources/
│       │   ├── _api_resource.json.jbuilder
│       │   ├── _form.html.slim
│       │   ├── edit.html.slim
│       │   ├── index.html.slim
│       │   ├── index.json.jbuilder
│       │   ├── new.html.slim
│       │   ├── show.html.slim
│       │   └── show.json.jbuilder
│       ├── application/
│       │   ├── _admin.html.slim
│       │   ├── _groups_header.html.slim
│       │   └── _host_header.html.slim
│       ├── common/
│       │   └── errors.json.jbuilder
│       ├── groups/
│       │   ├── _form.html.slim
│       │   ├── index.html.slim
│       │   ├── new.html.slim
│       │   └── show.html.slim
│       ├── home/
│       │   └── index.html.slim
│       ├── host_machines/
│       │   ├── index.html.slim
│       │   ├── new.html.slim
│       │   └── show.html.slim
│       ├── layouts/
│       │   ├── application.html.slim
│       │   ├── home.html.slim
│       │   └── profile.html.slim.disabled
│       ├── nss/
│       │   └── add_host.json.jbuilder
│       ├── organisations/
│       │   ├── _form.html.slim
│       │   ├── config_saml_app.html.slim
│       │   ├── index.html.slim
│       │   ├── new.html.slim
│       │   ├── saml_apps/
│       │   │   └── _datadog.html.erb
│       │   └── show.html.slim
│       ├── profile/
│       │   ├── _group_search.html.slim
│       │   ├── _user_search.html.slim
│       │   ├── group_admin.html.slim
│       │   ├── list.html.slim
│       │   ├── public_key.html.slim
│       │   ├── show.html.slim
│       │   ├── user.html.slim
│       │   └── user_admin.html.slim
│       ├── saml_idp/
│       │   └── idp/
│       │       └── new.html.erb
│       ├── users/
│       │   ├── _search.html.slim
│       │   ├── index.html.slim
│       │   ├── new.html.erb
│       │   └── show.html.slim
│       └── vpns/
│           ├── _form.html.slim
│           ├── edit.html.slim
│           ├── index.html.slim
│           ├── new.html.slim
│           └── show.html.slim
├── bin/
│   ├── bundle
│   ├── rails
│   ├── rake
│   ├── setup
│   └── update
├── config/
│   ├── application.rb
│   ├── boot.rb
│   ├── cable.yml
│   ├── database.yml
│   ├── environment.rb
│   ├── environments/
│   │   ├── development.rb
│   │   ├── integration.rb
│   │   ├── production.rb
│   │   └── test.rb
│   ├── initializers/
│   │   ├── application_controller_renderer.rb
│   │   ├── assets.rb
│   │   ├── backtrace_silencers.rb
│   │   ├── content_security_policy.rb
│   │   ├── cookies_serializer.rb
│   │   ├── devise.rb
│   │   ├── dotenv.rb
│   │   ├── filter_parameter_logging.rb
│   │   ├── inflections.rb
│   │   ├── mime_types.rb
│   │   ├── new_framework_defaults.rb
│   │   ├── new_framework_defaults_7_0.rb
│   │   ├── permissions_policy.rb
│   │   ├── session_store.rb
│   │   └── wrap_parameters.rb
│   ├── locales/
│   │   ├── devise.en.yml
│   │   ├── en.bootstrap.yml
│   │   └── en.yml
│   ├── newrelic.yml
│   ├── puma.rb
│   ├── redis.yml
│   ├── routes.rb
│   ├── schedule.rb
│   ├── secrets.yml
│   ├── spring.rb
│   └── storage.yml
├── config.ru
├── db/
│   ├── migrate/
│   │   ├── 20160419122430_devise_create_users.rb
│   │   ├── 20160419132647_add_provider_to_users.rb
│   │   ├── 20160419144739_add_name_to_users.rb
│   │   ├── 20160427123146_add_auth_key_to_user.rb
│   │   ├── 20160427123233_add_provisioning_uri_to_user.rb
│   │   ├── 20160519042340_add_active_to_users.rb
│   │   ├── 20160519064340_add_default_value_to_users.rb
│   │   ├── 20160615044834_create_hosts.rb
│   │   ├── 20160615045052_add_admin_to_user.rb
│   │   ├── 20160615112805_add_user_to_host.rb
│   │   ├── 20160628140022_add_deleted_at_to_host.rb
│   │   ├── 20160628140440_add_deleted_by_to_host.rb
│   │   ├── 20160629043358_add_homedir_to_user.rb
│   │   ├── 20160629043415_add_shell_to_user.rb
│   │   ├── 20160629075435_create_groups.rb
│   │   ├── 20160701090045_create_group_associations.rb
│   │   ├── 20160701112600_add_deleted_properties_to_group.rb
│   │   ├── 20160707115313_create_access_tokens.rb
│   │   ├── 20160714115228_add_public_key_to_user.rb
│   │   ├── 20160908081651_create_host_machines.rb
│   │   ├── 20161003145832_create_host_access_groups.rb
│   │   ├── 20170803140620_add_user_login_id_to_user.rb
│   │   ├── 20171013115441_create_versions.rb
│   │   ├── 20171016064705_remove_index_group_name.rb
│   │   ├── 20171016071526_add_unique_index_on_groups_name.rb
│   │   ├── 20171031060034_create_group_admin.rb
│   │   ├── 20171031060217_add_foreign_key_ref_on_group_admin.rb
│   │   ├── 20171031100758_create_vpns.rb
│   │   ├── 20171031101026_create_vpn_group_association.rb
│   │   ├── 20171031103518_add_foreign_key_ref_on_vpn_group_association.rb
│   │   ├── 20171031113123_create_vpn_group_user_association.rb
│   │   ├── 20171031121702_add_foreign_key_ref_on_vpn_group_user_association.rb
│   │   ├── 20171102071909_add_ip_address_to_vpns.rb
│   │   ├── 20171107114249_remove_url_from_vpns.rb
│   │   ├── 20171108130234_add_user_id_to_access_token.rb
│   │   ├── 20171108130353_add_foreign_key_ref_on_access_tokens.rb
│   │   ├── 20171124090240_add_uuid_to_vpns.rb
│   │   ├── 20171124114427_create_vpn_domain_name_servers.rb
│   │   ├── 20171124114830_create_vpn_search_domains.rb
│   │   ├── 20171124115925_create_vpn_supplemental_match_domains.rb
│   │   ├── 20180104081814_add_product_name_to_users.rb
│   │   ├── 20180202102206_create_saml_service_providers.rb
│   │   ├── 20180214050204_add_api_key_to_host_machines.rb
│   │   ├── 20180214052451_create_ip_addresses.rb
│   │   ├── 20180214052644_add_host_machine_to_ip_address.rb
│   │   ├── 20180219150818_add_description_to_group.rb
│   │   ├── 20180222135930_add_access_key_to_host_machine.rb
│   │   ├── 20180222140000_add_access_key_to_user.rb
│   │   ├── 20180227051732_create_api_resources.rb
│   │   ├── 20180301010021_add_user_to_api_resources.rb
│   │   ├── 20180301010035_add_group_to_api_resources.rb
│   │   ├── 20180306231200_add_deactivated_at_to_users.rb
│   │   ├── 20180311082600_rename_access_key_in_api_resources.rb
│   │   ├── 20180311161200_rename_token_in_access_tokens.rb
│   │   ├── 20180318083000_create_indexes_to_speedup_nss_controller.rb
│   │   ├── 20180613074108_create_organisations.rb
│   │   ├── 20180613165050_drop_saml_service_providers.rb
│   │   ├── 20180723175600_update_organisations_for_saml.rb
│   │   ├── 20181002023107_add_default_admins_to_host_machines.rb
│   │   ├── 20181016093315_create_saml_app_configs.rb
│   │   ├── 20181208184236_add_fields_to_user.rb
│   │   ├── 20190624024930_add_expiration_date_to_group_associations.rb
│   │   ├── 20190820070910_create_endpoints.rb
│   │   ├── 20190820075040_create_group_endpoints.rb
│   │   ├── 20190820080624_add_foreign_key_ref_on_group_endpoints.rb
│   │   ├── 20200113065717_add_sessions_table.rb
│   │   ├── 20220926001848_add_service_name_to_active_storage_blobs.active_storage.rb
│   │   ├── 20220926001849_create_active_storage_variant_records.active_storage.rb
│   │   └── 20220926001850_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
│   ├── schema.rb
│   ├── seeds/
│   │   ├── development.rb
│   │   ├── production.rb
│   │   └── test.rb
│   └── seeds.rb
├── docker-compose.yaml
├── docs/
│   ├── additional_setup.md
│   ├── administration.md
│   ├── dredd_setup.md
│   ├── newrelic.md
│   └── oauth_setup.md
├── dredd.yml
├── lib/
│   ├── assets/
│   │   └── .keep
│   ├── tasks/
│   │   ├── .keep
│   │   ├── app.rake
│   │   ├── setup.rake
│   │   ├── users.rake
│   │   └── vpn.rake
│   └── vpn/
│       ├── mobileconfig.erb
│       ├── mobileconfig.rb
│       └── namespace.rb
├── public/
│   ├── 404.html
│   ├── 422.html
│   ├── 500.html
│   └── robots.txt
├── scripts/
│   ├── gen-client-conf
│   └── gen-client-keys
├── setup.sh
└── spec/
    ├── clients/
    │   └── data_dog_client_spec.rb
    ├── controllers/
    │   ├── admin_controller_spec.rb
    │   ├── api/
    │   │   └── v1/
    │   │       ├── api_controller_spec.rb
    │   │       ├── endpoints_controller_spec.rb
    │   │       ├── groups_controller_spec.rb
    │   │       ├── users_controller_spec.rb
    │   │       └── vpns_controller_spec.rb
    │   ├── api_resources_controller_spec.rb
    │   ├── groups_controller_spec.rb
    │   ├── home_controller_spec.rb
    │   ├── host_machine_groups_controller_spec.rb
    │   ├── host_machines_controller_spec.rb
    │   ├── nss_controller_spec.rb
    │   ├── omniauth_callbacks_controller_spec.rb
    │   ├── organisations_controller_spec.rb
    │   ├── profile_controller_spec.rb
    │   ├── users/
    │   │   └── auth_controller_spec.rb
    │   ├── users_controller_spec.rb
    │   └── vpns_controller_spec.rb
    ├── factories/
    │   ├── access_tokens.rb
    │   ├── api_resources.rb
    │   ├── endpoints.rb
    │   ├── group_associations.rb
    │   ├── groups.rb
    │   ├── host_access_groups.rb
    │   ├── host_machine_groups.rb
    │   ├── host_machines.rb
    │   ├── ip_addresses.rb
    │   ├── organisations.rb
    │   ├── saml_app_configs.rb
    │   ├── user_host_access_groups.rb
    │   ├── users.rb
    │   └── vpns.rb
    ├── features/
    │   ├── layout_spec.rb
    │   ├── organisations/
    │   │   ├── add_user_saml_app_spec.rb
    │   │   ├── config_saml_app_spec.rb
    │   │   ├── create_spec.rb
    │   │   ├── list_spec.rb
    │   │   ├── list_user_saml_app_spec.rb
    │   │   ├── remove_user_saml_app_spec.rb
    │   │   ├── save_config_saml_app_spec.rb
    │   │   ├── setup_saml_spec.rb
    │   │   └── update_spec.rb
    │   ├── saml/
    │   │   └── show_spec.rb
    │   └── users/
    │       ├── create_user_spec.rb
    │       └── regenerate_auth_spec.rb
    ├── helpers/
    │   └── application_helper_spec.rb
    ├── lib/
    │   ├── datadog_spec.rb
    │   ├── saml_app_spec.rb
    │   └── tasks/
    │       └── users_rake_spec.rb
    ├── models/
    │   ├── access_token_spec.rb
    │   ├── api_resource_spec.rb
    │   ├── endpoint_spec.rb
    │   ├── group_association_spec.rb
    │   ├── group_endpoint_spec.rb
    │   ├── group_spec.rb
    │   ├── host_machine_spec.rb
    │   ├── host_spec.rb
    │   ├── ip_address_spec.rb
    │   ├── organisation_spec.rb
    │   ├── user_spec.rb
    │   └── vpn_spec.rb
    ├── rails_helper.rb
    ├── routing/
    │   └── api_resources_routing_spec.rb
    ├── spec_helper.rb
    ├── support/
    │   └── helpers/
    │       └── x509_certificate_helper.rb
    └── views/
        ├── api_resources/
        │   ├── edit.html.slim_spec.rb
        │   ├── index.html.slim_spec.rb
        │   ├── new.html.slim_spec.rb
        │   └── show.html.slim_spec.rb
        ├── groups/
        │   └── show.html.slim_spec.rb
        └── layouts/
            └── home.html.slim_spec.rb

================================================
FILE CONTENTS
================================================

================================================
FILE: .deepsource.toml
================================================
version = 1

[[analyzers]]
name = "ruby"
enabled = true

[[analyzers]]
name = "javascript"
enabled = true


================================================
FILE: .dockerignore
================================================
/log/*
!/log/.keep
/tmp
.idea
.env
*.swp
config/application.yml
dump.rdb
coverage
.git


================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
#   git config --global core.excludesfile '~/.gitignore_global'

# Ignore bundler config.
/.bundle
.local
vendor/*

# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal

# Ignore all logfiles and tempfiles.
/log/*
!/log/.keep
/tmp
.idea
.env
.ruby-gemset
*.swp
config/application.yml
public/assets/*
dump.rdb
coverage
data/


================================================
FILE: .rspec
================================================
--color
--format documentation
--require spec_helper


================================================
FILE: .rubocop.yml
================================================
---
require: 
  - rubocop-rails
  - rubocop-performance
  - rubocop-faker

AllCops:
  NewCops: enable
  Exclude:
  - config/initializers/*.rb
  - config/environments/*.rb
  - db/*.rb
  - app/workers/*.rb
  - app/services/**/*.rb
  - spec/services/*.rb
  - bin/*
  - Rakefile
  - lib/tasks/*.rake
  - spec/workers/*.rb
  - chef-repo/**/*
Naming/AccessorMethodName:
  Description: Check the naming of accessor methods for get_/set_.
  Enabled: false
Style/Alias:
  Description: Use alias_method instead of alias.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#alias-method
  Enabled: false
Style/ArrayJoin:
  Description: Use Array#join instead of Array#*.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#array-join
  Enabled: false
Style/AsciiComments:
  Description: Use only ascii symbols in comments.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#english-comments
  Enabled: false
Naming/AsciiIdentifiers:
  Description: Use only ascii symbols in identifiers.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#english-identifiers
  Enabled: false
Style/Attr:
  Description: Checks for uses of Module#attr.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#attr
  Enabled: false
Metrics/BlockNesting:
  Description: Avoid excessive block nesting
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count
  Enabled: false
Style/CaseEquality:
  Description: Avoid explicit use of the case equality operator(===).
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-case-equality
  Enabled: false
Style/CharacterLiteral:
  Description: Checks for uses of character literals.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-character-literals
  Enabled: false
Style/ClassAndModuleChildren:
  Description: Checks style of children classes and modules.
  Enabled: true
  EnforcedStyle: compact
  Exclude:
  - config/application.rb
Metrics/ClassLength:
  Description: Avoid classes longer than 100 lines of code.
  Enabled: false
Metrics/ModuleLength:
  Description: Avoid modules longer than 100 lines of code.
  Enabled: false
Style/ClassVars:
  Description: Avoid the use of class variables.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-class-vars
  Enabled: false
Style/CollectionMethods:
  Enabled: true
  PreferredMethods:
    find: detect
    inject: reduce
    collect: map
    find_all: select
Style/ColonMethodCall:
  Description: 'Do not use :: for method call.'
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#double-colons
  Enabled: false
Style/CommentAnnotation:
  Description: Checks formatting of special comments (TODO, FIXME, OPTIMIZE, HACK,
    REVIEW).
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#annotate-keywords
  Enabled: false
Metrics/AbcSize:
  Description: A calculated magnitude based on number of assignments, branches, and
    conditions.
  Enabled: false
Metrics/BlockLength:
  CountComments: true
  Max: 25
  AllowedMethods: []
  Exclude:
  - spec/**/*
  - config/environments/*.rb
Metrics/CyclomaticComplexity:
  Description: A complexity metric that is strongly correlated to the number of test
    cases needed to validate a method.
  Enabled: false
Rails/Delegate:
  Description: Prefer delegate method for delegations.
  Enabled: false
Style/PreferredHashMethods:
  Description: Checks use of `has_key?` and `has_value?` Hash methods.
  StyleGuide: "#hash-key"
  Enabled: false
Style/Documentation:
  Description: Document classes and non-namespace modules.
  Enabled: false
Style/DoubleNegation:
  Description: Checks for uses of double negation (!!).
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-bang-bang
  Enabled: false
Style/EachWithObject:
  Description: Prefer `each_with_object` over `inject` or `reduce`.
  Enabled: false
Style/EmptyLiteral:
  Description: Prefer literals to Array.new/Hash.new/String.new.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#literal-array-hash
  Enabled: false
Style/Encoding:
  Enabled: false
Style/EvenOdd:
  Description: Favor the use of Fixnum#even? && Fixnum#odd?
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#predicate-methods
  Enabled: false
Naming/FileName:
  Description: Use snake_case for source file names.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-files
  Enabled: false
Style/FrozenStringLiteralComment:
  Description: Add the frozen_string_literal comment to the top of files to help transition
    from Ruby 2.3.0 to Ruby 3.0.
  Enabled: false
Style/FormatString:
  Description: Enforce the use of Kernel#sprintf, Kernel#format or String#%.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#sprintf
  Enabled: false
Style/GlobalVars:
  Description: Do not introduce global variables.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#instance-vars
  Reference: http://www.zenspider.com/Languages/Ruby/QuickRef.html
  Enabled: false
Style/GuardClause:
  Description: Check for conditionals that can be replaced with guard clauses
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals
  Enabled: false
Style/IfUnlessModifier:
  Description: Favor modifier if/unless usage when you have a single-line body.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier
  Enabled: false
Style/IfWithSemicolon:
  Description: Do not use if x; .... Use the ternary operator instead.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs
  Enabled: false
Style/InlineComment:
  Description: Avoid inline comments.
  Enabled: false
Style/Lambda:
  Description: Use the new lambda literal syntax for single-line blocks.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#lambda-multi-line
  Enabled: false
Style/LambdaCall:
  Description: Use lambda.call(...) instead of lambda.(...).
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#proc-call
  Enabled: false
Style/LineEndConcatenation:
  Description: Use \ instead of + or << to concatenate two string literals at line
    end.
  Enabled: false
Metrics/MethodLength:
  Description: Avoid methods longer than 10 lines of code.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#short-methods
  Enabled: false
Style/ModuleFunction:
  Description: Checks for usage of `extend self` in modules.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#module-function
  Enabled: false
Style/MultilineBlockChain:
  Description: Avoid multi-line chains of blocks.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks
  Enabled: false
Style/NegatedIf:
  Description: Favor unless over if for negative conditions (or control flow or).
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#unless-for-negatives
  Enabled: false
Style/NegatedWhile:
  Description: Favor until over while for negative conditions.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#until-for-negatives
  Enabled: false
Style/Next:
  Description: Use `next` to skip iteration instead of a condition at the end.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals
  Enabled: false
Style/NilComparison:
  Description: Prefer x.nil? to x == nil.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#predicate-methods
  Enabled: false
Style/Not:
  Description: Use ! instead of not.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#bang-not-not
  Enabled: false
Style/NumericLiterals:
  Description: Add underscores to large numeric literals to improve their readability.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics
  Enabled: false
Style/OneLineConditional:
  Description: Favor the ternary operator(?:) over if/then/else/end constructs.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#ternary-operator
  Enabled: false
Naming/BinaryOperatorParameterName:
  Description: When defining binary operators, name the argument other.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#other-arg
  Enabled: false
Metrics/ParameterLists:
  Description: Avoid parameter lists longer than three or four parameters.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#too-many-params
  Enabled: false
Style/PercentLiteralDelimiters:
  Description: Use `%`-literal delimiters consistently
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-literal-braces
  Enabled: false
Style/PerlBackrefs:
  Description: Avoid Perl-style regex back references.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers
  Enabled: false
Naming/PredicateName:
  Description: Check the names of predicate methods.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark
  ForbiddenPrefixes:
  - is_
  Exclude:
  - spec/**/*
Style/Proc:
  Description: Use proc instead of Proc.new.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#proc
  Enabled: false
Style/RaiseArgs:
  Description: Checks the arguments passed to raise/fail.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#exception-class-messages
  Enabled: false
Style/RegexpLiteral:
  Description: Use / or %r around regular expressions.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-r
  Enabled: false
Style/SelfAssignment:
  Description: Checks for places where self-assignment shorthand should have been
    used.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#self-assignment
  Enabled: false
Style/SingleLineBlockParams:
  Description: Enforces the names of some block params.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#reduce-blocks
  Enabled: false
Style/SingleLineMethods:
  Description: Avoid single-line methods.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-single-line-methods
  Enabled: false
Style/SignalException:
  Description: Checks for proper usage of fail and raise.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#fail-method
  Enabled: false
Style/SpecialGlobalVars:
  Description: Avoid Perl-style global variables.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms
  Enabled: false
Style/StringLiterals:
  Description: Checks if uses of quotes match the configured preference.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-string-literals
  EnforcedStyle: single_quotes
  Enabled: true
Style/TrailingCommaInArguments:
  Description: Checks for trailing comma in argument lists.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas
  EnforcedStyleForMultiline: no_comma
  SupportedStylesForMultiline:
  - comma
  - consistent_comma
  - no_comma
  Enabled: true
Style/TrailingCommaInArrayLiteral:
  Description: Checks for trailing comma in array literals.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas
  EnforcedStyleForMultiline: comma
  SupportedStylesForMultiline:
  - comma
  - consistent_comma
  - no_comma
  Enabled: true
Style/TrailingCommaInHashLiteral:
  Description: Checks for trailing comma in hash literals.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas
  EnforcedStyleForMultiline: comma
  SupportedStylesForMultiline:
  - comma
  - consistent_comma
  - no_comma
  Enabled: true
Style/TrivialAccessors:
  Description: Prefer attr_* methods to trivial readers/writers.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#attr_family
  Enabled: false
Style/VariableInterpolation:
  Description: Don't interpolate global, instance and class variables directly in
    strings.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#curlies-interpolate
  Enabled: false
Style/WhenThen:
  Description: Use when x then ... for one-line cases.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#one-line-cases
  Enabled: false
Style/WhileUntilModifier:
  Description: Favor modifier while/until usage when you have a single-line body.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier
  Enabled: false
Style/WordArray:
  Description: Use %w or %W for arrays of words.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-w
  Enabled: false
Layout/ConditionPosition:
  Description: Checks for condition placed in a confusing position relative to the
    keyword.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#same-line-condition
  Enabled: false
Layout/DotPosition:
  Description: Checks the position of the dot in multi-line method calls.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains
  EnforcedStyle: trailing
Layout/ExtraSpacing:
  Description: Do not use unnecessary spacing.
  Enabled: true
Layout/MultilineOperationIndentation:
  Description: Checks indentation of binary operations that span more than one line.
  Enabled: true
  EnforcedStyle: indented
Layout/MultilineMethodCallIndentation:
  Description: Checks indentation of method calls with the dot operator that span
    more than one line.
  Enabled: true
  EnforcedStyle: indented
Layout/InitialIndentation:
  Description: Checks the indentation of the first non-blank non-comment line in a
    file.
  Enabled: false
Lint/AmbiguousOperator:
  Description: Checks for ambiguous operators in the first argument of a method invocation
    without parentheses.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-as-args
  Enabled: false
Lint/AmbiguousRegexpLiteral:
  Description: Checks for ambiguous regexp literals in the first argument of a method
    invocation without parenthesis.
  Enabled: false
Lint/AssignmentInCondition:
  Description: Don't use assignment in conditions.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition
  Enabled: false
Lint/CircularArgumentReference:
  Description: Don't refer to the keyword argument in the default value.
  Enabled: false
Lint/DeprecatedClassMethods:
  Description: Check for deprecated class method calls.
  Enabled: false
Lint/EachWithObjectArgument:
  Description: Check for immutable argument given to each_with_object.
  Enabled: false
Lint/ElseLayout:
  Description: Check for odd code arrangement in an else block.
  Enabled: false
Lint/FormatParameterMismatch:
  Description: The number of parameters to format/sprint must match the fields.
  Enabled: false
Lint/LiteralAsCondition:
  Description: Checks of literals used in conditions.
  Enabled: false
Lint/LiteralInInterpolation:
  Description: Checks for literals used in interpolation.
  Enabled: false
Lint/Loop:
  Description: Use Kernel#loop with break rather than begin/end/until or begin/end/while
    for post-loop tests.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#loop-with-break
  Enabled: false
Lint/NestedMethodDefinition:
  Description: Do not use nested method definitions.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-methods
  Enabled: false
Lint/NonLocalExitFromIterator:
  Description: Do not use return in iterator to cause non-local exit.
  Enabled: false
Lint/ParenthesesAsGroupedExpression:
  Description: Checks for method calls with a space before the opening parenthesis.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-no-spaces
  Enabled: false
Lint/RequireParentheses:
  Description: Use parentheses in the method call to avoid confusion about precedence.
  Enabled: false
Lint/UnderscorePrefixedVariableName:
  Description: Do not use prefix `_` for a variable that is used.
  Enabled: false
Lint/Void:
  Description: Possible use of operator/literal/variable in void context.
  Enabled: false
Performance/CaseWhenSplat:
  Description: Place `when` conditions that use splat at the end of the list of `when`
    branches.
  Enabled: false
Performance/Count:
  Description: Use `count` instead of `select...size`, `reject...size`, `select...count`,
    `reject...count`, `select...length`, and `reject...length`.
  Enabled: false
Performance/Detect:
  Description: Use `detect` instead of `select.first`, `find_all.first`, `select.last`,
    and `find_all.last`.
  Reference: https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code
  Enabled: false
Performance/FlatMap:
  Description: Use `Enumerable#flat_map` instead of `Enumerable#map...Array#flatten(1)`
    or `Enumberable#collect..Array#flatten(1)`
  Reference: https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code
  Enabled: false
Performance/ReverseEach:
  Description: Use `reverse_each` instead of `reverse.each`.
  Reference: https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code
  Enabled: false
Performance/Size:
  Description: Use `size` instead of `count` for counting the number of elements in
    `Array` and `Hash`.
  Reference: https://github.com/JuanitoFatas/fast-ruby#arraycount-vs-arraysize-code
  Enabled: false
Performance/StringReplacement:
  Description: Use `tr` instead of `gsub` when you are replacing the same number of
    characters. Use `delete` instead of `gsub` when you are deleting characters.
  Reference: https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code
  Enabled: false
Rails/ActionFilter:
  Description: Enforces consistent use of action filter methods.
  Enabled: false
Rails/Date:
  Description: Checks the correct usage of date aware methods, such as Date.today,
    Date.current etc.
  Enabled: false
Rails/FindBy:
  Description: Prefer find_by over where.first.
  Enabled: false
Rails/FindEach:
  Description: Prefer all.find_each over all.find.
  Enabled: false
Rails/HasAndBelongsToMany:
  Description: Prefer has_many :through to has_and_belongs_to_many.
  Enabled: false
Rails/Output:
  Description: Checks for calls to puts, print, etc.
  Enabled: false
Rails/ReadWriteAttribute:
  Description: Checks for read_attribute(:attr) and write_attribute(:attr, val).
  Enabled: false
Rails/ScopeArgs:
  Description: Checks the arguments of ActiveRecord scopes.
  Enabled: false
Rails/TimeZone:
  Description: Checks the correct usage of time zone aware methods.
  StyleGuide: https://github.com/bbatsov/rails-style-guide#time
  Reference: http://danilenko.org/2012/7/6/rails_timezones
  Enabled: false
Rails/Validation:
  Description: Use validates :attribute, hash of validations.
  Enabled: false
Lint/FlipFlop:
  Description: Checks for flip flops
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-flip-flops
  Enabled: false
Layout/LineLength:
  Description: Limit lines to 80 characters.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#80-character-limits
  Max: 100
Style/Sample:
  Description: Use `sample` instead of `shuffle.first`, `shuffle.last`, and `shuffle[Fixnum]`.
  Reference: https://github.com/JuanitoFatas/fast-ruby#arrayshufflefirst-vs-arraysample-code
  Enabled: false
Layout/ParameterAlignment:
  Description: Here we check if the parameters on a multi-line method call or definition
    are aligned.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-double-indent
  Enabled: false
Lint/DuplicateHashKey:
  Description: Check for duplicate keys in hash literals.
  Enabled: false
Lint/SuppressedException:
  Description: Don't suppress exception.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions
  Enabled: false
Lint/RedundantCopDisableDirective:
  Description: 'Checks for rubocop:disable comments that can be removed. Note: this
    cop is not disabled when disabling all cops. It must be explicitly disabled.'
  Enabled: false


================================================
FILE: .ruby-version
================================================
ruby-3.1.2


================================================
FILE: .travis.yml
================================================
dist: xenial
sudo: required
language: ruby
rvm:
  - ruby-2.4.4
services:
  - redis-server
  - mysql

env:
  global:
  - GATE_SERVER_URL=gate.server/url
  - SIGN_IN_TYPE=
  - GATE_OAUTH_CLIENT_ID=totally_secret_client_id
  - GATE_OAUTH_CLIENT_SECRET=totally_secret
  - GATE_HOSTED_DOMAIN=gate.domain
  - GATE_HOSTED_DOMAINS=test1.com,test2.com
  - GATE_DB_HOST=localhost
  - GATE_DB_PORT=3306
  - GATE_DB_NAME=
  - GATE_DB_USER=root
  - GATE_DB_PASSWORD=
  - CACHE_HOST=localhost
  - CACHE_PORT=6379
  - SECRET_KEY_BASE=
  - SECRET_API_KEY=
  - GATE_CONFIG_SECRET=gate_pw_secret
  - USER_ROLES=employee,consultant
  - UID_BUFFER=5000
  - DEFAULT_HOST_PATTERN=s*
  - PRODUCT_LIST=pr1,pr2
  - SAML_APPS=datadog
  - 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=
  - 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=
  - secure: KtmItMzRHfZEPS54hTnYXNAWsqxCZZMtmxamJPcr2SVP+pzM/25eEF3M2b0JZECFRtW3xPBGita+4BjH8IY4NljH0IDVhPPKQsMHaVKtaqEeiTCRXCqkJd0DMUDeqyRcccwPDJSy9HeZns6E+zQDCGaC/tmFwDUWy5BYpairWd7sdw436iOeTmQ2fJ8SQ9lBr9LGpk8NwDmkYqg14fnOraIhw70tiWv9Wn82mEv+OwY4iKphOv2ZXEBeJYitoT1LQmhnaVvNvax16TYIY3Mxh89Oy2gVkKCgvbR9q1NmAIiWmMTyCLWxF0bd6A6Q+dHDDaQZF0pDsQqe83rnoA0xuaJUF9dh+ahvW0bn0aVFIa8tWgiBEMXWxHEDuEyfCWFis9vf3uj1xJl0SV0ktE5oIRzaF/0cTQorAwRy0UpN1HxK1yB9aITCad/BJ9ERK8MXYnfzC65jn2zHuQMjBz9Aeig4sgLc4IgtGM2DzabsIkhYG3ygL1TYaXXSj5Mo2KDwOuaZ46C7B6qxlzcesPmSnM3KF1CBatRZQK5tjsMm5GnxXKKtqg/udGtOA2fELlNolFsojtpmO4A79TAnebRUpEsU2vP0da1x7yTD5+2NWg6dnvq7tA3c5b8qBK2cAMnCHS0gmjaN3RYeFhLYO55dHDdmx/DNyQQ1V/YEkuisy80=
  - COMMIT=${TRAVIS_COMMIT::8}
  - DOCKER_COMPOSE_VERSION=1.11.2

before_install:
  - gem update --system
  - gem install bundler

stages:
  - build, test and releases
  - name: build and publish docker
    if: (branch = master OR tag IS present) and type != pull_request

jobs:
  include:
    - stage: build, test and releases
      script:
        - sudo mysql_upgrade -u root
        - sudo service mysql restart
        - mysql -e 'CREATE DATABASE IF NOT EXISTS gate_test;'
        - gem install bundler
        - bundle package --all
        - bundle install --binstubs --local
        - bundle exec rake db:migrate
        - bundle exec rake spec
        - bundle exec rake assets:precompile RAILS_ENV=production
      before_deploy:
        - "rm -rf $TRAVIS_BUILD_DIR/*.tar.gz"
        - "tar zcf gate-$TRAVIS_TAG.tar.gz -C $TRAVIS_BUILD_DIR ./*"
      deploy:
        provider: releases
        skip_cleanup: true
        api_key:
          secure: VWDGnrtrECoeJBATfe71HdpvnD15IKTkHWq/cQDfo9FTrm005ngTusQpW0eim76tSTMcJ1OBLoE9aAkd4Szi00TWenNPaAWHtgodcvNXxjqyZEtznRmtZW/vSk2H+iwJxJZoPDMZqStjmmSoD0dA1t2GLGltFzeJ5Y409YeroLUsVdRcTJY0mvQYbuLt1aZCu76gY5nB01yH7ib4ykZllJQbkyX57TKBYSLoQVUQ3hqo9BLUoROknHSfFGBHjSN9D3zIqKoh0XDOuTnyMHB+zMGmjjJbHMx5Z2eW1NW1HvpqDUOPl8uRnUEoCCfujA7OUlGgdadRBeUJObzpNkpQUNLEh5zIIr9NAZzjunzYkMiMZIseTNflJ6HOajld2Bcgz3PcbzM8hcIFog5loE7CBLWEzDoOIY/B75NYuHPIzRzTbgw7jf8XdfypCNMm0RLWiCXiDmuDMcNPZT9NrrheNGFz1CDmWkwpJmUTIv8J13J2Ux9ex1NY9iSRD8rgJ+bLOfp/u1nJWjACki8zq2/vffm2oUWz/R7q23Keq5Itddpe3mJUyFjokTUZPNSAlC45rF+rgK4ka7yJb+ZZAOaq/N8iIfVkgnfviWtaPQzAWRkO8J8Wzt1GrfQpNlo2BMM6Cpy0iV9ANYqAVYerImAopMWlzQmjbtt8P+xmmYA0FIw=
        file_glob: true
        file: $TRAVIS_BUILD_DIR/*.tar.gz
        on:
          tags: true
          repo: gate-sso/gate
    - stage: build and publish docker
      install: skip
      script:
        - gem install bundler
        - bundle install
        - bundle exec rake assets:precompile RAILS_ENV=production
        - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
        - docker build -t gatesso/gate:latest .
        - if [ -n "$TRAVIS_TAG" ]; then
            docker tag gatesso/gate:latest gatesso/gate:$TRAVIS_TAG;
          fi
        - docker images
        - docker push gatesso/gate


================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [1.1.8] - 2020-02-17
- Securing organisations configuration page

## [1.1.7] - 2020-02-24
### Fixed
- Fix audit trail when user removed from group

## [1.1.6] - 2020-01-14
### Changed
- Store session to db using active record instead of using cookies

## [1.1.5] - 2019-10-20
### Changed
- User API now also return `id`

## [1.1.4] - 2019-10-05
### Added
- API to add user to group (POST /api/v1/groups/:group_id/users)

## [1.1.3] - 2019-09-17
### Changed
- Update devise to version `4.7.1`.

## [1.1.2] - 2019-09-17
### Added
- Show email of group admin on group detail
- Show users join date to group on group detail

## [1.1.1] - 2019-09-02
### Changed
- Update nokogiri to version `1.10.4`.

## [1.1.0] - 2019-09-02
### Fixed
- Ensure that 'deactivated_at' on user is automatically set when we make user inactive
### Changed
- Improve access policy for actions on resources including profile, user, api resource, host machine, organisation
### Added
- Add endpoint entity to represent gate endpoint. Group will own endpoints, this mechanism will be used as an authorization for gate.
- Add api to deactivate user in gate (PATCH /api/v1/users/:id/deactivate).

## [1.0.5] - 2019-08-06
### Fixed
- Improve loading time when opening group and user show page

## [1.0.4] - 2019-07-17
### Fixed
- If a user don't have any VPNs, they should still be able to click download VPN without incurring exception
- Create missing tests for user model
- Optimize queries when fetching sysadmins

## [1.0.3] - 2019-07-16
### Fixed
- Fix nil pointer exception when group members response is nil

## [1.0.2] - 2019-07-15
### Fixed
- Optimize slow queries on vpn model

## [1.0.1] - 2019-07-15
### Added
- Add the ability to only fetch active user for `/api/v1/users/profile` API

## [1.0.0] - 2019-07-15
### Changed
- Use dotenv instead of figaro. This is a breaking change and warrant a major version release.
- All spiders are banned by default now in `robots.txt`
- When admin account become inactive, the admin status will automatically revoked.
- Admin can set expiration date on group assignment. This expiration date is optional, when not specified then it's a permanent assignment.

## [0.1.0] - 2019-05-20
### Changed
- Gate now uses semantic versioning.


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing Guidelines

## Releasing Gate

> Gate uses semantic versioning, check [this page](https://semver.org/) for more details on how to bump the version number.

Steps on releasing Gate.

1. Bump version on [VERSION](VERSION) file.
2. Add appropriate changelogs on [CHANGELOG.md](CHANGELOG.md) file. Please follow existing format.
3. Tag the commit by the new version number and push it, travis will automatically build and release Gate.


================================================
FILE: Dockerfile
================================================
FROM ruby:2.4

RUN apt-get update
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash
RUN apt-get update -qq && apt-get install -y build-essential nodejs git

RUN mkdir /app
WORKDIR /app

COPY Gemfile /app
COPY Gemfile.lock /app

RUN gem install bundler -v '>= 2.0'
RUN bundle install --without development
COPY . /app

CMD [ "bundle", "exec", "rails", "s" ]


================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'

gem 'rails', '7.1.3.2'

gem 'activerecord-session_store'
gem 'ansi'
gem 'bootstrap'
gem 'countries', require: 'countries/global'
gem 'devise'
gem 'dotenv-rails'
gem 'font-awesome-rails'
gem 'httparty'
gem 'jbuilder'
gem 'mysql2'
gem 'omniauth'
gem 'omniauth-google-oauth2'
gem 'paranoia'
gem 'puma'
gem 'redis'
gem 'rotp'
gem 'rqrcode'

gem 'sassc-rails'


#Disabling SAML Feature during upgrade
# #TODO ENable this feature later
#gem 'ruby-saml', '1.8.0'
#gem 'saml_idp', git: 'https://github.com/gate-sso/saml_idp'
gem 'sdoc', group: :doc
# #TODO ENable this feature later

gem 'slim-rails'
gem 'turbolinks'
gem 'uglifier'
gem 'whenever', require: false

group :development, :test do
  gem 'capybara'
  gem 'coveralls'
  gem 'database_cleaner'
  gem 'dredd_hooks'
  gem 'factory_bot_rails'
  gem 'faker'
  gem 'mock_redis'
  gem 'pry'
  gem 'rails-controller-testing'
  gem 'rspec-rails'
  gem 'rubocop'
  gem 'rubocop-faker'
  gem 'rubocop-performance'
  gem 'rubocop-rails'
  gem 'rubocop-rspec'
  gem 'shoulda-matchers'
  gem 'simplecov'
  gem 'simplecov-console'
  gem 'timecop'
  gem 'webmock'
end

group :development do
  gem 'web-console', platform: :ruby
end


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2016 gate-sso

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Gate is now Gate-WireGuard 

## DEPRECATED

#### Please use [Gate-WireGuard](https://github.com/gate-sso/gate-wireguard)

GateWireguard is based on WireGuard VPN Backend, also, it provides better security, faster speeds; it's much easier to install and run.


## *MASTER is broken* we are migrating to Rails 7.

## Ubuntu dependencies
Apart from devtools you need to install the following packages as well.
```
    apt-get install libmysqlclient-dev
    apt-get install libyaml-dev
```

#### 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

#### Please use [RAILS 5 Branch](https://github.com/gate-sso/gate/tree/RAILS_5_RELEASE) for backward compatibilty

> 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).

Gate is a single sign-on (SSO) platform for centralised authentication across Linux, OpenVPN and CAS.

Gate 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:

1. Setup OpenVPN with Gate authentication.
2. Automatically create VPN profiles for each users.
3. Provide you with JaSig CAS Custom Authentication Handler to authenticate with Gate SSO and in turn enabling MFA for JaSig CAS.
4. Enable Linux authentication with [pam_gate](https://github.com/gate-sso/pam_gate), which sits like a small module with Linux and allow authentication.
5. Enable Name Service Switch (NSS) on Linux, so that Gate users can be discovered and authenticated on Linux.
6. **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).

> 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.

> 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.

### Modules

* [pam_gate](https://github.com/gate-sso/pam_gate) - Gate module for Linux PAM
* [nss_gate](https://github.com/gate-sso/nss_gate) - Gate module for Linux Name Server Switch (NSS)
* [cas_gate](https://github.com/gate-sso/cas_gate) - CAS Customer MFA authentication handler for Gate
* open_vpn_gate - for OpenVPN setup, it is not extracted yet.

## Development Setup

> We are in the process of improving Gate setup process, please check back for updated instructions.

### Manual Setup

#### Initializing Your Application

* Ensure that ruby is installed (>= 2.4) and `bundler` gem is installed.
* Clone [Gate repository](https://github.com/gate-sso/gate)
* Run `bundle install`
* Run `rake app:init` to create environment file based on sample (we use dotenv to manage environment variables).

#### Setting up OAuth (Optional)

> 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.

> Note that you still need to update `GATE_HOSTED_DOMAINS` to serve your e-mail domain.

Check [this guide](docs/oauth_setup.md) For detailed information on how to setup OAuth.

#### Setting up Database and Cache

* Install and setup database (mysql) and update the following values (`GATE_DB_HOST`, `GATE_DB_PORT`, `GATE_DB_USER`, `GATE_DB_PASSWORD`) on `.env`.
* Install and setup cache (redis) and update the following values (`CACHE_DB`, `CACHE_HOST`).

#### Finishing Steps

To 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.

Once Gate is setup, sign-in with your user and you should see welcome page with VPN profile download and VPN MFA Scanning.

If you want Gate to setup VPN for you then just install OpenVPN with easy rsa. Gate should just work fine with it.

> **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.

### Docker Setup

* Build docker image using `docker build -t gate .`
* Create and update `.env` file according to `.env.example` with appropriate values
* Run the image using `docker run -p 3000:3000 --env-file=.env -it gate`
* If you want use docker-compose run using `docker-compose up`

## Additional Topics

* [API Blueprint Test](docs/dredd_setup.md)
* [Additional Setup](docs/additional_setup.md)
* [Administration](docs/administration.md)
* [Newrelic Integration](docs/newrelic.md)

### Changelog

See [CHANGELOG.md](CHANGELOG.md)

### Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md)

### License

MIT License, See [LICENSE](LICENSE).


================================================
FILE: Rakefile
================================================
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require File.expand_path('../config/application', __FILE__)

Rails.application.load_tasks


================================================
FILE: VERSION
================================================
1.1.8


================================================
FILE: api_blueprint/bin/dredd_server.sh
================================================
#!/bin/bash
# dredd_server.sh
kill -9 $(lsof -i tcp:9865 -t)
export RAILS_ENV=test
export LOG_LEVEL=info
rake db:drop
rake db:setup
bundle exec rails server --port=9865



================================================
FILE: api_blueprint/group.apib
================================================
FORMAT: 1A

# API Group

# Group [/api/v1/groups]

## Create Groups [POST]
Create new group

+ Request(application/json)

    + Body

            {
                "access_token": "token",
                "name" : "foo"
            }
    
    + Schema
    
            {
                "type": "object",
                "properties" : {
                    "name" : {
                        "type" : "string"
                    },
                    "access_token" : {
                        "type" : "string"
                    }
                }
            }
    
+ Response 200

    + Headers

            x-frame-options: SAMEORIGIN
            x-xss-protection: 1; mode=block
            x-content-type-options: nosniff
            content-type: application/json; charset=utf-8
            etag: W/"5c15461069e69109955c72671ffc465d"
            cache-control: max-age=0, private, must-revalidate
            x-request-id: 0613ec11-afd7-4dc0-9096-fee607d71c12
            x-runtime: 0.073641
            connection: close
            transfer-encoding: chunked

    + Body
    
            {
                "id": 1,
                "name": "foo"
            }
    + Schema
    
            {
                "type": "object",
                "properties" : {
                    "id" : {
                        "type" : "int"
                    },
                    "name" : {
                        "type" : "string"
                    }
                }
            }

+ Request(application/json)

    + Body

            {
                "access_token": "token",
                "name" : "foo"
            }
    
    + Schema
    
            {
                "type": "object",
                "properties" : {
                    "name" : {
                        "type" : "string"
                    },
                    "access_token" : {
                        "type" : "string"
                    }
                }
            }

+ Response 422

    + Headers

            x-frame-options: SAMEORIGIN
            x-xss-protection: 1; mode=block
            x-content-type-options: nosniff
            content-type: application/json; charset=utf-8
            cache-control: no-cache
            x-request-id: 30ec7a3a-4794-435b-a5b9-8ff480cd648c
            x-runtime: 0.007976
            connection: close
            transfer-encoding: chunked

    + Body

            {
                "status": "group already exist",
                "id": 1,
                "name": "foo"
            }
    + Schema
    
            {
                "type": "object",
                "properties" : {
                    "status" : {
                        "type" : "string"
                    },
                    "id" : {
                        "type" : "int"
                    },
                    "name" : {
                        "type" : "string"
                    }
                }
            }

+ Request(application/json)

    + Body

            {
                "access_token": "wrong token",
                "name" : "foo"
            }
    
    + Schema
    
            {
                "type": "object",
                "properties" : {
                    "name" : {
                        "type" : "string"
                    },
                    "access_token" : {
                        "type" : "string"
                    }
                }
            }

+ Response 401

    + Headers

            x-frame-options: SAMEORIGIN
            x-xss-protection: 1; mode=block
            x-content-type-options: nosniff
            content-type: text/html
            cache-control: no-cache
            x-request-id: 30276bf6-0ce6-4ba6-b87d-7528b4f2c85d
            x-runtime: 0.002650
            connection: close
            transfer-encoding: chunked

    + Body

# Group Users [/api/v1/groups/{group_id}/users]

+ Parameters
    + group_id: 1 (required, number) - ID of the Group in form of an integer

## Add user [POST]

+ user_id (required, number) - ID of the User in form of an integer
+ expiration_date (optional, date) - Membership expiration date in format (YYYY-MM-DD)

+ Request (application/json)

    + Header

            Authorization: <user-token>

    + Body

            {
                "user_id": 7,
                "expiration_date": "2019-11-20"
            }

+ Response 204


================================================
FILE: api_blueprint/hooks/dredd_hooks.rb
================================================
ENV['RAILS_ENV'] ||= 'test'

require File.expand_path('../../config/environment', __dir__)
require 'dredd_hooks/methods'

include DreddHooks::Methods

before_all do |_|
  user = User.create(name: 'foo', email: 'bar@test.com', admin: 1)
  access_token = AccessToken.new
  access_token.token = 'token'
  access_token.user = user
  user.access_token = access_token
  access_token.save!
  user.save!
  inactive_user = User.create(name: 'foo', email: 'foo@test2.com', active: 0, user_login_id: 'foo')
  inactive_user.save!
  active_user = User.create(name: 'foo', email: 'foo@test.com', active: 1, user_login_id: 'foo')
  active_user.save!
end


================================================
FILE: api_blueprint/user.apib
================================================
FORMAT: 1A

# API User

# User [/api/v1/users]

## Create Users [POST]
Create new users gate

+ Request(application/json)

    + Body
    
            {
                "access_token": "token",
                "name" : "foo",
                "email" : "foo@test.com"
            }

    + Schema
    
            {
                "type": "object",
                "properties" : {
                    "name" : {
                        "type" : "string"
                    },
                    "email" : {
                        "type" : "string"
                    },
                    "access_token" : {
                        "type" : "string"
                    }
                }
            }

+ Response 200

    + Headers

            x-frame-options: SAMEORIGIN
            x-xss-protection: 1; mode=block
            x-content-type-options: nosniff
            content-type: application/json; charset=utf-8
            etag: W/"5c15461069e69109955c72671ffc465d"
            cache-control: max-age=0, private, must-revalidate
            x-request-id: 0613ec11-afd7-4dc0-9096-fee607d71c12
            x-runtime: 0.073641
            connection: close
            transfer-encoding: chunked

    + Body
    
            {
                "status" : "created"
            }

    + Schema
    
            {
                "type": "object",
                "properties" : {
                    "status" : {
                        "type" : "string"
                    }
                }
            }


+ Request(application/json)

    + Body
    
            {
                "access_token": "wrong token",
                "name" : "foo",
                "email" : "foo@test.com"
            }

    + Schema
    
            {
                "type": "object",
                "properties" : {
                    "name" : {
                        "type" : "string"
                    },
                    "email" : {
                        "type" : "string"
                    },
                    "access_token" : {
                        "type" : "string"
                    }
                }
            }

+ Response 401

    + Headers

            x-frame-options: SAMEORIGIN
            x-xss-protection: 1; mode=block
            x-content-type-options: nosniff
            content-type: text/html
            cache-control: no-cache
            x-request-id: 30276bf6-0ce6-4ba6-b87d-7528b4f2c85d
            x-runtime: 0.002650
            connection: close
            transfer-encoding: chunked

    + Body

# User [/api/v1/users/profile/{?email,uid,username,active}]

## Get Users [GET]
Get user data with email, uid or username

+ Request

    + Headers

            Authorization: token

    + Parameters

        + email (string, optional)
            + Default: bar@test.com
        + uid (string, optional)
        + username (string, optional)
        + active ((string, boolean, int), optional)

+ Response 200

    + Body

            {
                "email": "bar@test.com",
                "id": null,
                "uid": null,
                "name": "foo",
                "active": true,
                "admin": true,
                "home_dir": null,
                "shell": null,
                "public_key": null,
                "user_login_id": null,
                "product_name": null,
                "groups": []
            }

+ Request

    + Headers

            Authorization: token

    + Parameters

        + email (string, optional)
        + uid (string, optional)
        + username (string, optional)
            + Default: foo
        + active ((string, boolean, int), optional)
            + Default: 1

+ Response 200

    + Body

            {
                "email": "foo@test.com",
                "uid": null,
                "name": "foo",
                "active": true,
                "admin": true,
                "home_dir": null,
                "shell": null,
                "public_key": null,
                "user_login_id": "foo",
                "product_name": null,
                "groups": []
            }
            
+ Request

    + Headers

            Authorization: token

    + Parameters

        + email (string, optional)
            + Default: notfound@test.com
        + uid (string, optional)
        + username (string, optional)
        + active ((string, boolean, int), optional)

+ Response 404

    + Body

# User [/api/v1/users/profile]

## Update Users [POST]
Update users data with email as key 

+ Request(application/json)

    + Headers

            Authorization: token

    + Body
    
            {
                "name" : "foo bar",
                "email" : "bar@test.com"
            }

    + Schema
    
            {
                "type": "object",
                "properties" : {
                    "name" : {
                        "type" : "string"
                    },
                    "email" : {
                        "type" : "string"
                    }
                }
            }

+ Response 200

    + Headers

            x-frame-options: SAMEORIGIN
            x-xss-protection: 1; mode=block
            x-content-type-options: nosniff
            content-type: application/json; charset=utf-8
            etag: W/"c955e57777ec0d73639dca6748560d00"
            cache-control: max-age=0, private, must-revalidate
            x-request-id: 136874b1-39fb-4704-b548-d7670773a4bf
            x-runtime: 0.014707
            connection: close
            transfer-encoding: chunked

    + Body

            {
                "success": true
            }    


+ Request(application/json)

    + Headers

            Authorization: wrong-token

    + Body
    
            {
                "name" : "foo bar",
                "email" : "bar@test.com"
            }

    + Schema
    
            {
                "type": "object",
                "properties" : {
                    "name" : {
                        "type" : "string"
                    },
                    "email" : {
                        "type" : "string"
                    }
                }
            }

+ Response 401

    + Headers

            x-frame-options: SAMEORIGIN
            x-xss-protection: 1; mode=block
            x-content-type-options: nosniff
            content-type: text/html
            cache-control: no-cache
            x-request-id: 85cb1679-58d4-41c4-9479-ce5f9c75ca7a
            x-runtime: 0.002542
            connection: close
            transfer-encoding: chunked


================================================
FILE: api_blueprint/vpns.apib
================================================
FORMAT: 1A

# API VPNS

# VPNS [/api/v1/vpns]

## Create VPNS [POST]
Create new vpns

+ Request(application/json)

    + Headers
    
            Authorization: token

    + Body
    
            {
                "id": 1,
                "name": "test-vpn",
                "host_name": "test-vpn.example.com",
                "ip_address" : "10.10.10.10"
            }

    + Schema

            {
                "type": "object",
                "properties" : {
                    "id" : {
                        "type" : int
                    },
                    "name" : {
                        "type" : "string"
                    },
                    "host_name" : {
                        "type" : "string"
                    },
                    "ip_address" : {
                        "type" : "string"
                    }
                }
            }

+ Response 200

    + Body

            {
                "id": 1,
                "name": "test-vpn",
                "host_name": "test-vpn.example.com",
                "ip_address": "10.10.10.10"
            }

+ Request(application/json)

    + Headers
    
            Authorization: wrong-token
    
    + Body

+ Response 401

    + Headers

            x-frame-options: SAMEORIGIN
            x-xss-protection: 1; mode=block
            x-content-type-options: nosniff
            content-type: text/html
            cache-control: no-cache
            x-request-id: 73b10aa8-6f68-400b-b723-2e992ac4d000
            x-runtime: 0.002242
            connection: close
            transfer-encoding: chunked


================================================
FILE: app/assets/config/manifest.js
================================================
//= link application.css
//= link apple-touch-icon-144x144-precomposed.png
//= link apple-touch-icon-72x72-precomposed.png
//= link apple-touch-icon-precomposed.png
//= link favicon.ico
//= link application.js


================================================
FILE: app/assets/images/.keep
================================================


================================================
FILE: app/assets/javascripts/admin.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/api_resources.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/

API_RESOURCE_NAME_FORMAT_ERROR_MSG = "Please enter valid name containing only alphanumeric (a-z, A-Z, 0-9), underscore (_) and dash (-)"
API_RESOURCE_NAME_UNIQUENESS_ERROR_MSG = "API name already taken"
API_RESOURCE_DESC_REQUIRED_ERROR_MSG = "Please enter description"

api_resources_ready = ->
  $('#api_resource_name').on 'blur', ->
    valid = validate_pattern($('#api_resource_name'), '^[a-zA-Z0-9_-]+$', API_RESOURCE_NAME_FORMAT_ERROR_MSG)
    if valid 
      validate_uniqueness($('#api_resource_name'), '/api_resources/search', API_RESOURCE_NAME_UNIQUENESS_ERROR_MSG)

  $('#api_resource_description').on 'blur', ->
    validate_required($('#api_resource_description'), API_RESOURCE_DESC_REQUIRED_ERROR_MSG)

  $('#new_api_resource').submit (event) ->
    if $('.is-invalid').length > 0
      event.preventDefault()
      event.stopPropagation()
    return

$(document).on('turbolinks:load', api_resources_ready)


================================================
FILE: app/assets/javascripts/application.js
================================================
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
// require jquery3
// require popper
// require bootstrap-sprockets
// require jquery_ujs
// require turbolinks
// require_tree .


================================================
FILE: app/assets/javascripts/bootstrap.js.coffee
================================================
jQuery ->
  $("a[rel~=popover], .has-popover").popover()
  $("a[rel~=tooltip], .has-tooltip").tooltip()


================================================
FILE: app/assets/javascripts/group.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/

group_ready = ->
  $('#assign_admin_include_inactive_user').prop('checked', false);
  $('#assign_admin_user_id').selectize
    maxItems: 1
    valueField: 'id'
    labelField: 'name'
    searchField: 'name_email'
    create: false
    render: option: (item, escape) ->
      '<div>' + escape(item.name) + '<br /><span class=\'small\'>' + escape(item.email) + '</span></div>'
    load: (query, callback) ->
      if !query.length
        return callback()

      # Clear the cache, because user list may change
      this.clearOptions()
      include_inactive = $('#assign_admin_include_inactive_user').is(':checked')

      # Use remote as source
      $.ajax
        url: '/users/search?q=' + encodeURIComponent(query) + '&include_inactive=' + include_inactive
        type: 'GET'
        error: ->
          callback()
          return
        success: (res) ->
          callback res
          return
      return

  $('#assign_admin_user_id').on 'change', ->
    set_allow_submit($(this).val(), $(this))

  $('#add_user_include_inactive_user').prop('checked', false);
  $('#add_user_user_id').selectize
    maxItems: 1
    valueField: 'id'
    labelField: 'name'
    searchField: 'name_email'
    create: false
    render: option: (item, escape) ->
      '<div>' + escape(item.name) + '<br /><span class=\'small\'>' + escape(item.email) + '</span></div>'
    load: (query, callback) ->
      if !query.length
        return callback()

      # Clear the cache, because user list may change
      this.clearOptions()
      include_inactive = $('#add_user_include_inactive_user').is(':checked')

      # Use remote as source
      $.ajax
        url: '/users/search?q=' + encodeURIComponent(query) + '&include_inactive=' + include_inactive
        type: 'GET'
        error: ->
          callback()
          return
        success: (res) ->
          callback res
          return
      return

  $('#add_user_user_id').on 'change', ->
    set_allow_submit($(this).val(), $(this))

  $('#add_vpn_vpn_id').selectize
    maxItems: 1
    valueField: 'id'
    labelField: 'name'
    searchField: 'name'
    create: false
    render: option: (item, escape) ->
      '<div>' + escape(item.name) + '</div>'
    load: (query, callback) ->
      if !query.length
        return callback()

      # Use remote as source
      $.ajax
        url: '/vpns/search?q=' + encodeURIComponent(query) 
        type: 'GET'
        error: ->
          callback()
          return
        success: (res) ->
          callback res
          return
      return

  $('#add_vpn_vpn_id').on 'change', ->
    set_allow_submit($(this).val(), $(this))

  $('#add_machine_machine_id').selectize
    maxItems: 1
    valueField: 'id'
    labelField: 'name'
    searchField: 'name'
    create: false
    render: option: (item, escape) ->
      '<div>' + escape(item.name) + '</div>'
    load: (query, callback) ->
      if !query.length
        return callback()

      # Use remote as source
      $.ajax
        url: '/host_machines/search?q=' + encodeURIComponent(query) 
        type: 'GET'
        error: ->
          callback()
          return
        success: (res) ->
          callback res
          return
      return

  $('#add_machine_machine_id').on 'change', ->
    set_allow_submit($(this).val(), $(this))

$(document).on('turbolinks:load', group_ready)


================================================
FILE: app/assets/javascripts/groups.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/home.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/host_access_groups.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/host_machine_groups.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/host_machines.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/

host_machines_ready = ->
  $('#group_id').selectize
    maxItems: 1
    valueField: 'id'
    labelField: 'name'
    searchField: 'name'
    create: false
    render: option: (item, escape) ->
      '<div>' + escape(item.name) + '</div>'
    load: (query, callback) ->
      if !query.length
        return callback()

      # Use remote as source
      $.ajax
        url: '/groups/search?q=' + encodeURIComponent(query) 
        type: 'GET'
        error: ->
          callback()
          return
        success: (res) ->
          callback res
          return
      return

  $('#group_id').on 'change', ->
    set_allow_submit($(this).val(), $(this))

$(document).on('turbolinks:load', host_machines_ready)


================================================
FILE: app/assets/javascripts/nss.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/omniauth_callbacks.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/profile.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/users.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/

users_ready = ->
  $('#group_id').selectize
    maxItems: 1
    valueField: 'id'
    labelField: 'name'
    searchField: 'name'
    create: false
    render: option: (item, escape) ->
      '<div>' + escape(item.name) + '</div>'
    load: (query, callback) ->
      if !query.length
        return callback()

      # Use remote as source
      $.ajax
        url: '/groups/search?q=' + encodeURIComponent(query) 
        type: 'GET'
        error: ->
          callback()
          return
        success: (res) ->
          callback res
          return
      return

  $('#group_id').on 'change', ->
    set_allow_submit($(this).val(), $(this))

$(document).on('turbolinks:load', users_ready)


================================================
FILE: app/assets/javascripts/utilities.coffee
================================================
# Utility functions

@append_error_msg = (elem, res, msg) ->
  if !res
    elem.addClass('is-invalid')
    elem.next('.invalid-feedback').html(msg)
  else
    elem.removeClass('is-invalid')
    elem.next('.invalid-feedback').html('')

@validate_required = (elem, msg) ->
  input = elem.val()
  append_error_msg(elem, input, msg)
  return !!input

@validate_pattern = (elem, pattern, msg) ->
  input = elem.val()
  regex = new RegExp(pattern)
  validate_regex = regex.test(input)
  append_error_msg(elem, validate_regex, msg)
  return !!validate_regex

@validate_uniqueness = (elem, check_url, msg) ->
  input = elem.val()
  $.ajax
    url: check_url + '?q=' + encodeURIComponent(input) + '&exact=true'
    type: 'GET'
    error: ->
      return
    success: (res) ->
      append_error_msg(elem, $.isEmptyObject(res), msg)
      return

@set_allow_submit = (cond, elem) ->
  curSubmit = $("input[type=submit]", $(elem).parents('form'))
  if !!cond
    curSubmit.prop("disabled", false)
  else
    curSubmit.prop('disabled', true)
  return


================================================
FILE: app/assets/javascripts/viewport.js
================================================
/*!
 * IE10 viewport hack for Surface/desktop Windows 8 bug
 * Copyright 2014-2015 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 */

// See the Getting Started docs for more information:
// http://getbootstrap.com/getting-started/#support-ie10-width

(function () {
  'use strict';

  if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
    var msViewportStyle = document.createElement('style')
    msViewportStyle.appendChild(
      document.createTextNode(
        '@-ms-viewport{width:auto!important}'
      )
    )
    document.querySelector('head').appendChild(msViewportStyle)
  }

})();


================================================
FILE: app/assets/stylesheets/application.scss
================================================
/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
 * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 * compiled file so the styles you add here take precedence over styles defined in any styles
 * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
 * file per style scope.
 *
 *= require_tree .
 *= require_self
 *= stub 'profile'
 *= stub 'general'
 */


@import url(https://fonts.googleapis.com/css?family=Roboto:300);
@import url(https://fonts.googleapis.com/css?family=Lato:300);
@import "bootstrap";
@import "font-awesome";

/*
 * https://github.com/ladjs/bootstrap-social
 */
@import "bootstrap-social";
a.btn { color: white!important;  }
.form-group .field_with_errors { display: block; background: none;  }
.form-group .field_with_errors label::after { content: ' *'; color: red;  }
body {
  font-family: 'Roboto', 'Lato', sans-serif;
}

.container-profile {
  max-width: 768px;
  min-width: 768px;
}

a:visited {
    color: blue;
}
/* mouse over link */
a:hover {
    color: blue;
    background: none;
}
.table-responsive { overflow-x: inherit;  }

#configAppTabsContent .tab-pane { padding-top: 20px; }
#configAppTabsContent ol li { margin-top: 10px; }
#configAppTabsContent li > ol { margin-bottom: 10px; }


================================================
FILE: app/assets/stylesheets/bootstrap-social.scss
================================================
/*
 * Social Buttons for Bootstrap
 *
 * Copyright 2013-2016 Panayiotis Lipiridis
 * Licensed under the MIT License
 *
 * https://github.com/lipis/bootstrap-social
 */
.btn-social, .btn-social-icon {
  position: relative;
  padding-left: 3.5rem;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis; }
  .btn-social > :first-child, .btn-social-icon > :first-child {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 3rem;
    line-height: 5rem;
    font-size: 1.6em;
    text-align: center;
    border-right: 1px solid rgba(0, 0, 0, 0.2); }
  .btn-social.btn-lg, .btn-lg.btn-social-icon {
    padding-left: 4.75rem; }
    .btn-social.btn-lg > :first-child, .btn-lg.btn-social-icon > :first-child {
      line-height: 4rem;
      width: 4rem;
      font-size: 1.8em; }
  .btn-social.btn-sm, .btn-sm.btn-social-icon {
    padding-left: 2.25rem; }
    .btn-social.btn-sm > :first-child, .btn-sm.btn-social-icon > :first-child {
      line-height: 2rem;
      width: 2rem;
      font-size: 1.4em; }
  .btn-social.btn-xs, .btn-xs.btn-social-icon {
    padding-left: 2.25rem; }
    .btn-social.btn-xs > :first-child, .btn-xs.btn-social-icon > :first-child {
      line-height: 2rem;
      width: 2rem;
      font-size: 1.2em; }

.btn-social > :first-child, .btn-social-icon > :first-child {
  line-height: 1.25 !important;
  padding-top: 0.5rem !important;
  padding-bottom: 0.5rem !important;
  font-size: inherit !important; }
.btn-social.btn-lg > :first-child, .btn-lg.btn-social-icon > :first-child {
  line-height: 1.25 !important;
  padding-top: 0.75rem !important;
  padding-bottom: 0.75rem !important;
  font-size: inherit !important; }

.btn-social-icon {
  height: 3rem;
  width: 3rem;
  padding: 0; }
  .btn-social-icon > :first-child {
    border: none;
    text-align: center;
    width: 100% !important; }
  .btn-social-icon.btn-lg {
    height: 4rem;
    width: 4rem;
    padding-left: 0;
    padding-right: 0; }
  .btn-social-icon.btn-sm {
    height: 2rem;
    width: 2rem;
    padding-left: 0;
    padding-right: 0; }
  .btn-social-icon.btn-xs {
    height: 2rem;
    width: 2rem;
    padding-left: 0;
    padding-right: 0; }

.btn-adn {
  color: #fff;
  background-color: #d87a68;
  border-color: #d87a68; }
  .btn-adn:hover {
    color: #fff;
    background-color: #d05f4a;
    border-color: #ce563f; }
  .btn-adn:focus, .btn-adn.focus {
    box-shadow: 0 0 0 3px rgba(216, 122, 104, 0.5); }
  .btn-adn.disabled, .btn-adn:disabled {
    background-color: #d87a68;
    border-color: #d87a68; }
  .btn-adn:active, .btn-adn.active, .show > .btn-adn.dropdown-toggle {
    background-color: #d05f4a;
    background-image: none;
    border-color: #ce563f; }

.btn-bitbucket {
  color: #fff;
  background-color: #205081;
  border-color: #205081; }
  .btn-bitbucket:hover {
    color: #fff;
    background-color: #183d62;
    border-color: #163758; }
  .btn-bitbucket:focus, .btn-bitbucket.focus {
    box-shadow: 0 0 0 3px rgba(32, 80, 129, 0.5); }
  .btn-bitbucket.disabled, .btn-bitbucket:disabled {
    background-color: #205081;
    border-color: #205081; }
  .btn-bitbucket:active, .btn-bitbucket.active, .show > .btn-bitbucket.dropdown-toggle {
    background-color: #183d62;
    background-image: none;
    border-color: #163758; }

.btn-dropbox {
  color: #fff;
  background-color: #1087dd;
  border-color: #1087dd; }
  .btn-dropbox:hover {
    color: #fff;
    background-color: #0d71b9;
    border-color: #0d6aad; }
  .btn-dropbox:focus, .btn-dropbox.focus {
    box-shadow: 0 0 0 3px rgba(16, 135, 221, 0.5); }
  .btn-dropbox.disabled, .btn-dropbox:disabled {
    background-color: #1087dd;
    border-color: #1087dd; }
  .btn-dropbox:active, .btn-dropbox.active, .show > .btn-dropbox.dropdown-toggle {
    background-color: #0d71b9;
    background-image: none;
    border-color: #0d6aad; }

.btn-facebook {
  color: #fff;
  background-color: #3b5998;
  border-color: #3b5998; }
  .btn-facebook:hover {
    color: #fff;
    background-color: #30497c;
    border-color: #2d4373; }
  .btn-facebook:focus, .btn-facebook.focus {
    box-shadow: 0 0 0 3px rgba(59, 89, 152, 0.5); }
  .btn-facebook.disabled, .btn-facebook:disabled {
    background-color: #3b5998;
    border-color: #3b5998; }
  .btn-facebook:active, .btn-facebook.active, .show > .btn-facebook.dropdown-toggle {
    background-color: #30497c;
    background-image: none;
    border-color: #2d4373; }

.btn-flickr {
  color: #fff;
  background-color: #ff0084;
  border-color: #ff0084; }
  .btn-flickr:hover {
    color: #fff;
    background-color: #d90070;
    border-color: #cc006a; }
  .btn-flickr:focus, .btn-flickr.focus {
    box-shadow: 0 0 0 3px rgba(255, 0, 132, 0.5); }
  .btn-flickr.disabled, .btn-flickr:disabled {
    background-color: #ff0084;
    border-color: #ff0084; }
  .btn-flickr:active, .btn-flickr.active, .show > .btn-flickr.dropdown-toggle {
    background-color: #d90070;
    background-image: none;
    border-color: #cc006a; }

.btn-foursquare {
  color: #fff;
  background-color: #f94877;
  border-color: #f94877; }
  .btn-foursquare:hover {
    color: #fff;
    background-color: #f8235b;
    border-color: #f71752; }
  .btn-foursquare:focus, .btn-foursquare.focus {
    box-shadow: 0 0 0 3px rgba(249, 72, 119, 0.5); }
  .btn-foursquare.disabled, .btn-foursquare:disabled {
    background-color: #f94877;
    border-color: #f94877; }
  .btn-foursquare:active, .btn-foursquare.active, .show > .btn-foursquare.dropdown-toggle {
    background-color: #f8235b;
    background-image: none;
    border-color: #f71752; }

.btn-github {
  color: #fff;
  background-color: #444444;
  border-color: #444444; }
  .btn-github:hover {
    color: #fff;
    background-color: #313131;
    border-color: #2b2b2b; }
  .btn-github:focus, .btn-github.focus {
    box-shadow: 0 0 0 3px rgba(68, 68, 68, 0.5); }
  .btn-github.disabled, .btn-github:disabled {
    background-color: #444444;
    border-color: #444444; }
  .btn-github:active, .btn-github.active, .show > .btn-github.dropdown-toggle {
    background-color: #313131;
    background-image: none;
    border-color: #2b2b2b; }

.btn-google {
  color: #fff;
  background-color: #dd4b39;
  border-color: #dd4b39; }
  .btn-google:hover {
    color: #fff;
    background-color: #cd3623;
    border-color: #c23321; }
  .btn-google:focus, .btn-google.focus {
    box-shadow: 0 0 0 3px rgba(221, 75, 57, 0.5); }
  .btn-google.disabled, .btn-google:disabled {
    background-color: #dd4b39;
    border-color: #dd4b39; }
  .btn-google:active, .btn-google.active, .show > .btn-google.dropdown-toggle {
    background-color: #cd3623;
    background-image: none;
    border-color: #c23321; }

.btn-instagram {
  color: #fff;
  background-color: #3f729b;
  border-color: #3f729b; }
  .btn-instagram:hover {
    color: #fff;
    background-color: #345e80;
    border-color: #305777; }
  .btn-instagram:focus, .btn-instagram.focus {
    box-shadow: 0 0 0 3px rgba(63, 114, 155, 0.5); }
  .btn-instagram.disabled, .btn-instagram:disabled {
    background-color: #3f729b;
    border-color: #3f729b; }
  .btn-instagram:active, .btn-instagram.active, .show > .btn-instagram.dropdown-toggle {
    background-color: #345e80;
    background-image: none;
    border-color: #305777; }

.btn-linkedin {
  color: #fff;
  background-color: #007bb6;
  border-color: #007bb6; }
  .btn-linkedin:hover {
    color: #fff;
    background-color: #006190;
    border-color: #005983; }
  .btn-linkedin:focus, .btn-linkedin.focus {
    box-shadow: 0 0 0 3px rgba(0, 123, 182, 0.5); }
  .btn-linkedin.disabled, .btn-linkedin:disabled {
    background-color: #007bb6;
    border-color: #007bb6; }
  .btn-linkedin:active, .btn-linkedin.active, .show > .btn-linkedin.dropdown-toggle {
    background-color: #006190;
    background-image: none;
    border-color: #005983; }

.btn-microsoft {
  color: #fff;
  background-color: #2672ec;
  border-color: #2672ec; }
  .btn-microsoft:hover {
    color: #fff;
    background-color: #135fd9;
    border-color: #125acd; }
  .btn-microsoft:focus, .btn-microsoft.focus {
    box-shadow: 0 0 0 3px rgba(38, 114, 236, 0.5); }
  .btn-microsoft.disabled, .btn-microsoft:disabled {
    background-color: #2672ec;
    border-color: #2672ec; }
  .btn-microsoft:active, .btn-microsoft.active, .show > .btn-microsoft.dropdown-toggle {
    background-color: #135fd9;
    background-image: none;
    border-color: #125acd; }

.btn-odnoklassniki {
  color: #fff;
  background-color: #f4731c;
  border-color: #f4731c; }
  .btn-odnoklassniki:hover {
    color: #fff;
    background-color: #df600b;
    border-color: #d35b0a; }
  .btn-odnoklassniki:focus, .btn-odnoklassniki.focus {
    box-shadow: 0 0 0 3px rgba(244, 115, 28, 0.5); }
  .btn-odnoklassniki.disabled, .btn-odnoklassniki:disabled {
    background-color: #f4731c;
    border-color: #f4731c; }
  .btn-odnoklassniki:active, .btn-odnoklassniki.active, .show > .btn-odnoklassniki.dropdown-toggle {
    background-color: #df600b;
    background-image: none;
    border-color: #d35b0a; }

.btn-openid {
  color: #111;
  background-color: #f7931e;
  border-color: #f7931e; }
  .btn-openid:hover {
    color: #111;
    background-color: #e78008;
    border-color: #da7908; }
  .btn-openid:focus, .btn-openid.focus {
    box-shadow: 0 0 0 3px rgba(247, 147, 30, 0.5); }
  .btn-openid.disabled, .btn-openid:disabled {
    background-color: #f7931e;
    border-color: #f7931e; }
  .btn-openid:active, .btn-openid.active, .show > .btn-openid.dropdown-toggle {
    background-color: #e78008;
    background-image: none;
    border-color: #da7908; }

.btn-pinterest {
  color: #fff;
  background-color: #cb2027;
  border-color: #cb2027; }
  .btn-pinterest:hover {
    color: #fff;
    background-color: #aa1b21;
    border-color: #9f191f; }
  .btn-pinterest:focus, .btn-pinterest.focus {
    box-shadow: 0 0 0 3px rgba(203, 32, 39, 0.5); }
  .btn-pinterest.disabled, .btn-pinterest:disabled {
    background-color: #cb2027;
    border-color: #cb2027; }
  .btn-pinterest:active, .btn-pinterest.active, .show > .btn-pinterest.dropdown-toggle {
    background-color: #aa1b21;
    background-image: none;
    border-color: #9f191f; }

.btn-reddit {
  color: #111;
  background-color: #eff7ff;
  border-color: #eff7ff; }
  .btn-reddit:hover {
    color: #111;
    background-color: #c9e4ff;
    border-color: #bcdeff; }
  .btn-reddit:focus, .btn-reddit.focus {
    box-shadow: 0 0 0 3px rgba(239, 247, 255, 0.5); }
  .btn-reddit.disabled, .btn-reddit:disabled {
    background-color: #eff7ff;
    border-color: #eff7ff; }
  .btn-reddit:active, .btn-reddit.active, .show > .btn-reddit.dropdown-toggle {
    background-color: #c9e4ff;
    background-image: none;
    border-color: #bcdeff; }

.btn-soundcloud {
  color: #fff;
  background-color: #ff5500;
  border-color: #ff5500; }
  .btn-soundcloud:hover {
    color: #fff;
    background-color: #d94800;
    border-color: #cc4400; }
  .btn-soundcloud:focus, .btn-soundcloud.focus {
    box-shadow: 0 0 0 3px rgba(255, 85, 0, 0.5); }
  .btn-soundcloud.disabled, .btn-soundcloud:disabled {
    background-color: #ff5500;
    border-color: #ff5500; }
  .btn-soundcloud:active, .btn-soundcloud.active, .show > .btn-soundcloud.dropdown-toggle {
    background-color: #d94800;
    background-image: none;
    border-color: #cc4400; }

.btn-tumblr {
  color: #fff;
  background-color: #2c4762;
  border-color: #2c4762; }
  .btn-tumblr:hover {
    color: #fff;
    background-color: #203448;
    border-color: #1c2e3f; }
  .btn-tumblr:focus, .btn-tumblr.focus {
    box-shadow: 0 0 0 3px rgba(44, 71, 98, 0.5); }
  .btn-tumblr.disabled, .btn-tumblr:disabled {
    background-color: #2c4762;
    border-color: #2c4762; }
  .btn-tumblr:active, .btn-tumblr.active, .show > .btn-tumblr.dropdown-toggle {
    background-color: #203448;
    background-image: none;
    border-color: #1c2e3f; }

.btn-twitter {
  color: #fff;
  color: #fff;
  background-color: #1DA1F2;
  border-color: #1DA1F2; }
  .btn-twitter:hover {
    color: #fff;
    background-color: #0d8ddc;
    border-color: #0c85d0; }
  .btn-twitter:focus, .btn-twitter.focus {
    box-shadow: 0 0 0 3px rgba(29, 161, 242, 0.5); }
  .btn-twitter.disabled, .btn-twitter:disabled {
    background-color: #1DA1F2;
    border-color: #1DA1F2; }
  .btn-twitter:active, .btn-twitter.active, .show > .btn-twitter.dropdown-toggle {
    background-color: #0d8ddc;
    background-image: none;
    border-color: #0c85d0; }

.btn-vimeo {
  color: #fff;
  background-color: #1ab7ea;
  border-color: #1ab7ea; }
  .btn-vimeo:hover {
    color: #fff;
    background-color: #139ecb;
    border-color: #1295bf; }
  .btn-vimeo:focus, .btn-vimeo.focus {
    box-shadow: 0 0 0 3px rgba(26, 183, 234, 0.5); }
  .btn-vimeo.disabled, .btn-vimeo:disabled {
    background-color: #1ab7ea;
    border-color: #1ab7ea; }
  .btn-vimeo:active, .btn-vimeo.active, .show > .btn-vimeo.dropdown-toggle {
    background-color: #139ecb;
    background-image: none;
    border-color: #1295bf; }

.btn-vk {
  color: #fff;
  background-color: #587ea3;
  border-color: #587ea3; }
  .btn-vk:hover {
    color: #fff;
    background-color: #4b6b8a;
    border-color: #466482; }
  .btn-vk:focus, .btn-vk.focus {
    box-shadow: 0 0 0 3px rgba(88, 126, 163, 0.5); }
  .btn-vk.disabled, .btn-vk:disabled {
    background-color: #587ea3;
    border-color: #587ea3; }
  .btn-vk:active, .btn-vk.active, .show > .btn-vk.dropdown-toggle {
    background-color: #4b6b8a;
    background-image: none;
    border-color: #466482; }

.btn-yahoo {
  color: #fff;
  background-color: #720e9e;
  border-color: #720e9e; }
  .btn-yahoo:hover {
    color: #fff;
    background-color: #590b7b;
    border-color: #500a6f; }
  .btn-yahoo:focus, .btn-yahoo.focus {
    box-shadow: 0 0 0 3px rgba(114, 14, 158, 0.5); }
  .btn-yahoo.disabled, .btn-yahoo:disabled {
    background-color: #720e9e;
    border-color: #720e9e; }
  .btn-yahoo:active, .btn-yahoo.active, .show > .btn-yahoo.dropdown-toggle {
    background-color: #590b7b;
    background-image: none;
    border-color: #500a6f; }

.btn-stripe {
  color: #fff;
  background-color: #1275FF;
  border-color: #1275FF; }
  .btn-stripe:hover {
    color: #fff;
    background-color: #0062eb;
    border-color: #005dde; }
  .btn-stripe:focus, .btn-stripe.focus {
    box-shadow: 0 0 0 3px rgba(18, 117, 255, 0.5); }
  .btn-stripe.disabled, .btn-stripe:disabled {
    background-color: #1275FF;
    border-color: #1275FF; }
  .btn-stripe:active, .btn-stripe.active, .show > .btn-stripe.dropdown-toggle {
    background-color: #0062eb;
    background-image: none;
    border-color: #005dde; }

.btn-amazon {
  color: #232F3E;
  color: #111;
  background-color: #FF9900;
  border-color: #FF9900; }
  .btn-amazon:hover {
    color: #111;
    background-color: #d98200;
    border-color: #cc7a00; }
  .btn-amazon:focus, .btn-amazon.focus {
    box-shadow: 0 0 0 3px rgba(255, 153, 0, 0.5); }
  .btn-amazon.disabled, .btn-amazon:disabled {
    background-color: #FF9900;
    border-color: #FF9900; }
  .btn-amazon:active, .btn-amazon.active, .show > .btn-amazon.dropdown-toggle {
    background-color: #d98200;
    background-image: none;
    border-color: #cc7a00; }

.btn-patreon {
  color: #052D49;
  color: #fff;
  background-color: #F96854;
  border-color: #F96854; }
  .btn-patreon:hover {
    color: #fff;
    background-color: #f8472f;
    border-color: #f73c23; }
  .btn-patreon:focus, .btn-patreon.focus {
    box-shadow: 0 0 0 3px rgba(249, 104, 84, 0.5); }
  .btn-patreon.disabled, .btn-patreon:disabled {
    background-color: #F96854;
    border-color: #F96854; }
  .btn-patreon:active, .btn-patreon.active, .show > .btn-patreon.dropdown-toggle {
    background-color: #f8472f;
    background-image: none;
    border-color: #f73c23; }

.btn-untappd {
  color: #111;
  background-color: #ffc000;
  border-color: #ffc000; }
  .btn-untappd:hover {
    color: #111;
    background-color: #d9a300;
    border-color: #cc9a00; }
  .btn-untappd:focus, .btn-untappd.focus {
    box-shadow: 0 0 0 3px rgba(255, 192, 0, 0.5); }
  .btn-untappd.disabled, .btn-untappd:disabled {
    background-color: #ffc000;
    border-color: #ffc000; }
  .btn-untappd:active, .btn-untappd.active, .show > .btn-untappd.dropdown-toggle {
    background-color: #d9a300;
    background-image: none;
    border-color: #cc9a00; }

.btn-gitlab {
  color: #fff;
  background-color: #de7e00;
  border-color: #de7e00; }
  .btn-gitlab:hover {
    color: #fff;
    background-color: #b86800;
    border-color: #ab6100; }
  .btn-gitlab:focus, .btn-gitlab.focus {
    box-shadow: 0 0 0 3px rgba(222, 126, 0, 0.5); }
  .btn-gitlab.disabled, .btn-gitlab:disabled {
    background-color: #de7e00;
    border-color: #de7e00; }
  .btn-gitlab:active, .btn-gitlab.active, .show > .btn-gitlab.dropdown-toggle {
    background-color: #b86800;
    background-image: none;
    border-color: #ab6100; }

.btn-whatsapp {
  color: #fff;
  color: #fff;
  background-color: #25D366;
  border-color: #25D366; }
  .btn-whatsapp:hover {
    color: #fff;
    background-color: #1fb256;
    border-color: #1da851; }
  .btn-whatsapp:focus, .btn-whatsapp.focus {
    box-shadow: 0 0 0 3px rgba(37, 211, 102, 0.5); }
  .btn-whatsapp.disabled, .btn-whatsapp:disabled {
    background-color: #25D366;
    border-color: #25D366; }
  .btn-whatsapp:active, .btn-whatsapp.active, .show > .btn-whatsapp.dropdown-toggle {
    background-color: #1fb256;
    background-image: none;
    border-color: #1da851; }

/*# sourceMappingURL=bootstrap-social.css.map */


================================================
FILE: app/assets/stylesheets/general.css
================================================
body {
  padding-top: 70px;
}


================================================
FILE: app/assets/stylesheets/home.scss.erb
================================================
// Place all the styles related to the Home controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

:root {
  --input-padding-x: .75rem;
  --input-padding-y: .75rem;
}

html,
body {
  height: 100%;
}
@import url(https://fonts.googleapis.com/css?family=Roboto:300);
@import url(https://fonts.googleapis.com/css?family=Lato:300);

.home {
  display: -ms-flexbox;
  display: -webkit-box;
  display: flex;
  -ms-flex-align: center;
  -ms-flex-pack: center;
  -webkit-box-align: center;
  align-items: center;
  -webkit-box-pack: center;
  justify-content: center;
  padding-top: 40px;
  padding-bottom: 40px;
  font-family: 'Roboto', sans-serif;
  font-size: 16px;
  font-weight: 300;
  color: white;
  line-height: 30px;
  text-align: center;
  background-image: image-url('bg.jpeg');
  background-size: cover;
  background-repeat: no-repeat;
  background-color: #444444;
  background-position: 50%;
}

.form-signin {
  width: 100%;
  max-width: 420px;
  padding: 15px;
  margin: 0 auto;
}

.token-qr table {
  border-width: 0;
  border-style: none;
  border-color: #0000ff;
  border-collapse: collapse;
}

.token-qr td {
  border-left: solid 5px #000;
  padding: 0; 
  margin: 0; 
  width: 0px; 
  height: 5px; 
}

.token-qr td.black { border-color: #000; }
.token-qr td.white { border-color: #fff; }



================================================
FILE: app/assets/stylesheets/profile.css
================================================
/* Everything but the jumbotron gets side spacing for mobile first views */

.container-profile {
  max-width: 730px;
  min-width: 768px;
}


================================================
FILE: app/assets/stylesheets/scaffolds.scss
================================================
body {
  background-color: #fff;
  color: #333;
  font-family: verdana, arial, helvetica, sans-serif;
  font-size: 13px;
  line-height: 18px;
}

p, ol, ul, td {
  font-family: verdana, arial, helvetica, sans-serif;
  font-size: 13px;
  line-height: 18px;
}

pre {
  background-color: #eee;
  padding: 10px;
  font-size: 11px;
}

a {
  color: #000;

  &:visited {
    color: #666;
  }

  &:hover {
    color: #fff;
    background-color: #000;
  }
}

div {
  &.field, &.actions {
    margin-bottom: 10px;
  }
}

#notice {
  color: green;
}

.field_with_errors {
  padding: 2px;
  background-color: red;
  display: table;
}

#error_explanation {
  width: 450px;
  border: 2px solid red;
  padding: 7px;
  padding-bottom: 0;
  margin-bottom: 20px;
  background-color: #f0f0f0;

  h2 {
    text-align: left;
    font-weight: bold;
    padding: 5px 5px 5px 15px;
    font-size: 12px;
    margin: -7px;
    margin-bottom: 0px;
    background-color: #c00;
    color: #fff;
  }

  ul li {
    font-size: 12px;
    list-style: square;
  }
}


================================================
FILE: app/clients/data_dog_client.rb
================================================
class DataDogClient
  include HTTParty
  base_uri 'https://api.datadoghq.com/api/v1'

  def initialize(app_key, api_key)
    @base_path = 'https://api.datadoghq.com/api/v1'
    @app_key = app_key
    @api_key = api_key
    @headers = {
      'Content-Type' => 'application/json',
      'Accept' => 'application/json',
    }
  end

  def get_user(email)
    url = append_auth("/user/#{email}")
    response = self.class.get(url, headers: @headers)
    if response.success?
      JSON.parse(response.body)['user']
    else
      {}
    end
  end

  def new_user(email)
    url = append_auth('/user')
    response = self.class.post(url, body: { handle: email }.to_json, headers: @headers)
    if response.success?
      JSON.parse(response.body)['user']
    else
      {}
    end
  end

  def activate_user(email)
    url = append_auth("/user/#{email}")
    response = self.class.put(url, body: { email: email, disabled: false }.to_json, headers: @headers)
    if response.success?
      JSON.parse(response.body)['user']
    else
      {}
    end
  end

  def deactivate_user(email)
    url = append_auth("/user/#{email}")
    response = self.class.put(url, body: { email: email, disabled: true }.to_json, headers: @headers)
    if response.success?
      JSON.parse(response.body)['user']
    else
      {}
    end
  end

  private

  def append_auth(str)
    auth_str = "api_key=#{@api_key}&application_key=#{@app_key}"
    str += str.include?('?') ? "&#{auth_str}" : "?#{auth_str}"
    str
  end
end


================================================
FILE: app/controllers/admin_controller.rb
================================================
class AdminController < ApplicationController
  def index; end
end


================================================
FILE: app/controllers/api/v1/base_controller.rb
================================================
class ::Api::V1::BaseController < ActionController::Base
  protect_from_forgery with: :null_session
  before_action :authenticate_user_from_token!

  def authenticate_user_from_token!
    if get_token.nil? || !AccessToken.valid_token(get_token)
      raise_unauthorized
    end
  end

  protected

  def current_user
    access_token = AccessToken.find_token(get_token)
    access_token.user
  end

  private

  def get_token
    if params.key?(:access_token)
      params[:access_token]
    elsif params.key?(:token)
      params[:token]
    elsif request.headers.key?(:Authorization)
      request.headers[:Authorization].split(' ').last
    end
  end

  def raise_unauthorized
    head :unauthorized
  end
end


================================================
FILE: app/controllers/api/v1/endpoints_controller.rb
================================================
class ::Api::V1::EndpointsController < ::Api::V1::BaseController
  before_action :authorize_user

  def create
    endpoint = Endpoint.new(endpoint_param)
    if endpoint.save
      render json: {
        id: endpoint.id,
        path: endpoint.path,
        method: endpoint.method,
      }
    else
      render json: { status: endpoint.errors }, status: :unprocessable_entity
    end
  end

  def add_group
    endpoint = Endpoint.find_by(id: params[:id])
    if endpoint.nil?
      return head :not_found
    end

    group = Group.find_by(group_param)
    group_endpoint = GroupEndpoint.new(group: group, endpoint: endpoint)

    if group_endpoint.save
      render json: {}
    else
      head :unprocessable_entity
    end
  end

  private

  def authorize_user
    unless current_user.admin?
      head :forbidden
    end
  end

  def group_param
    params.require(:group).permit(:id)
  end

  def endpoint_param
    params.require(:endpoint).permit(:path, :method)
  end
end


================================================
FILE: app/controllers/api/v1/groups_controller.rb
================================================
class ::Api::V1::GroupsController < ::Api::V1::BaseController
  def create
    if current_user.admin?
      @group = Group.new(group_params)
      if @group.save
        render json: {
          id: @group.id,
          name: @group.name,
        }, status: :ok
      else
        is_taken = @group.errors.details[:name].select { |x| x[:error] == :taken }

        if !is_taken.blank?
          existing_group = Group.find_by(name: @group.name)
          render json: {
            status: 'group already exist',
            id: existing_group.id,
            name: existing_group.name,
          }, status: :unprocessable_entity
        else
          render json: {
            status: 'error',
          }, status: :unprocessable_entity
        end
      end
    end
  end

  def add_user
    @group = Group.find_by(id: params[:id])
    return head :not_found unless @group.present?

    return raise_unauthorized unless current_user.admin? || @group.admin?(current_user)

    user = User.find_by(id: params[:user_id])
    return head :unprocessable_entity unless user.present?

    expiration_date = params[:expiration_date]
    @group.add_user_with_expiration(params[:user_id], expiration_date)
    head :no_content
  end

  private

  def group_params
    params.require(:group).permit(:name)
  end
end


================================================
FILE: app/controllers/api/v1/users_controller.rb
================================================
class ::Api::V1::UsersController < ::Api::V1::BaseController
  before_action :set_user, only: %i[show update]

  def create
    return head :forbidden unless current_user.admin?

    user = user_params
    if User.add_temp_user user[:name], user[:email]
      render json: { status: 'created' }, status: :ok
    else
      render json: { status: 'error' }, status: :unprocessable_entity
    end
  end

  def show
    if @user.present?
      user_attrs = %w(
        email id uid name active admin home_dir shell public_key user_login_id
        product_name
      )
      data = @user.attributes.select { |k, _v| user_attrs.include?(k) }
      data['groups'] = @user.groups.map { |g| { 'id' => g.gid, 'name' => g.name } }
      render json: data
    else
      head :not_found
    end
  end

  def deactivate
    endpoint = Endpoint.find_by(path: api_v1_deactivate_user_path(':id'), method: 'PATCH')
    if !current_user.admin? && !current_user.permitted_endpoint?(endpoint)
      return head :forbidden
    end

    user = User.find_by(id: params[:id])
    return head :not_found if user.nil?

    user.active = false
    if user.save
      head :no_content
    else
      head :unprocessable_entity
    end
  end

  def update
    return raise_unauthorized unless current_user.admin? || current_user == @user

    render json: { success: @user.update_profile(user_params) }
  end

  private

  def set_user
    @user = if params.key?('email')
              User.find_by_email(params['email'])
            elsif params.key?('uid')
              User.find_by_uid(params['uid'])
            elsif params.key?('username')
              if params.key?('active')
                is_active = [1, true, '1', 'true'].include?(params['active'])
                User.where(user_login_id: params['username'], active: is_active).take
              else
                User.find_by_user_login_id(params['username'])
              end
            end
  end

  def user_params
    if params.key?(:user)
      params.require(:user).permit(:name, :email, :public_key, :product_name)
    else
      params.permit(:name, :email, :public_key, :product_name)
    end
  end
end


================================================
FILE: app/controllers/api/v1/vpns_controller.rb
================================================
class ::Api::V1::VpnsController < ::Api::V1::BaseController
  before_action :set_vpn, only: [:assign_group]

  def create
    if current_user.admin?
      @vpn = Vpn.new(vpn_params)
      @vpn.uuid = SecureRandom.uuid
      if @vpn.save
        render json: {
          id: @vpn.id,
          name: @vpn.name,
          host_name: @vpn.host_name,
          ip_address: @vpn.ip_address,
        }, status: :ok
      else
        render json: { status: 'error' }, status: :unprocessable_entity
      end
    end
  end

  def assign_group
    if current_user.admin?
      @vpn.groups.delete_all
      @vpn.groups << Group.where(id: params[:group_id]).first
      render json: { status: 'group assigned' }, status: :ok
    end
  end

  private

  def set_vpn
    @vpn = Vpn.find(params[:id])
  end

  def vpn_params
    params.require(:vpn).permit(:name, :host_name, :ip_address)
  end
end


================================================
FILE: app/controllers/api_resources_controller.rb
================================================
class ApiResourcesController < ApplicationController
  before_action :set_api_resource, only: %i[show edit update destroy regenerate_access_key]
  before_action :authenticate_user!, except: [:authenticate]
  before_action :authorize_user, only: %i[regenerate_access_key destroy update]

  # GET /api_resources
  # GET /api_resources.json
  def index
    @api_resources = ApiResource.where(user: current_user) if !current_user.admin
    @api_resources = ApiResource.all if current_user.admin
  end

  # GET /api_resources/1
  # GET /api_resources/1.json
  def show; end

  def authenticate
    # this authenticates and tells whether users is able to access this api or not
    if ApiResource.authenticate(params[:access_key], params[:access_token])
      render  json: { result: 0 }, status: :ok
    else
      render  json: { result: 1 }, status: 401
    end
  end

  # GET /api_resources/new
  def new
    @api_resource = ApiResource.new
  end

  # GET /api_resources/1/edit
  def edit; end

  # POST /api_resources
  # POST /api_resources.json
  def create
    @api_resource = ApiResource.new(api_resource_params)
    @api_resource.access_key = ROTP::Base32.random_base32
    @api_resource.user = current_user
    group = Group.create name: "#{@api_resource.name}_api_group"
    @api_resource.group = group
    group.add_admin current_user
    group.save!
    respond_to do |format|
      if @api_resource.save
        format.html { redirect_to api_resource_path(@api_resource.id), notice: 'Api resource was successfully created.', flash: { access_key: @api_resource.access_key } }
        format.json { render :show, status: :created, location: @api_resource }
      else
        format.html { render :new }
        format.json { render json: @api_resource.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /api_resources/1
  # PATCH/PUT /api_resources/1.json
  def update
    respond_to do |format|
      if @api_resource.update(api_resource_params)
        format.html { redirect_to api_resources_path, notice: 'Api resource was successfully updated.' }
        format.json { render :show, status: :ok, location: @api_resource }
      else
        format.html { render :edit }
        format.json { render json: @api_resource.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /api_resources/1
  # DELETE /api_resources/1.json
  def destroy
    @api_resource.group.destroy if @api_resource.group.present?
    @api_resource.destroy
    respond_to do |format|
      format.html { redirect_to api_resources_url, notice: 'Api resource was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  # GET /api_resources/q=.json
  def search
    if params[:exact]
      @api_resources = ApiResource.where('name LIKE ?', params[:q].to_s)
    else
      @api_resources = ApiResource.where('name LIKE ?', "%#{params[:q]}%")
    end
    @api_resources = @api_resources.order('name ASC').limit(20)
    data = @api_resources.map { |group| { id: group.id, name: group.name } }
    render json: data
  end

  # GET /api_resources/:id/regenerate_access_key
  def regenerate_access_key
    @api_resource.access_key = ROTP::Base32.random_base32
    respond_to do |format|
      if @api_resource.save
        format.html { redirect_to api_resource_path(@api_resource.id), notice: 'Access key regenerated.', flash: { access_key: @api_resource.access_key } }
        format.json { render :show, status: :ok, location: @api_resource }
      else
        format.html { redirect_to api_resource_path(@api_resource.id), notice: 'Access key failed to regenerate.' }
        format.json { render json: @api_resource.errors, status: :unprocessable_entity }
      end
    end
  end

  private

  # Use callbacks to share common setup or constraints between actions.
  def set_api_resource
    @api_resource = ApiResource.find(params[:id])
  end

  # Never trust parameters from the scary internet, only allow the white list through.
  def api_resource_params
    params.require(:api_resource).permit(:name, :access_key, :description, :user_id, :group_id)
  end

  def authorize_user
    unless current_user.admin? || current_user == @api_resource.user
      respond_to do |format|
        format.html { redirect_to api_resources_url, notice: 'Unauthorized access' }
        format.json { render json: {}, status: :unauthorized }
      end
    end
  end
end


================================================
FILE: app/controllers/application_controller.rb
================================================
class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception

  def render_404
    respond_to do |format|
      format.html { render file: "#{Rails.root}/public/404", layout: false, status: :not_found }
      format.xml  { head :not_found }
      format.any  { head :not_found }
    end
  end

  def authenticate_access_token!
    unless AccessToken.valid_token(params[:token])
      render_error(['Unauthorized'], :unauthorized)
    end
  end

  def render_error(errors, status = 400)
    render 'common/errors', locals: { errors: errors }, status: status
  end
end


================================================
FILE: app/controllers/concerns/.keep
================================================


================================================
FILE: app/controllers/groups_controller.rb
================================================
class GroupsController < ApplicationController
  before_action :set_group, only: %i[show edit update destroy
                                     add_user add_machine add_vpn add_admin
                                     remove_admin delete_user delete_vpn delete_machine]
  before_action :authenticate_user!

  def index
    @groups = []
    @group_search = params[:group_search]
    if current_user.admin && @group_search.present?
      @groups = Group.where('name LIKE ?', "%#{@group_search}%")
    elsif current_user.group_admin? && !current_user.admin
      @groups = GroupAdmin.where(user_id: current_user.id).map(&:group)
    end
  end

  def create
    if current_user.admin?
      @group = Group.new(group_params)
      respond_to do |format|
        if @group.save
          format.html { redirect_to group_path(@group), notice: 'Group was successfully created.' }
          format.json { render status: :created, json: "#{@group.name}host created" }
        else
          format.html { redirect_to groups_path, notice: "Can't save '#{group_params[:name]}'" }
          format.json { render status: :error, json: "#{@group.name} not created" }
        end
      end
    else
      format.html { redirect_to groups_path, notice: "Can't save '#{group_params[:name]}'" }
      format.json { render status: :error, json: "#{@group.name} not created" }
    end
  end

  def new
    @group = Group.new
  end

  def show
    @group_users = User.
      select(%Q{
        users.id AS id,
        name,
        email,
        active,
        group_associations.created_at AS join_date,
        group_associations.expiration_date AS group_expiration_date
      }).
      joins('LEFT OUTER JOIN group_associations ON users.id = group_associations.user_id').
      where('group_associations.group_id = ?', @group.id)
  end

  def delete_machine
    @machine = HostMachine.find(params[:host_machine_id])
    if current_user.admin?
      @machine.groups.delete(@group)
    end

    redirect_to group_path @group
  end

  def delete_user
    if current_user.admin? || @group.admin?(current_user)
      @user = User.find(params[:user_id])

      if @user.email.split('@').first != @group.name
        @group.remove_user @user
      end

    end
    redirect_to group_path(@group, anchor: 'group_members')
  end

  def add_user
    if current_user.admin? || @group.admin?(current_user)
      user = User.find(params[:user_id])
      begin
        expiration_date = expiration_date_param
      rescue ArgumentError
        return respond_to do |format|
          format.html { redirect_to group_path(@group), notice: 'Expiration date is wrong' }
        end
      end
      @group.add_user_with_expiration(user.id, expiration_date) if user.present?
    end

    respond_to do |format|
      format.html do
        redirect_to group_path(@group, anchor: 'group_members')
      end
    end
  end

  def add_machine
    if current_user.admin?
      machine = HostMachine.find(params[:machine_id])
      machine.groups << @group if machine.present? && machine.groups.find_by_id(@group.id).blank?
      machine.save!
    end

    respond_to do |format|
      format.html do
        redirect_to group_path @group
      end
    end
  end

  def add_admin
    if current_user.admin?
      GroupAdmin.find_or_create_by(group_id: @group.id, user_id: params[:user_id])
    end

    respond_to do |format|
      format.html do
        redirect_to group_path @group
      end
    end
  end

  def remove_admin
    if current_user.admin?
      GroupAdmin.delete(params[:group_admin_id])
    end

    respond_to do |format|
      format.html do
        redirect_to group_path @group
      end
    end
  end

  def add_vpn
    if current_user.admin?
      VpnGroupAssociation.find_or_create_by(group_id: @group.id, vpn_id: params[:vpn_id])
    end

    respond_to do |format|
      format.html do
        redirect_to group_path @group
      end
    end
  end

  def delete_vpn
    return unless current_user.admin? || @group.group_admin.user == current_user

    VpnGroupAssociation.where(group_id: @group.id, vpn_id: params[:vpn_id]).destroy_all
    VpnGroupUserAssociation.where(group_id: @group.id, vpn_id: params[:vpn_id]).destroy_all

    respond_to do |format|
      format.html do
        redirect_to group_path @group
      end
    end
  end

  def add_group
    user_id = params[:id]
    if current_user.admin?
      begin
        expiration_date = expiration_date_param
      rescue ArgumentError
        response_message = 'Expiration date is wrong'
        return redirect_to user_path, notice: response_message
      end
      group = Group.find(params[:group_id])
      group.add_user_with_expiration(user_id, expiration_date)
    end
    redirect_to user_path
  end

  def delete_group
    @user = User.find(params[:user_id])
    if current_user.admin?
      group = Group.find(params[:id])

      if @user.email.split('@').first != group.name
        @user.groups.delete(group)
      end

    end

    redirect_to user_path(@user)
  end

  def list
    @groups = []
    @group_search = params[:group_search]
    return unless @group_search.present?

    if current_user.admin?
      @groups = Group.where('name LIKE ?', "%#{@group_search}%")
    elsif current_user.group_admin?
      @groups = GroupAdmin.where(user_id: current_user.id).map(&:group)
    end
  end

  def search
    @groups = Group.
      where('name LIKE ?', "%#{params[:q]}%").
      order('name ASC').
      limit(20)
    data = @groups.map { |group| { id: group.id, name: group.name } }
    render json: data
  end

  private

  # Use callbacks to share common setup or constraints between actions.
  def set_group
    @group = Group.find(params[:id])
  end

  # Never trust parameters from the scary internet, only allow the white list through.
  def group_params
    params.require(:group).permit(:name)
  end

  def expiration_date_param
    expiration_date = params[:expiration_date]
    return nil if expiration_date.nil? || expiration_date.empty?

    Date.parse(expiration_date, '%Y-%m-%d')
  end
end


================================================
FILE: app/controllers/home_controller.rb
================================================
class HomeController < ApplicationController

  before_action :check_signed_in

  def check_signed_in
    redirect_to profile_path if signed_in?
  end

  def index; end
end


================================================
FILE: app/controllers/host_controller.rb
================================================
class HostController < ApplicationController
  before_action :authenticate_user!
  def add_host
    @user = User.find(params[:id])
    if current_user.admin?
      host = Host.new
      host.user = @user
      host.host_pattern = params[:host_pattern]
      host.save!

    end
    redirect_to user_path
  end

  def delete_host
    @user = User.find(params[:user_id])
    if current_user.admin?
      @host = Host.find(params[:id])
      @host.deleted_by = current_user.id
      @host.save!
      @host.destroy
    end

    redirect_to user_path(@user)
  end
end


================================================
FILE: app/controllers/host_machine_groups_controller.rb
================================================
class HostMachineGroupsController < ApplicationController
  def show
    @host_machines = HostMachine.all
  end

  def create
    @host_machine = HostMachine.new(host_machine_params)
    respond_to do |format|
      @host_machine.save
      format.html { redirect_to :show, notice: 'host_machine was successfully created.' }
      format.json { render status: :created, json: "#{@host_machine.name}host created" }
    end
  end

  private

  # Use callbacks to share common setup or constraints between actions.
  def set_host_machine
    @host_machine = host_machine.find(params[:id])
  end

  # Never trust parameters from the scary internet, only allow the white list through.
  def host_machine_params
    params.require(:host_machine).permit(:name)
  end
end


================================================
FILE: app/controllers/host_machines_controller.rb
================================================
class HostMachinesController < ApplicationController
  before_action :set_host_machine, only: %i[add_group show edit update destroy delete_group]
  before_action :authenticate_user!
  before_action :authorize_user, only: %i[delete_group update]

  def index
    @title = 'Host'
    @host_machines = HostMachine.all
    @host_machines = []
    @host_machine_search = params[:host_machine_search]
    if @host_machine_search.present?
      if current_user.admin?
        @host_machines = HostMachine.where('name LIKE ?', "%#{@host_machine_search}%")
      end
    end
  end

  def create
    @host_machine = HostMachine.new(host_machine_params)
    respond_to do |format|
      if @host_machine.save
        format.html { redirect_to host_machines_path, notice: 'Host was successfully created.' }
        format.json { render status: :created, json: "#{@host_machine.name}host created" }
      else
        format.html { redirect_to host_machines_path, notice: "Can't save '#{host_machine_params[:name]}'" }
        format.json { render status: :error, json: "#{@host_machine.name} not created" }
      end
    end
  end

  def show
    @machine = @host_machine
    @groups = Group.all
  end

  def update
    @host_machine.update(default_admins: params[:host_machine][:default_admins])
    redirect_to host_machine_path @host_machine
  end

  def add_group
    @machine = @host_machine
    if current_user.admin?
      group = Group.find(params[:group_id])
      @machine.groups << group if @machine.present? && @machine.groups.find_by_id(group.id).blank?
      @machine.save!
    end

    respond_to do |format|
      format.html do
        redirect_to host_machine_path @host_machine
      end
    end
  end

  def delete_group
    group = Group.find(params[:group_id])
    @host_machine.groups.delete(group)
    @host_machine.save!
    redirect_to host_machine_path @host_machine
  end

  def search
    @host_machines = HostMachine.
      where('name LIKE ?', "%#{params[:q]}%").
      order('name ASC').
      limit(20)
    data = @host_machines.map { |host_machine| { id: host_machine.id, name: host_machine.name } }
    render json: data
  end

  private

  # Use callbacks to share common setup or constraints between actions.
  def set_host_machine
    @host_machine = HostMachine.find(params[:id])
  end

  # Never trust parameters from the scary internet, only allow the white list through.
  def host_machine_params
    params.require(:host_machine).permit(:name)
  end

  def authorize_user
    unless current_user.admin?
      redirect_to host_machines_path, notice: 'Unauthorized access'
    end
  end
end


================================================
FILE: app/controllers/nss_controller.rb
================================================
class NssController < ApplicationController
  skip_before_action :verify_authenticity_token, only: %i[add_host add_user_to_group]
  before_action :authenticate_access_token!, only: %i[add_host]

  def host
    token = AccessToken.valid_token params[:token]
    @response = nil
    if token
      @response = HostMachine.get_group_response(params[:name]) if params[:name].present?
      render json: @response
      return
    end

    host_machine = HostMachine.find_by(access_key: params[:token])
    sysadmins = host_machine.sysadmins if host_machine.present?
    if sysadmins.present? && sysadmins.count.positive?
      @response = Group.get_sysadmins_and_groups sysadmins, host_machine.default_admins
    end
    render json: @response
    nil
  end

  def add_host
    if params[:name].present?
      host = HostMachine.find_or_create_by(name: params[:name])
      host.default_admins = params[:default_admins]
      host.save
      host.add_host_group(params[:name])
      host.add_group(params[:group_name])
      render 'add_host', locals: { host: host }, format: :json
    else
      errors = ['Name can\'t be blank']
      if params.key?(:group_name) && params[:group_name].blank?
        errors << 'Group Name can\'t be blank'
      end
      render_error(errors)
    end
  end

  def group
    @response = REDIS_CACHE.get("#{GROUP_RESPONSE}:#{params[:token]}")
    @response = JSON.parse(@response) if @response.present?
    if @response.blank?
      host_machine = HostMachine.find_by(access_key: params[:token])
      sysadmins = host_machine.sysadmins if host_machine.present?
      if sysadmins.present? && sysadmins.count.positive?
        @response = Group.get_sysadmins_and_groups sysadmins, host_machine.default_admins
        REDIS_CACHE.set("#{GROUP_RESPONSE}:#{params[:token]}", @response.to_json)
        REDIS_CACHE.expire("#{GROUP_RESPONSE}:#{params[:token]}", REDIS_KEY_EXPIRY)
      end
    end

    render json: @response
  end

  def passwd
    @response = REDIS_CACHE.get("#{PASSWD_RESPONSE}:#{params[:token]}")
    @response = JSON.parse(@response) if @response.present?
    if @response.blank?
      host_machine = HostMachine.find_by(access_key: params[:token])
      sysadmins = host_machine.sysadmins if host_machine.present?
      if sysadmins.present? && sysadmins.count.positive?
        @response = User.get_sysadmins sysadmins
        REDIS_CACHE.set("#{PASSWD_RESPONSE}:#{params[:token]}", @response.to_json)
        REDIS_CACHE.expire("#{PASSWD_RESPONSE}:#{params[:token]}", REDIS_KEY_EXPIRY)
      end
    end
    render json: @response
  end

  def shadow
    token = AccessToken.valid_token params[:token]
    @response = nil

    if token
      name = params[:name]

      if name.present?
        @response = REDIS_CACHE.get(SHADOW_NAME_RESPONSE + name)
        if @response.blank?
          @response = User.get_shadow_name_response(name).to_json
          REDIS_CACHE.set(SHADOW_NAME_RESPONSE + name, @response)
          REDIS_CACHE.expire(SHADOW_NAME_RESPONSE + name, REDIS_KEY_EXPIRY)
        end
      else
        @response = REDIS_CACHE.get(SHADOW_ALL_RESPONSE)
        if @response.blank?
          @response = User.get_all_shadow_response.to_json
          REDIS_CACHE.set(SHADOW_ALL_RESPONSE, @response)
          REDIS_CACHE.expire(SHADOW_ALL_RESPONSE, REDIS_KEY_EXPIRY)
        end
      end
    end
    render json: @response
  end

  def groups_list
    token = AccessToken.valid_token params[:token]
    if token
      user = User.get_user(params[:email].split('@').first)
      if user.blank?
        render json: { success: false }
      else
        groups = user.blank? ? [] : user.group_names_list
        render json: { success: true, groups: groups }
      end
    else
      render json: { success: false }
    end
  end
end


================================================
FILE: app/controllers/organisations_controller.rb
================================================
class OrganisationsController < ApplicationController
  before_action :authorize_user, except: [:index]
  before_action :load_org, only: %i(
    config_saml_app update show setup_saml save_config_saml_app
    remove_user_saml_app add_user_saml_app
  )
  before_action :validate_app_name, only: %i(
    config_saml_app save_config_saml_app
    remove_user_saml_app add_user_saml_app
  )

  def index
    render :index, locals: { org_list: Organisation.all }
  end

  def new
    render :new, locals: { org: Organisation.new }
  end

  def config_saml_app
    app_name = params[:app_name]
    saml_app = app_name.titleize.constantize.new(@org.id)
    config = saml_app.config
    users = config.persisted? ? config.group.users : []
    render :config_saml_app, locals: {
      org: @org,
      saml_config: config,
      app_name: app_name,
      users: users,
    }
  end

  def save_config_saml_app
    app_name = params[:app_name]
    saml_app_config = params[:saml_app_config]
    saml_app = app_name.titleize.constantize.new(@org.id)
    saml_app.save_config(saml_app_config[:sso_url], params[:config])
    flash[:success] = 'Configuration saved successfully'
    redirect_to organisation_config_saml_app_path(
      app_name: app_name,
      organisation_id: @org.id
    )
  end

  def remove_user_saml_app
    app_name = params[:app_name]
    saml_app = app_name.titleize.constantize.new(@org.id)
    if saml_app.remove_user(params[:email])
      flash[:success] = 'User removed successfullly'
    else
      flash[:error] = 'Issue removing the user'
    end
    redirect_to organisation_config_saml_app_path
  end

  def add_user_saml_app
    app_name = params[:app_name]
    saml_app = app_name.titleize.constantize.new(@org.id)
    if saml_app.add_user(params[:email])
      flash[:success] = 'User added successfullly'
    else
      flash[:error] = 'Issue adding the user'
    end
    redirect_to organisation_config_saml_app_path
  end

  def create
    org = Organisation.setup(organisation_params.to_h || {})
    if org.errors.blank?
      flash[:success] = 'Successfully created organisation'
      redirect_to organisations_path
    else
      flash[:errors] = org.errors.full_messages
      render :new, locals: { org: org }
    end
  end

  def update
    @org.update_profile(organisation_params.to_h || {})
    if @org.errors.blank?
      flash[:success] = 'Successfully updated organisation'
      redirect_to organisations_path
    else
      flash[:errors] = @org.errors.full_messages
      render :show, locals: { org: @org }
    end
  end

  def show
    render :show, locals: { org: @org }
  end

  def setup_saml
    if @org.saml_setup?
      flash[:errors] = 'SAML Certificates Already Setup'
    else
      @org.setup_saml_certs
      flash[:success] = 'Successfully setup SAML Certificates'
    end
    redirect_to organisations_path
  end

  private

  def authorize_user
    unless current_user.admin?
      respond_to do |format|
        format.html { redirect_to organisations_path, notice: 'Unauthorized access' }
        format.json { render json: {}, status: :unauthorized }
      end
    end
  end

  def load_org
    id = params[:id] || params[:organisation_id]
    @org = Organisation.where(id: id).first
    if @org.blank?
      redirect_to organisations_path
    end
  end

  def validate_app_name
    saml_apps = ENV['SAML_APPS'].split(',').map(&:downcase)
    unless saml_apps.include?(params[:app_name].downcase)
      redirect_to organisation_path(id: params[:organisation_id])
    end
  end

  def organisation_params
    params.require(:organisation).permit(
      :name, :website, :domain, :country, :state, :address, :admin_email_address,
      :slug, :unit_name
    )
  end
end


================================================
FILE: app/controllers/pings_controller.rb
================================================
class PingsController < ApplicationController
  def show
    render plain: 'pong'
  end
end


================================================
FILE: app/controllers/profile_controller.rb
================================================
class ProfileController < ApplicationController
  require 'vpn/mobileconfig'

  skip_before_action :verify_authenticity_token, if: Proc.new { |c| c.request.format == 'application/json' }
  before_action :authenticate_user!, except: %i[user_id verify authenticate authenticate_cas authenticate_ms_chap authenticate_pam public_key]

  def regen_auth
    current_user.generate_two_factor_auth(true)
    redirect_to profile_path
  end

  def show
    @token_qr = nil
    unless current_user.provisioning_uri.blank?
      @token_qr = RQRCode::QRCode.new(current_user.provisioning_uri, size: 10, level: :h)
    end
  end

  def user_admin
    @users = []
    @groups = []
    if !current_user.admin?
      redirect_to profile_path
    end

    @user_search = params[:user_search]
    if @user_search.present?
      @users = User.where('name LIKE ? OR email LIKE ?', "%#{@user_search}%", "%#{@user_search}%").
        take(5)
      redirect_to profile_list_path(user_search: params[:user_search]) if @users.count.positive?
    end

    @group_search = params[:group_search]
    if @group_search.present?
      @groups = Group.where('name LIKE ?', "%#{@group_search}%").take(5)
      redirect_to group_list_path(group_search: params[:group_search]) if @groups.count.positive?
    end
  end

  def group_admin
    @users = []
    @groups = []
    if !current_user.admin? && !current_user.group_admin?
      redirect_to profile_path
    end

    @user_search = params[:user_search]
    if @user_search.present?
      @users = User.where('name LIKE ? OR email LIKE ?', "%#{@user_search}%", "%#{@user_search}%").
        take(5)
      redirect_to profile_list_path(user_search: params[:user_search]) if @users.count.positive?
    end

    @group_search = params[:group_search]
    if @group_search.present?
      @groups = Group.where('name LIKE ?', "%#{@group_search}%").take(5)
      redirect_to group_list_path(group_search: params[:group_search]) if @groups.count >= 0
    end
  end

  def user_id
    token = AccessToken.valid_token params[:token]
    response = 0
    if token
      user = User.get_user(params[:name])
      response = user.uid if user.present?
    end
    render plain: response
  end

  def download_vpn
    if !Pathname.new("/opt/vpnkeys/#{current_user.email}.tar.gz").exist?
      `cd /etc/openvpn/easy-rsa/ && bash /etc/openvpn/easy-rsa/gen-client-keys #{current_user.email}`
    else
      `cd /etc/openvpn/easy-rsa/ && bash /etc/openvpn/easy-rsa/gen-client-conf #{current_user.email}`
    end
    send_file(
      "/opt/vpnkeys/#{current_user.email}.tar.gz",
      type: 'application/zip',
      disposition: "attachment; filename=#{current_user.email}.tar.gz"
    )
  end

  def download_vpn_for_ios_and_mac
    mobileconfig = Mobileconfig.new
    vpns = Vpn.user_vpns current_user

    return render plain: "you don't have access to any vpns" unless vpns.present?

    mobileconfig_data = mobileconfig.generate(vpns, current_user)

    send_data(
      mobileconfig_data,
      filename: "#{current_user.email}.mobileconfig",
      type: 'application/x-apple-aspen-config'
    )
  end

  def download_vpn_for_user
    render plain: 'Please download vpn config from your homepage'
  end

  def authenticate
    response = User.authenticate params
    if response
      render plain: 0
    else
      render plain: 1
    end
  end

  def authenticate_ms_chap
    response = User.ms_chap_auth params
    render plain: response
  end

  def authenticate_cas
    username = User.authenticate_cas request.env['HTTP_AUTHORIZATION']

    ## cas-5.2.x expects {"@c":".SimplePrincipal","id":"casuser","attributes":{}}
    response_map = {
      '@class': 'org.apereo.cas.authentication.principal.SimplePrincipal',
      'id': username,
      'attributes': { 'backend': 'gate-sso' },
    }

    if username.present?
      render json: response_map, status: :ok
    else
      response_map['attributes'] = nil
      render json: response_map, status: 401
    end
  end

  def authenticate_pam
    response = User.authenticate_pam params
    if response
      render plain: 0
    else
      render plain: 1
    end
  end

  def verify
    token = AccessToken.valid_token params[:token]
    if token
      response = User.verify params
      if response
        render plain: 0
      else
        render plain: 1
      end
    else
      render plain: 1
    end
  end

  def list
    @users = []
    @user_search = params[:user_search]
    if @user_search.present?
      @users = User.where('name LIKE ? OR email LIKE ?', "%#{@user_search}%", "%#{@user_search}%").
        take(5)
    end
  end

  def admin
    @users = []
    @groups = []
    if !current_user.admin?
      redirect_to profile_path
    end

    @user_search = params[:user_search]
    if @user_search.present?
      @users = User.where('name LIKE ? OR email LIKE ?', "%#{@user_search}%", "%#{@user_search}%").
        take(5)
      redirect_to profile_list_path(user_search: params[:user_search]) if @users.count.positive?
    end

    @group_search = params[:group_search]
    if @group_search.present?
      @groups = Group.where('name LIKE ?', "%#{@group_search}%").take(5)
      redirect_to group_list_path(group_search: params[:group_search]) if @groups.count.positive?
    end
  end

  def update
    if current_user.admin?
      @user = User.find(params[:id])
      @user.update(admin_active)
    end
    redirect_to user_path
  end

  def user_edit; end

  def public_key_update
    @user = User.where(id: params[:id]).first
    if current_user.admin? || current_user.id == @user.id
      @user.public_key = params[:public_key]
      @user.save!
    end
    redirect_to user_path
  end

  def public_key
    public_key = ''
    @user = User.get_user(params[:name])
    public_key = @user.public_key if @user.present?
    render plain: public_key
  end

  def user
    @group = Group.all
    @user = User.where(id: params[:id]).first

    if !current_user.admin? && current_user.id != @user.id
      redirect_to profile_path
    end
    render_404 if @user.blank?
    if @user.present?
      # hack add blank text to public_key
      @user.public_key = 'Add public key' if @user.public_key.blank?
      respond_to do |format|
        format.html
      end
    end
  end

  protected

  def admin_active
    params.require(:user).permit(:active, :admin)
  end
end


================================================
FILE: app/controllers/saml_idp_controller.rb
================================================
#TODO rename back to SAMLController

#class SamlIdpController < SamlIdp::IdpController
class SamlIdpController < ApplicationController
  layout false
  before_action :setup_saml_configuration

  def show
    xml_content = SamlIdp.metadata.signed
    if params.key?(:download)
      send_data xml_content,
        type: 'text/xml',
        filename: 'metadata.xml'
    else
      render xml: xml_content
    end
  end

  private

  def idp_authenticate(email, password)
    user = User.find_and_validate_saml_user(email, password, params[:app])
    user.present? ? user : nil
  end

  def idp_make_saml_response(found_user)
    encode_response found_user
  end

  def idp_logout
    # user = User.by_email(saml_request.name_id)
    # user.logout
  end

  def setup_saml_configuration
    slug = params[:slug]
    app = params[:app]
    org = Organisation.find_by_slug(slug)
    saml_url = "#{ENV['GATE_SERVER_URL']}/#{slug}/#{app}/saml"
    SamlIdp.configure do |config|
      config.x509_certificate = org.cert_key
      config.secret_key = org.cert_private_key
      config.organization_name = org.name
      config.organization_url = org.website
      config.base_saml_location = saml_url
      config.session_expiry = 86400
      config.name_id.formats = {
        email_address: ->(principal) { principal.email },
        transient: ->(principal) { principal.user_login_id },
        persistent: ->(principal) { principal.user_login_id },
        name: ->(principal) { principal.name },
      }
      config.attributes = {
        'eduPersonPrincipalName' => {
          'name' => 'urn:oid:1.3.6.1.4.1.5923.1.1.1.6',
          'name_format' => 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
          'getter' => ->(principal) { principal.email },
        },
      }
      config.attribute_service_location = "#{saml_url}/attributes"
      config.single_service_post_location = "#{saml_url}/auth"
      config.single_logout_service_post_location = "#{saml_url}/logout"
      config.single_logout_service_redirect_location = "#{saml_url}/logout"
    end
  end
end


================================================
FILE: app/controllers/users/auth_controller.rb
================================================
class Users::AuthController < ApplicationController

  def log_in
    unless ENV['SIGN_IN_TYPE'] == 'form'
      return redirect_to root_path
    end

    email = params.require(:email)
    name = params.require(:name)

    unless User.valid_domain? email.split('@').last
      return render plain: 'Your domain is unauthorized', status: :unauthorized
    end

    user = User.create_user(name, email)
    user.generate_two_factor_auth
    sign_in_and_redirect user, event: :authentication
  end
end


================================================
FILE: app/controllers/users/omniauth_callbacks_controller.rb
================================================
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def google_oauth2
    # You need to implement the method below in your model (e.g. app/models/user.rb)
    #
    data = request.env['omniauth.auth']
    domain = data['info']['email'].split('@').last

    unless User.valid_domain? domain
      return render plain: 'Your domain is unauthorized', status: :unauthorized
    end

    @user = User.create_user(data.info['name'], data.info['email'])

    if @user.persisted?
      @user.generate_two_factor_auth
      sign_in_and_redirect @user, event: :authentication
    else
      session['devise.google_data'] = request.env['omniauth.auth']
      redirect_to new_user_registration_url
    end
  end
end


================================================
FILE: app/controllers/users_controller.rb
================================================
class UsersController < ApplicationController
  before_action :authenticate_user!, except: %i[user_id verify authenticate authenticate_cas authenticate_ms_chap authenticate_pam public_key]
  before_action :authorize_user, only: %i[create update]

  def index
    @user_search = params[:user_search]
    @users = []
    @users = User.where('name like ?', "%#{@user_search}%").take(20) if @user_search.present?
  end

  def show
    @user = User.where(id: params[:id]).first
    return render_404 if @user.blank?

    @user_groups = Group.
      select(%{
        groups.id AS id,
        gid,
        name,
        deleted_at,
        group_associations.expiration_date AS group_expiration_date
      }).
      joins('INNER JOIN group_associations ON groups.id = group_associations.group_id').
      where('group_associations.user_id = ?', @user.id)

    if @user.access_token.blank?
      access_token = AccessToken.new
      access_token.token = ROTP::Base32.random_base32
      access_token.user = @user
      access_token.save!
    end

    @vpns = Vpn.user_vpns @user

    return unless current_user.admin? || current_user == @user

    return unless @user.present? && (current_user.admin? || current_user.id == @user.id)

    respond_to do |format|
      format.html { render :show, flash: { token: access_token.try(:token) } }
    end
  end

  def new
    return redirect_to profile_path unless current_user.admin?

    render :new, locals: {
      roles: ENV['USER_ROLES'].split(','),
      domains: ENV['GATE_HOSTED_DOMAINS'].split(','),
    }
  end

  def create
    user = User.add_user(
      user_params[:first_name],
        user_params[:last_name],
        user_params[:user_role],
        params[:user_domain]
      )
    if user.errors.present?
      flash[:errors] = user.errors.full_messages
      redirect_to(new_user_path)
    else
      flash[:success] = 'Successfully Created User'
      redirect_to user_path(id: user.id)
    end
  end

  def update
    @user = User.find(params[:id])
    begin
      @user.update(product_name: product_name)
      response_message = 'product name updated successfully!!'
    rescue ActionController::ParameterMissing
      response_message = 'Params are missing'
    end

    form_response(response_message)
  end

  def search
    @users = User.
      where('name LIKE :q OR email LIKE :q', q: "%#{params[:q]}%").
      order('name ASC').
      limit(20)
    unless params[:include_inactive] == 'true'
      @users = @users.where(active: true)
    end
    data = @users.map do |user|
      {
        id: user.id,
        name: user.name,
        email: user.email,
        name_email: "#{user.name} - #{user.email}",
      }
    end
    render json: data
  end

  # GET /users/:id/regenerate_token
  def regenerate_token
    @user = User.find(params[:id])
    unless current_user.admin? || (current_user.id == @user.id)
      return respond_to do |format|
        format.html { redirect_to user_path(@user.id), notice: 'You cannot regenerate this token.' }
        format.json { render json: @user.errors, status: :unauthorized }
      end
    end

    @access_token = @user.access_token
    @access_token.token = ROTP::Base32.random_base32
    respond_to do |format|
      if @access_token.save
        format.html { redirect_to user_path(@user.id), notice: 'Token regenerated.', flash: { token: @access_token.token } }
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { redirect_to user_path(@user.id), notice: 'Token failed to regenerate.' }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  private

  def user_params
    params.require(:user).permit(
      :first_name, :last_name, :user_role
    )
  end

  def form_response(message)
    respond_to do |format|
      format.html { redirect_to user_path, notice: message }
    end
  end

  def product_name
    params.require(:product_name)
  end

  def authorize_user
    unless current_user.admin?
      flash[:errors] = 'unauthorized access'
      redirect_to profile_path
    end
  end
end


================================================
FILE: app/controllers/vpn_domain_name_servers_controller.rb
================================================
class VpnsController < ApplicationController
  before_action :authorize_user
  before_action :set_vpn, only: %i[show edit update destroy user_associated_groups]

  def destroy; end
end

#TODO fix this in future. This is something not great.
class VpnDomainNameServersController < ApplicationController
end


================================================
FILE: app/controllers/vpns_controller.rb
================================================
class VpnsController < ApplicationController
  before_action :authorize_user, except: %i[create_group_associated_users show index
                                            user_associated_groups group_associated_users search]
  before_action :set_vpn, only: %i[show edit update destroy
                                   user_associated_groups add_dns_server remove_dns_server
                                   add_search_domain remove_search_domain
                                   add_supplemental_match_domain remove_supplemental_match_domain
                                   migrate_to_new_group assign_group]

  before_action :authenticate_user!

  require 'securerandom'

  def index
    @vpns = Vpn.order(:name)
  end

  def update
    if current_user.admin?
      @vpn = Vpn.find(params[:id])
      if @vpn.update(vpn_params)
        redirect_to vpn_path(@vpn), notice: 'Vpn was successfully updated.'
      end
    else
      redirect_to vpn_path(@vpn), notice: 'You can not update, not sufficient privileges.'
    end
  end

  def create
    @vpn = Vpn.new(vpn_params)
    @vpn.uuid = SecureRandom.uuid
    respond_to do |format|
      if @vpn.save
        format.html { redirect_to vpns_path, notice: 'Vpn was successfully added.' }
        format.json { render status: :created, json: "#{@vpn.name}host created" }
      else
        format.html { redirect_to vpns_path, notice: "Can't save '#{vpn_params[:name]}'" }
        format.json { render status: :error, json: "#{@vpn.name} not created" }
      end
    end
  end

  def new
    @vpn = Vpn.new
  end

  def add_dns_server
    if current_user.admin? && params[:server_address].present?
      VpnDomainNameServer.find_or_create_by(server_address: params[:server_address], vpn: @vpn)
    end
    redirect_to vpn_path(@vpn, anchor: 'dns_hosts')
  end

  def add_search_domain
    if current_user.admin? && params[:search_domain].present?
      VpnSearchDomain.find_or_create_by(search_domain: params[:search_domain], vpn: @vpn)
    end
    redirect_to vpn_path(@vpn, anchor: 'search_domains')
  end

  def add_supplemental_match_domain
    if current_user.admin? && params[:supplemental_match_domain].present?
      VpnSupplementalMatchDomain.find_or_create_by(
        supplemental_match_domain: params[:supplemental_match_domain],
        vpn: @vpn
      )
    end
    redirect_to vpn_path(@vpn, anchor: 'match_domains')
  end

  def remove_dns_server
    if current_user.admin?
      VpnDomainNameServer.delete(params[:vpn_domain_name_server_id])
    end
    redirect_to vpn_path(@vpn, anchor: 'dns_hosts')
  end

  def remove_search_domain
    if current_user.admin?
      VpnSearchDomain.delete(params[:vpn_search_domain_id])
    end
    redirect_to vpn_path(@vpn, anchor: 'search_domains')
  end

  def remove_supplemental_match_domain
    if current_user.admin?
      VpnSupplementalMatchDomain.delete(params[:vpn_supplemental_match_domain_id])
    end
    redirect_to vpn_path(@vpn, anchor: 'match_domains')
  end

  def assign_group
    if current_user.admin?
      @vpn.groups.delete_all
      @vpn.groups << Group.where(id: params[:group_id]).first
    end
    redirect_to vpn_path(@vpn, anchor: 'match_domains')
  end

  def show
    @vpn = Vpn.find(params[:id])
    @groups = Group.order(:name)
  end

  def user_associated_groups
    @groups_under_current_user = []
    @group_id = params[:group_id]
    @vpn_id = params[:id]
    if current_user.admin?
      @groups_under_current_user = Group.all
    else
      @vpn.groups.each do |vpn_group|
        if vpn_group.group_admin.try(:user) == current_user
          @groups_under_current_user << vpn_group
        end
      end
    end

    render 'show'
  end

  def group_associated_users
    @group = Group.find(params[:group_id])
    if current_user.admin? || @group.group_admin.try(:user) == current_user
      @users = @group.users
      @vpn_group_user_associations = VpnGroupUserAssociation.where(
        vpn_id: params[:vpn_id],
        group_id: params[:group_id]
      )
      @vpn_enabled_users = @vpn_group_user_associations.map(&:user)

      @vpn_disabled_users = @users - @vpn_enabled_users

      @vpn_enabled_users = @vpn_enabled_users.sort_by(&:email)
      @vpn_disabled_users = @vpn_disabled_users.sort_by(&:email)
    end
    respond_to do |format|
      format.json { render status: :ok, json: { enabled: @vpn_enabled_users, disabled: @vpn_disabled_users } }
    end
  end

  def create_group_associated_users
    if current_user.admin? ||
        VpnGroupAssociation.find_by_vpn_id_and_group_id(
          params[:vpn_id].to_i,
          params[:group_id]
        ).group.group_admin.user == current_user
      @users_selected = params[:users] || []
      @associations_made = []
      VpnGroupUserAssociation.where(
        vpn_id: params[:vpn_id].to_i,
        group_id: params[:group_id].to_i
      ).each do |vpn_association|
        unless @users_selected.include? vpn_association.user.id
          vpn_association.destroy
        end
      end
      @users_selected.each do |user|
        @associations_made << VpnGroupUserAssociation.find_or_create_by(
          vpn_id: params[:vpn_id],
          group_id: params[:group_id],
          user_id: user.to_i
        )
      end

      respond_to do |format|
        format.json { render status: :ok, json: @associations_made }
      end
    else
      respond_to do |format|
        format.json { render status: :unauthorized, json: 'not gonna happen' }
      end
    end
  end

  def destroy
    VpnGroupUserAssociation.where(vpn_id: params[:id]).destroy_all
    VpnGroupAssociation.where(vpn_id: params[:id]).destroy_all
    Vpn.destroy(params[:id])

    respond_to do |format|
      format.html { redirect_to vpns_path, notice: 'Vpn was successfully destroyed.' }
      format.json { render status: :ok, json: 'vpn destroyed' }
    end
  end

  def search
    @vpns = Vpn.
      where('name LIKE ?', "%#{params[:q]}%").
      order('name ASC').
      limit(20)
    data = @vpns.map { |vpn| { id: vpn.id, name: vpn.name } }
    render json: data
  end

  private

  def set_vpn
    @vpn = Vpn.find(params[:id])
  end

  def vpn_params
    params.require(:vpn).permit(:name, :host_name, :ip_address)
  end

  def authorize_user
    unless current_user.admin?
      redirect_to profile_path
    end
  end
end


================================================
FILE: app/helpers/admin_helper.rb
================================================
module AdminHelper
end


================================================
FILE: app/helpers/api_resources_helper.rb
================================================
module ApiResourcesHelper
end


================================================
FILE: app/helpers/application_helper.rb
================================================
module ApplicationHelper
  def add_placeholder_to_list(list, placeholder, string_convert: 'titleize')
    (list.map do |row|
      name = string_convert.present? ? row.send(string_convert.to_sym) : row
      [name, row]
    end).insert(0, [placeholder, ''])
  end
end


================================================
FILE: app/helpers/group_helper.rb
================================================
module GroupHelper
end


================================================
FILE: app/helpers/groups_helper.rb
================================================
module GroupsHelper
end


================================================
FILE: app/helpers/home_helper.rb
================================================
module HomeHelper
end


================================================
FILE: app/helpers/host_access_groups_helper.rb
================================================
module HostAccessGroupsHelper
end


================================================
FILE: app/helpers/host_machine_groups_helper.rb
================================================
module HostMachineGroupsHelper
end


================================================
FILE: app/helpers/host_machines_helper.rb
================================================
module HostMachinesHelper
end


================================================
FILE: app/helpers/nss_helper.rb
================================================
module NssHelper
end


================================================
FILE: app/helpers/omniauth_callbacks_helper.rb
================================================
module OmniauthCallbacksHelper
end


================================================
FILE: app/helpers/profile_helper.rb
================================================
module ProfileHelper
end


================================================
FILE: app/helpers/users_helper.rb
================================================
module UsersHelper
end


================================================
FILE: app/lib/datadog.rb
================================================
class Datadog < SamlApp

  def initialize(org_id)
    @app_name = 'datadog'
    super(org_id)
    if @config.persisted?
      @client = DataDogClient.new(
        @config.config['app_key'],
        @config.config['api_key']
      )
    else
      @config.config = { app_key: '', api_key: '' }
    end
  end

  def save_config(sso_url, config = {})
    @config.config = @config.config.merge(config)
    super(sso_url, config)
  end

  def add_user(email)
    user_detail_response = @client.get_user(email)
    response = if user_detail_response.eql?({})
                 @client.new_user(email)
               else
                 @client.activate_user(email)
               end
    super(email) unless response.eql?({})
  end

  def remove_user(email)
    response = @client.deactivate_user(email)
    super(email) unless response.eql?({})
  end
end


================================================
FILE: app/lib/saml_app.rb
================================================
class SamlApp

  attr_accessor :config, :app_name

  def initialize(org_id)
    @config = SamlAppConfig.find_or_initialize_by(
      app_name: @app_name, organisation_id: org_id
    )
  end

  def save_config(sso_url, config = {})
    unless @config.persisted?
      group_name = "#{@config.organisation.slug}_saml_#{app_name}_users"
      @config.group = Group.find_or_create_by(name: group_name)
    end
    @config.sso_url = sso_url
    @config.save
  end

  def add_user(email)
    user = User.where(email: email).first
    unless user.blank?
      @config.group.add_user(user.id)
      return true
    end
    false
  end

  def remove_user(email)
    user = User.where(email: email).first
    unless user.blank?
      @config.group.remove_user(user.id)
      return true
    end
    false
  end
end


================================================
FILE: app/mailers/.keep
================================================


================================================
FILE: app/models/.keep
================================================


================================================
FILE: app/models/access_token.rb
================================================
class AccessToken < ApplicationRecord
  attr_accessor :token


  belongs_to :user

  before_save :hash_token!

  def self.find_token challenge_token
    AccessToken.where(hashed_token: Digest::SHA512.hexdigest(challenge_token)).first
  end

  def self.valid_token challenge_token
    find_token(challenge_token).present?
  end

  private

  def hash_token!
    self.hashed_token = Digest::SHA512.hexdigest self.token unless self.token.blank?
  end
end


================================================
FILE: app/models/api_resource.rb
================================================
class ApiResource < ApplicationRecord
  attr_accessor :access_key

  validates :name, format: { with: /\A[a-zA-Z0-9_-]+\Z/ }, uniqueness: true, presence: true
  validates :access_key, presence: true, on: :create
  belongs_to :user
  belongs_to :group

  before_save :hash_access_key!

  def self.authenticate access_key, access_token
    api_resource = ApiResource.find_by(hashed_access_key: Digest::SHA512.hexdigest(access_key))
    user = AccessToken.find_by(hashed_token: Digest::SHA512.hexdigest(access_token)).user
    api_resource.group.member? user
  end

  private

  def hash_access_key!
    self.hashed_access_key = Digest::SHA512.hexdigest self.access_key unless self.access_key.blank?
  end
end


================================================
FILE: app/models/application_record.rb
================================================
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end


================================================
FILE: app/models/concerns/.keep
================================================


================================================
FILE: app/models/concerns/ms_chap_auth.rb
================================================
module MsChapAuth

  def hexlify(msg)
    msg.unpack('H*').first
  end

  def unhexlify(msg)
    msg.scan(/../).collect { |c| c.to_i(16).chr }.join
  end

  def test_key key, challenge
    des = OpenSSL::Cipher::DES.new('ECB')
    des.encrypt
    des.key = key
    des.update challenge
    #need to do it twice, don't know why!!! :-(
    des.update challenge
    end

    def ntlm_challenge_response word, challenge

      uword = word.encode('iso-8859-1').encode('utf-16le')
      ntlmhash = md4_hash uword

      response = []

      response.push(test_key(key56_to_key64(ntlmhash[0...14]), challenge))
      response.push(test_key(key56_to_key64(ntlmhash[14...28]), challenge))
      response.push(test_key(key56_to_key64(ntlmhash[28...ntlmhash.length] + '0000000000'), challenge))
      hexlify(response[0]) + hexlify(response[1]) + hexlify(response[2])
    end


    def md4_hash word
      md4 = OpenSSL::Digest::MD4.new
      return md4.hexdigest(word)
    end

    def set_key_odd_parity key
      for pos in 0..key.length - 1
        for k in 0..6
          bit = 0
          t = key[pos] >> k
          bit = (t ^ bit) & 0x1
        end
        key[pos] = (key[pos] & 0xFE) | bit
      end
      return key
    end

    def key56_to_key64 key_raw
      key_raw = unhexlify(key_raw)
      key_56 = []
      key_raw.split("").each {|c| key_56.push(c.ord)}

      key = []
      (0..7).to_a.each {|i| key.push(0)}

      key[0] = key_56[0]
      key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
      key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
      key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
      key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
      key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
      key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
      key[7] =  (key_56[6] << 1) & 0xFF;

      key = set_key_odd_parity(key)

      keyout = ''
      key.each {|k| keyout += k.chr}
      return keyout

    end

    def nt_password_hash password
      md4 = OpenSSL::Digest::MD4.new
      md4.digest(password)
    end

    def get_nt_key password
      unicode_pwd = password.encode('iso-8859-1').encode('utf-16le')
      pwd_hash = nt_password_hash(unicode_pwd)
      nt_key   = nt_password_hash(pwd_hash)
      return hexlify(nt_key)
    end

    def authenticate_ms_chap password, challenge, response
      if ntlm_challenge_response(password, unhexlify(challenge)) == response
        return "NT_KEY: " + get_nt_key(password).upcase
      end
      return ("NT_STATUS_UNSUCCESSFUL: Failure (0xC0000001)")
    end

    def authenticate_ms_chap_with_drift passwords, challenge, response
      passwords.each do |password|
        if ntlm_challenge_response(password, unhexlify(challenge)) == response
          return "NT_KEY: " + get_nt_key(password).upcase
        end
      end
      return ("NT_STATUS_UNSUCCESSFUL: Failure (0xC0000001)")
    end

  end




================================================
FILE: app/models/endpoint.rb
================================================
class Endpoint < ApplicationRecord


  has_many :group_endpoints
  has_many :groups, through: :group_endpoints

  validates_presence_of :path
  validates_presence_of :method
  validates :method, inclusion: { in: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] }
  validates_format_of :path, with: /\A((\/(([0-9, a-z,\-,_]+)|(:[a-z]+))+)+|\/)\Z/i
end


================================================
FILE: app/models/group.rb
================================================
class Group < ApplicationRecord

  has_many :group_admins, dependent: :destroy
  has_many :group_associations
  has_many :users, through: :group_associations

  has_many :vpn_group_associations
  has_many :vpns, through: :vpn_group_associations

  has_many :host_access_groups
  has_many :host_machines, through: :host_access_groups
  belongs_to :vpn

  has_many :group_endpoints
  has_many :endpoints, through: :group_endpoints

  validates_uniqueness_of :name, case_sensitive: false
  validates :name, presence: true

  before_create :set_lower_case_name
  acts_as_paranoid

  after_create :add_gid

  GID_CONSTANT = 9000

  def burst_host_cache
    if host_machines.count.positive?
      host_machines.each do |host|
        if host.access_key.present?
          REDIS_CACHE.del "#{GROUP_RESPONSE}:#{host.access_key}"
          REDIS_CACHE.del "#{PASSWD_RESPONSE}:#{host.access_key}"
          Rails.logger.info "hello #{host.name} #{host.access_key}"
        end
      end
    end
  end

  def add_admin(user)
    GroupAdmin.find_or_create_by(group_id: id, user: user)
  end

  def set_lower_case_name
    self.name = name.downcase
  end

  def add_gid
    self.gid = id + GID_CONSTANT
    save!
  end

  def self.get_name_response(name)
    response = REDIS_CACHE.get(GROUP_NSS_RESPONSE + name)
    if response.blank?
      group = Group.where(name: name).first
      group = [] if group.blank?
      response = group.group_response.to_json
      REDIS_CACHE.set(GROUP_NSS_RESPONSE + name, response)
      REDIS_CACHE.expire(GROUP_NSS_RESPONSE + name, REDIS_KEY_EXPIRY)
    end
    JSON.parse(response, symbolize_names: true)
  end

  def self.get_all_response
    response = REDIS_CACHE.get(GROUP_ALL_RESPONSE)
    if response.blank?
      response_array = []
      Group.all.includes(:users).each do |group|
        response_array << group.group_response
      end
      response = response_array.to_json
      REDIS_CACHE.set(GROUP_ALL_RESPONSE, response)
      REDIS_CACHE.expire(GROUP_ALL_RESPONSE, REDIS_KEY_EXPIRY)
    end
    response
  end

  def self.get_gid_response(gid)
    group = Group.where(gid: gid).first
    return [] if group.blank?

    group.group_response
  end

  def admin?(user)
    GroupAdmin.where(group_id: self, user_id: user).first.present?
  end

  def member?(user)
    users.exists? user.id
  end

  def self.generate_group_response(name, gid, members)
    {
      gr_name: name,
      gr_passwd: 'x',
      gr_gid: gid,
      gr_mem: members,
    }
  end

  def group_response
    Group.group_nss_response name
  end

  def self.group_nss_response(name)
    group_response = REDIS_CACHE.get("#{GROUP_NSS_RESPONSE}:#{name}")
    group_response = JSON.parse(group_response) if group_response.present?

    if group_response.blank?
      group = Group.find_by(name: name)
      if group.present?
        members = group.users.map(&:user_login_id)
        response_hash = Group.generate_group_response(group.name, group.gid, members)
        REDIS_CACHE.set("#{GROUP_NSS_RESPONSE}:#{group.name}", response_hash.to_json)
        REDIS_CACHE.expire("#{GROUP_NSS_RESPONSE}:#{group.name}", REDIS_KEY_EXPIRY)
        group_response = response_hash
      end
    end
    group_response
  end

  def self.get_sysadmins_and_groups(sysadmins, default_admins = true)
    sysadmins_login_ids = User.
      select(:user_login_id).
      where('id IN (?)', sysadmins).
      map(&:user_login_id)

    # TODO: extract to query object
    groups = Group.
      select(%(
        id,
        name,
        gid,
        (
          SELECT GROUP_CONCAT(user_login_id)
          FROM users
          INNER JOIN group_associations
            ON users.id = group_associations.user_id
          WHERE group_associations.group_id = groups.id
        ) AS members
      )).
      where('name IN (?)', sysadmins_login_ids).
      map do |group|
        members = (group.members || '').split(',')
        Group.generate_group_response(group.name, group.gid, members)
      end
    groups << Group.get_default_sysadmin_group_for_host(sysadmins_login_ids, default_admins)
    groups.to_json
  end

  def get_user_ids
    user_ids = REDIS_CACHE.get("#{GROUP_UID_RESPONSE}:#{name}")
    user_ids = JSON.parse(user_ids) if user_ids.present?

    if user_ids.blank?
      user_ids = users.map(&:user_login_id)
      REDIS_CACHE.set("#{GROUP_UID_RESPONSE}:#{name}", user_ids.to_json)
      REDIS_CACHE.expire("#{GROUP_UID_RESPONSE}:#{name}", REDIS_KEY_EXPIRY)
    end
    user_ids
  end

  def self.get_default_sysadmin_group_for_host(sysadmins_login_ids, default_admins = true)
    sysadmins = sysadmins_login_ids

    if default_admins
      group = Group.find_by(name: 'sysadmins')

      if group.present?
        sysadmins = sysadmins + group.get_user_ids
      end
    end
    group_id = group.blank? ? 8999 : group.id

    sysadmin_group = Group.generate_group_response('sysadmins', group_id, sysadmins.uniq)
    sysadmin_group
  end

  def add_user(user_id)
    add_user_with_expiration(user_id, nil)
  end

  def add_user_with_expiration(user_id, expiration_date)
    remove_user user_id
    group_associations.create(user_id: user_id, expiration_date: expiration_date)
  end

  def remove_user(user_id)
    group_associations.where(user_id: user_id).destroy_all
    burst_host_cache
  end
end


================================================
FILE: app/models/group_admin.rb
================================================
class GroupAdmin < ApplicationRecord
  belongs_to :user
  belongs_to :group
end


================================================
FILE: app/models/group_association.rb
================================================
class GroupAssociation < ApplicationRecord
  belongs_to :user
  belongs_to :group

  def self.revoke_expired(date = Date.today)
    where('expiration_date < ?', date).destroy_all
  end
end


================================================
FILE: app/models/group_endpoint.rb
================================================
class GroupEndpoint < ApplicationRecord


  belongs_to :group
  belongs_to :endpoint

  validates :group, uniqueness: { scope: :endpoint }
  validates_presence_of :group
  validates_presence_of :endpoint
end


================================================
FILE: app/models/host.rb
================================================
class Host < ApplicationRecord
  belongs_to :user
  acts_as_paranoid
end


================================================
FILE: app/models/host_access_group.rb
================================================
class HostAccessGroup < ApplicationRecord
  belongs_to :host_machine
  belongs_to :group
end


================================================
FILE: app/models/host_machine.rb
================================================
class HostMachine < ApplicationRecord

  has_many :host_access_groups
  has_many :groups, through: :host_access_groups
  validates_uniqueness_of :name, case_sensitive: false
  validates :name, presence: true

  before_create :set_lower_case_name
  before_create :set_host_access_key

  def set_host_access_key
    self.access_key = ROTP::Base32.random_base32
  end

  def set_lower_case_name
    self.name = self.name.downcase
  end

  def self.get_group_response name
    host_machine = HostMachine.find_by_name(name)
    response = {}
    return response if host_machine.blank?
    response[:host_name] = name
    response[:groups] = host_machine.groups.collect { |g| g.name }
    response
  end

  def sysadmins
    users = GroupAssociation.
      select(:user_id).
      distinct.
      joins(:user).
      where("group_id IN (?)", groups.collect(&:id)).
      collect(&:user_id)
  end

  def add_host_group(name)
    name = name.squish
    if name.present?
      name = "#{name}_host_group"
      self.add_group(name.downcase)
    end
  end

  def add_group(name)
    name = name.squish
    if name.present?
      group =  Group.find_or_initialize_by(name: name.downcase)
      self.groups << group unless self.groups.include? group
      self.save
    end
  end
end


================================================
FILE: app/models/ip_address.rb
================================================
class IpAddress < ApplicationRecord
end


================================================
FILE: app/models/organisation.rb
================================================
class Organisation < ApplicationRecord
  validates :name, :website, :domain, :country, :state, :address,
            :admin_email_address, :slug, presence: true
  validates :address, format: {
    with: /\A[a-zA-Z0-9\s]+\z/,
    message: 'Invalid - Only Alphabets, Space and Numbers Allowed',
  }
  validates :admin_email_address, email: true
  validates :slug, uniqueness: true

  attr_accessor :cert, :rsa_key

  UPDATE_KEYS = %w(
    name website domain country state address admin_email_address slug unit_name
  ).freeze

  def self.find_by_slug(slug)
    Organisation.where(slug: slug).first
  end

  def self.setup(attrs = {})
    attrs = attrs.stringify_keys
    attrs = attrs.select { |k, _v| UPDATE_KEYS.include?(k) }
    org = Organisation.new(attrs)
    org.save if org.valid?
    org
  end

  def update_profile(attrs = {})
    attrs = attrs.stringify_keys
    attrs = attrs.select { |k, _v| UPDATE_KEYS.include?(k) }
    assign_attributes(attrs)
    save if valid?
  end

  def saml_setup?
    cert_fingerprint.present? && cert_key.present? && cert_private_key.present?
  end

  def setup_saml_certs
    return false unless persisted?
    require 'openssl'
    self.rsa_key = OpenSSL::PKey::RSA.new(2048)
    private_key = rsa_key.to_pem
    public_key = rsa_key.public_key
    subject = "/C=#{country}/ST=#{state}/L=#{address}/O=#{name}/OU=#{unit_name}/CN=#{domain}"
    self.cert = OpenSSL::X509::Certificate.new
    cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
    cert.not_before = Time.now
    cert.not_after = Time.now + 365 * 24 * 60 * 60
    cert.public_key = public_key
    cert.serial = SecureRandom.random_number(10)
    cert.version = 2
    ef = OpenSSL::X509::ExtensionFactory.new
    ef.subject_certificate = cert
    ef.issuer_certificate = cert
    cert.extensions = [
      ef.create_extension('basicConstraints', 'CA:TRUE', true),
      ef.create_extension('subjectKeyIdentifier', 'hash'),
    ]
    cert.add_extension ef.create_extension(
      'authorityKeyIdentifier', 'keyid:always,issuer:always'
    )
    cert.sign rsa_key, OpenSSL::Digest::SHA1.new
    update_attributes(
      cert_fingerprint: OpenSSL::Digest::SHA256.hexdigest(cert.to_der).scan(/../).join(':'),
      cert_key: cert.to_pem,
      cert_private_key: private_key
    )
  end
end


================================================
FILE: app/models/saml_app_config.rb
================================================
class SamlAppConfig < ApplicationRecord
  belongs_to :group
  belongs_to :organisation

  serialize :config, JSON

  def self.get_config(app_name, org_id)
    SamlAppConfig.find_or_initialize_by(
      app_name: app_name, organisation_id: org_id
    )
  end
end


================================================
FILE: app/models/user.rb
================================================
class User < ApplicationRecord


  include MsChapAuth
  devise :timeoutable, :omniauthable, omniauth_providers: [:google_oauth2]
  has_many :hosts
  has_many :group_associations
  has_many :groups, through: :group_associations
  has_many :group_admin, dependent: :destroy
  has_one :access_token

  # TODO: Need to add the validations for the user model, right now a lot of tests fail due to enabling this
  # validates :first_name, :last_name, :mobile, :user_role, presence: true
  # validates :first_name, :last_name, format: { with: /[a-zA-Z]/}, allow_blank: true
  # validates :user_role, inclusion: { in: ENV['USER_ROLES'].split(',') }
  # validate :validate_email_domain
  
  
  validates :email, uniqueness: true

  # TODO: Need to enable these again after rails 7 update
  # validate :remove_default_admin, on: :update

  # before_save :revoke_admin_when_inactive, on: :update
  # before_save :set_deactivated_at_when_inactive, on: :update

  HOME_DIR = '/home'.freeze
  USER_SHELL = '/bin/bash'.freeze

  def self.add_user(first_name, last_name, user_role, domain)
    user = User.new(first_name: first_name, last_name: last_name, user_role: user_role)
    user.assign_attributes(
      user_login_id: "#{first_name.downcase}.#{last_name.downcase}",
      uid: user.generate_uid,
      email: "#{first_name.downcase}.#{last_name.downcase}@#{domain}",
      name: "#{first_name} #{last_name}"
    )
    user.save
    user.initialise_host_and_group if user.persisted?
    user
  end

  def generate_login_id
    email.split('@').first
  end

  def generate_uid(uid_buffer = 5000)
    uid_buffer = ENV['UID_BUFFER'].present? ? ENV['UID_BUFFER'].to_i : uid_buffer
    User.last.blank? ? uid_buffer : User.last.id.to_i + uid_buffer
  end

  def initialise_host_and_group
    host = Host.find_or_initialize_by(user: self)
    unless ENV['DEFAULT_HOST_PATTERN'].blank?
      host.host_pattern = ENV['DEFAULT_HOST_PATTERN']
    end
    hosts << host
    groups << Group.find_or_initialize_by(name: user_login_id)
  end

  def generate_two_factor_auth(force_create = false)
    if persisted? && (force_create || (!force_create && auth_key.blank?))
      self.auth_key = ROTP::Base32.random_base32
      totp = ROTP::TOTP.new(auth_key)
      self.provisioning_uri = totp.provisioning_uri "GoJek-C #{email}"
      save!
    end
  end

  def self.create_user(name, email)
    user = User.find_or_initialize_by(email: email)
    unless user.persisted?
      user.assign_attributes(
        name: name, user_login_id: user.generate_login_id, uid: user.generate_uid
      )
      user.admin = User.first.blank?
      user.initialise_host_and_group
      user.save! if user.valid?
    end
    user
  end

  def self.add_temp_user(name, email)
    email += "@#{ENV['GATE_HOSTED_DOMAIN']}"
    user = User.create_user(name, email)
    user.generate_two_factor_auth
    user.auth_key
  end

  def update_profile(attrs = {})
    allowed_keys = %w(public_key name product_name admin active)
    attrs = attrs.stringify_keys
    attrs = attrs.select { |k, v| allowed_keys.include?(k) && (v.present? || v.eql?(false)) }
    assign_attributes(attrs)
    if active.eql?(false) && deactivated_at.blank?
      self.deactivated_at = Time.current
    end
    save! if valid?
  end

  def name_email
    "#{name} (#{email})"
  end

  def self.get_sysadmins user_ids
    users = User.
      select(%Q(
        id,
        name,
        uid,
        user_login_id,
        (
          SELECT gid
          FROM groups
          INNER JOIN group_associations
            ON groups.id = group_associations.group_id
          WHERE group_associations.user_id = users.id
          AND groups.name = users.user_login_id
          LIMIT 1
        ) AS gid,
        (
          SELECT COUNT(gid)
          FROM groups
          INNER JOIN group_associations
            ON groups.id = group_associations.group_id
          WHERE group_associations.user_id = users.id
          AND groups.name = users.user_login_id
          LIMIT 1
        ) AS gid_count
      )).
      where(id: user_ids)
    users.map(&:user_passwd_response)
  end

  def purge!
    if !self.active
      self.group_associations.each{ |g| g.destroy }
    end
  end

  def self.includes_restricted_characters? input_string
    return false if input_string.include?('@') == false
    restricted_characters = [ ' ', '-', '*']
    status  = false

    restricted_characters.each do |char|
      break if status
      status = input_string.include?(char)
    end

    status
  end

  def self.check_email_address email_address
    !includes_restricted_characters?(email_address) && email_address.split("@").count == 2 ? true : false
  end

  def self.valid_domain? domain
    hosted_domains = ENV['GATE_HOSTED_DOMAINS'].split(',')
    hosted_domains.include?(domain)
  end

  def self.verify params
    addresses = params[:addresses]
    return false if addresses.empty?
    address_array = addresses.split


    user = User.get_user params[:user]
    return false if user.blank?

    return user.permitted_hosts? address_array
  end

  def self.authenticate_pam params
    addresses = params[:addresses]
    return false if addresses.empty?
    address_array = addresses.split

    email, token = get_user_pass_attributes params
    return false if email.blank? || token.blank?

    user_auth = find_and_check_user email, token
    return check_user_host(email, address_array) if user_auth

    return user_auth
  end

  def self.check_user_host email, address_array
    user = User.get_user email
    return user.permitted_hosts? address_array
  end

  def permitted_vpns? address_array
    address_array.each do |host_address|
      Vpn.user_vpns(self).each do |vpn|
        return true if vpn.ip_address == host_address
      end
    end
    return false
  end

  def permitted_hosts? address_array
    address_array.each do |host_address|
      host_name = nil
      begin
        host_name = Resolv.getname(host_address)
      rescue
        Rails.logger.info "Can't resolve name"
      end
      host_name = host_address if host_name.blank?
      hosts.each do |host|
        return true if /^#{host.host_pattern}/.match(host_name).to_s.present?
      end
    end
    return false
  end

  def self.authenticate_cas encoded_string
    username_password = Base64.decode64 encoded_string.split(" ")[1]
    username = username_password.split(':').first
    password = username_password.split(':').last

    if User.find_and_check_user username, password
      return username
    else
      return nil
    end
  end


  def self.authenticate params
    email, token = User.get_user_pass_attributes params
    return false if email.blank? || token.blank?
    return User.find_and_check_user email, token
  end

  def self.get_user user_login_id
    return User.where(user_login_id: user_login_id, active: true).first
  end

  def self.find_and_validate_saml_user(email, password, app_name)
    query = 'users.email = ? and users.active = ? and groups.name = ?'
    users = User.joins(:groups).where(query, email, true, app_name)
    if users.present?
      users.first.valid_otp?(password) ? users.first : false
    else
      false
    end
  end

  def valid_otp?(password)
    user_key = "#{id}:#{Time.now.hour}"
    request_count = REDIS_CACHE.incrby user_key, 1
    REDIS_CACHE.expire user_key, 3600
    return false if request_count > RATE_LIMIT
    password.eql?(ROTP::TOTP.new(self.auth_key).now)
  end

  def self.find_and_check_user email, token
    user = User.get_user email
    return false if user.blank?
    return false if !user.active
    user_key = "#{user.id}:#{Time.now.hour}"
    request_count = REDIS_CACHE.incrby user_key, 1
    REDIS_CACHE.expire user_key, 3600
    return false if request_count > RATE_LIMIT
    token == ROTP::TOTP.new(user.auth_key).now
  end

  def self.get_user_pass_attributes params
    token = params[:token].present? ? params[:token] : params[:password]
    email = params[:email].present? ? params[:email] : params[:user]
    return [nil, nil] if email.blank? || token.blank?
    [email, token]
  end


  def self.get_shadow_name_response name
    user = User.where(name: name).first
    return nil if user.blank?

    user.get_shadow_hash
  end

  def get_shadow_hash
    shadow_hash = {}
    shadow_hash[:sp_namp] = user_login_id
    shadow_hash[:sp_pwdp] = "X"
    shadow_hash[:sp_lstchg] = updated_at.to_i
    shadow_hash[:sp_min] = 0
    shadow_hash[:sp_max] = 99999
    shadow_hash[:sp_warn] = 7
    shadow_hash[:sp_inact]= nil
    shadow_hash[:sp_expire] = nil
    shadow_hash[:sp_flag] = nil
    shadow_hash
  end

  def self.get_all_shadow_response
    user_array = []
    User.all.each do |user|
      user_array << user.get_shadow_hash
    end
    user_array
  end

  def self.get_all_passwd_response
    user_array = []
    User.all.each do |user|
      user_array << user.user_passwd_response
    end
    user_array
  end

  def self.get_passwd_name_response name
    user = User.where("email like ?", "#{name}@%").first
    return [] if user.blank?
    user.user_passwd_response
  end

  def self.response_array response
    user_response = []
    user_response << response
    user_response
  end

  def self.find_active_user_by_email(email)
    User.where(email: email, active: true).first
  end

  def group_names_list
    self.groups.map(&:name)
  end

  def reset_login_limit
    user_key = "#{self.id}:#{Time.now.hour}"
    REDIS_CACHE.set user_key, 0
  end

  def within_limits?
    user_key = "#{self.id}:#{Time.now.hour}"
    request_count = REDIS_CACHE.incrby user_key, 1
    REDIS_CACHE.expire user_key, 3600
    request_count < RATE_LIMIT
  end

  def self.ms_chap_auth params
    auth_failed_message =  "NT_STATUS_UNSUCCESSFUL: Failure (0xC0000001)"

    addresses = params[:addresses]
    user_name = params[:user]
    challenge_string = params[:challenge]
    response_string = params[:response]

    return auth_failed_message  if user_name.blank? || challenge_string.blank? || response_string.blank? || addresses.blank?

    address_array = addresses.split

    user = User.get_user user_name
    if user.present? && user.permitted_vpns?(address_array)
      drift_interval = 30
      t = Time.now
      otps = []
      otps.push(user.get_user_otp_at(t))
      otps.push(user.get_user_otp_at(t - drift_interval))
      otps.push(user.get_user_otp_at(t + drift_interval))
      return user.authenticate_ms_chap_with_drift otps, challenge_string, response_string
    else
      return auth_failed_message
    end
  end

  #this method is here because we need to mock/stub for testing
  def get_user_otp
    return ROTP::TOTP.new(self.auth_key).now
  end

  def get_user_otp_at time
    return ROTP::TOTP.new(self.auth_key).at time
  end

  def user_passwd_response
    user_hash = {}
    user_hash[:pw_name] = user_login_id
    user_hash[:pw_passwd] = 'x'
    user_hash[:pw_uid] = uid.to_i

    # If gid is supplied (avoid N+1)
    if respond_to?(:gid) && gid
      user_hash[:pw_gid] = gid.to_i
    elsif respond_to?(:gid_count)
      if gid_count.positive?
        user_hash[:pw_gid] = groups.where(name: user_login_id).first.gid
      end
    elsif groups.where(name: user_login_id).count.positive?
      user_hash[:pw_gid] = groups.where(name: user_login_id).first.gid
    end

    user_hash[:pw_gecos] = name.to_s
    user_hash[:pw_dir] = "#{HOME_DIR}/#{user_login_id}"
    user_hash[:pw_shell] = '/bin/bash'
    user_hash
  end

  def group_admin?
    GroupAdmin.find_by_user_id(self.id).present?
  end

  def permitted_endpoint?(endpoint)
    return false if endpoint.nil?

    user_groups_id = group_associations.select(:group_id)
    endpoint.
      group_endpoints.
      where('group_id IN (?)', user_groups_id).
      select(:endpoint_id).present?
  end

  private

  def remove_default_admin
    admin_users = User.where('active = ? and admin = ? and id <> ?', true, true, id)
    if (!admin || !active) && admin_users.blank?
      errors.add(:admin, 'You cannot remove or make inactive the default admin account')
    end
  end

  def validate_email_domain
    domain_list = ENV['GATE_HOSTED_DOMAINS'].split(',')
    domain = email.split('@').last
    errors.add(:email, "Invalid Domain for Email Address") unless domain_list.include?(domain)
  end

  def revoke_admin_when_inactive
    self.admin = false unless active
  end

  def set_deactivated_at_when_inactive
    if active.eql?(false) && deactivated_at.blank?
      self.deactivated_at = Time.current
    end
  end
end


================================================
FILE: app/models/vpn.rb
================================================
class Vpn < ApplicationRecord
  belongs_to :user
  belongs_to :group

  has_many :vpn_group_associations
  has_many :groups, through: :vpn_group_associations

  has_many :vpn_group_user_associations
  has_many :users, through: :vpn_group_user_associations

  has_many :vpn_domain_name_servers
  has_many :vpn_search_domains
  has_many :vpn_supplemental_match_domains

  def self.administrator?(user)
    vpn_group_ids = VpnGroupAssociation.select(:group_id).collect(&:group_id)
    managed_group_vpns = user.group_admin.where(group_id: vpn_group_ids)
    !managed_group_vpns.empty?
  end

  def self.managed_vpns(user)

    vpn_group_ids = VpnGroupAssociation.select(:group_id).collect(&:group_id)
    managed_group_vpn_ids = user.
      group_admin.
      select(:group_id).
      where(group_id: vpn_group_ids).
      collect(&:group_id)
    VpnGroupAssociation.where(group_id: managed_group_vpn_ids).collect(&:vpn).uniq
  end

  def self.user_vpns user

    vpn_group_ids = VpnGroupAssociation.select(:group_id).collect(&:group_id)
    user_group_vpn_ids = user.
      group_associations.
      select(:group_id).
      where(group_id: vpn_group_ids).
      collect(&:group_id)
    VpnGroupAssociation.where(group_id: user_group_vpn_ids).collect(&:vpn).uniq
  end

  def migrate_to_new_group
    group_name = "#{name}_vpn_group".downcase.squish.gsub(' ', '_')
    group = Group.where(name: group_name).first
    if group.blank?
      # create a new group for VPN
      group = Group.create(name: group_name, description: "#{name} VPN Access group")
      # Assign VPN to group
      # get all vpn administrators
      admins = []
      groups.each do |admin_group|
        admin_group.group_admins.each do |group_admin|
          group.add_admin group_admin.user
        end
      end

      # get all vpn users
      # add all vpn users to new group
      users.each do |user|
        user.groups << group
      end
      group.save!
      group.vpns << self
    end
  end
end


================================================
FILE: app/models/vpn_domain_name_server.rb
================================================
class VpnDomainNameServer < ApplicationRecord
  belongs_to :vpn
end


================================================
FILE: app/models/vpn_group_association.rb
================================================
class VpnGroupAssociation < ApplicationRecord
  belongs_to :vpn
  belongs_to :group
end



================================================
FILE: app/models/vpn_group_user_association.rb
================================================
class VpnGroupUserAssociation < ApplicationRecord
  belongs_to :vpn
  belongs_to :user
end




================================================
FILE: app/models/vpn_search_domain.rb
================================================
class VpnSearchDomain < ApplicationRecord
  belongs_to :vpn
end


================================================
FILE: app/models/vpn_supplemental_match_domain.rb
================================================
class VpnSupplementalMatchDomain < ApplicationRecord
  belongs_to :vpn
end


================================================
FILE: app/validators/email_validator.rb
================================================
class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless value.to_s.match?(/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i)
      record.errors[attribute] << (options[:message] || 'is not an email')
    end
  end
end


================================================
FILE: app/views/admin/index.html.slim
================================================
h1 hello


================================================
FILE: app/views/api/v1/users/show.json.jbuilder
================================================
json.(@user, :email, :uid, :name, :active, :admin, :home_dir, :shell, :public_key, :user_login_id, :product_name)
json.groups @user.groups, :gid, :name


================================================
FILE: app/views/api_resources/_api_resource.json.jbuilder
================================================
json.extract! api_resource, :id, :name, :description, :access_key, :created_at, :updated_at
json.url api_resource_url(api_resource, format: :json)


================================================
FILE: app/views/api_resources/_form.html.slim
================================================
= form_for @api_resource, :class => 'form-horizontal', html: {novalidate: 'true'} do |f|
  - if @api_resource.errors.any?
    #error_explanation
      h2 = "#{pluralize(@api_resource.errors.count, "error")} prohibited this api_resource from being saved:"
      ul
        - @api_resource.errors.full_messages.each do |message|
          li = message

  hr
    .mb-3
      label for="name"
        | API Name
      = f.text_field :name, required: true, class: "form-control", placeholder: "API Name"
      .invalid-feedback
    .mb-3
      label for="description" 
        | Description
      = f.text_field :description, required: true, class: "form-control", placeholder: "Description"
      .invalid-feedback
    .mb-4
      = f.submit "Save", class: "btn btn-primary pull-right"


================================================
FILE: app/views/api_resources/edit.html.slim
================================================
h1 Editing api_resource

== render 'form'

=> link_to 'Show', @api_resource
'|
=< link_to 'Back', api_resources_path



================================================
FILE: app/views/api_resources/index.html.slim
================================================
.container-fluid.col-md-8
  - if notice.present?
    .alert.alert-primary role="alert"
      #notice
        = notice


  h4 API Keys

  table.table.responsive
    thead
      tr
        th Name
        th Description
        th Access key
        th Owner
        th Access Group
        th

    tbody
      - @api_resources.each do |api_resource|
        tr
          td = api_resource.name
          td = api_resource.description
          td = link_to 'Regenerate', regenerate_access_key_api_resource_path(api_resource), data: { confirm: 'Are you sure?' }
          td = link_to api_resource.user.name, api_resource.user if api_resource.user.present?
          td = link_to api_resource.group.name, api_resource.group if api_resource.group.present?
          td = link_to 'Destroy', api_resource, data: { confirm: 'Are you sure?' }, method: :delete

  br

javascript:
  $("#apiresource-index").addClass("active");


================================================
FILE: app/views/api_resources/index.json.jbuilder
================================================
json.array! @api_resources, partial: 'api_resources/api_resource', as: :api_resource


================================================
FILE: app/views/api_resources/new.html.slim
================================================
.container-fluid.col-md-4.col-md-offset-4
  - if notice.present?
    .alert.alert-primary role="alert"
      #notice
        = notice
  br
  h5 Create new API

  == render 'form'

  = link_to 'Back', api_resources_path


javascript:
  $("#apiresource-index").addClass("active");


================================================
FILE: app/views/api_resources/show.html.slim
================================================
.container-fluid.col-md-4.col-md-offset-4
  - if notice.present?
    .alert.alert-primary role="alert"
      #notice
        = notice
  br
  h5 Show API

  hr
    .mb-3
      label for="name"
        | API Name
      p.form-control-static
        = @api_resource.name
    .mb-3
      label for="description"
        | Description
      p.form-control-static
        = @api_resource.description
    - if flash[:access_key]
      .mb-3
        label for="access_key"
          | Access Key
        .alert.alert-warning role="alert"
          | Important! please make note of this access key, you will see it only this once.
        = text_field_tag :access_key, flash[:access_key], class: 'form-control', readonly: ""
    .mb-4
      => link_to 'Edit', edit_api_resource_path(@api_resource)
      '|
      => link_to 'Regenerate Access Key', regenerate_access_key_api_resource_path(@api_resource), :data => {:confirm => 'Are you sure?'}
      '|
      =< link_to 'Back', api_resources_path


================================================
FILE: app/views/api_resources/show.json.jbuilder
================================================
json.partial! "api_resources/api_resource", api_resource: @api_resource


================================================
FILE: app/views/application/_admin.html.slim
================================================
div
  ul.navbar-nav
    li.nav-item.active
      a.nav-link href="#" Users
    li.nav-item
      a.nav-link href="#" Groups


================================================
FILE: app/views/application/_groups_header.html.slim
================================================
#notice
  = notice
- if current_user.admin?
  = form_tag groups_path, method: 'post' do
    .row
      .col-md-8
        = text_field_tag "group[name]", "", class: "form-control", autofocus:  true
      .col-md-4
        = submit_tag "Add Group", class: "form-control btn btn-sm btn-primary", name: nil

br
= form_tag groups_path, method: 'get' do
  .row
    .col-md-8
      = text_field_tag :group_search, params[:group_search], class: "form-control" , autofocus: true
    .col-md-4
      = submit_tag "Search groups", class: "form-control btn btn-sm btn-primary", name: nil

hr



================================================
FILE: app/views/application/_host_header.html.slim
================================================
ul.nav.nav-tabs
  li#host_machine role="presentation" 
    = link_to "MyHosts", host_machines_path
  li#group role="presentation" 
    = link_to "Groups", groups_path
  li#host_machine_search role="presentation" 
    a href="#"  Search Host 
  li#host_machine_group_search role="presentation" 
    a href="#" Search Host Groups
p
  br



================================================
FILE: app/views/common/errors.json.jbuilder
================================================
json.success false
json.errors errors


================================================
FILE: app/views/groups/_form.html.slim
================================================


================================================
FILE: app/views/groups/index.html.slim
================================================
.container-fluid.col.container-profile
  - if current_user.admin?
    = form_tag groups_path, method: 'get', class: "p-2" do
      .input-group
        = text_field_tag :group_search, params[:group_search], class: "form-control" , autofocus: true, placeholder: "Search group name..."
        .input-group-append
          = submit_tag "Search", class: "button btn btn-secondary", name: nil

   
  - if @groups.count > 0
    h5 Your managed groups
    .table-responsive
      table.table.table-striped
        thead
          tr
            th Name
            th Gid
            th Group Admin
        tbody
          - @groups.each do |group|
            tr
              td
                = link_to "#{group.name}", group_path(group)
              td
                = "#{group.gid}"
              td
                - if group.group_admins.present?
                  - group.group_admins.each do |admin| 
                    .row
                      =  "#{admin.user.try(:name)}"
   

javascript:
  $("#group-index").addClass("active");



================================================
FILE: app/views/groups/new.html.slim
================================================
.container-fluid.col-md-4.col-md-offset-4
  h5 Add new group
  = form_for @group, :class => 'form-horizontal' do |f|
    - if @group.errors.any?
      #error_explanation
        h2 = "#{pluralize(@group.errors.count, "error")} prohibited this vpn from being saved:"
        ul
          - @group.errors.full_messages.each do |message|
            li = message

    hr
    .mb-3
      label for="name" 
        | Name
      = f.text_field :name, required: true, class: "form-control", placeholder: "Group Name"
      .invalid-feedback
        | Please enter a name

    .mb-4
      = f.submit "Add Group", class: "btn btn-primary pull-right"



================================================
FILE: app/views/groups/show.html.slim
================================================
.container.col-md-8
  h5.mb-3 Group Details
  hr
  .row
    .col 
      = "Group Name: #{@group.name}"
    .col
      = "Group ID: #{@group.gid}"
  br
  - if current_user.admin? or @group.admin?(current_user)
    = form_tag add_admin_to_group_path(@group.id), method: :post do
      .row
        .col
          | Group Administrator
        .col
          - if @group.group_admins.present?
            - @group.group_admins.each do |admin| 
              .row
                .col
                  = "#{admin.user.try(:name)}"
                .col
                  = "#{admin.user.try(:email)}"
                .col 
                  - if (current_user.admin?)
                    = link_to "Remove?", [@group, admin], method: :delete, data: {confirm: "Are you sure to remove #{admin.user.try(:name)} ?"}
          - if (current_user.admin?)
            .row
              .col-8
                = text_field_tag "user_id", "", id: "assign_admin_user_id", class: "form-control"
              .col
                = submit_tag "Assign admin", class: "form-control btn btn-md btn-primary", disabled: true
            .row
              .col-8
                = check_box_tag "assign_admin_include_inactive_user", "true", false
                = " Include Inactive User"
              .col
  a name="group_members"
  br
  .card
    .card-body
      h6.card-title Groups Members (#{@group_users.length})
      .table-responsive
        table.table.table-striped
          thead
            tr
              th User Details
              th Email Address
              th Join Date
              th Expiration Date
              th
          tbody
            - @group_users.sort_by{ |user| user.email}.each do |user|
              tr
                td
                  - if user.active?
                    = link_to "#{user.name}", user_path(user)
                  - else
                    = link_to "#{user.name} (inactive)", user_path(user)
                td
                  = "#{user.email}"
                td
                  = "#{user.join_date.strftime("%v")}"
                td
                  = "#{user.group_expiration_date}"
                td
                  = 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)

      br
      = "*This group does not have any group members" if @group_users.length == 0
      br
      - if current_user.admin or @group.admin?(current_user)
        .h7 Assign members
        = form_tag  add_user_to_group_path(@group.id), method: :post do
          .row
            .col
              .form_group
                label
                  | User
                = text_field_tag "user_id", "", id: "add_user_user_id", class: "form-control"
                = check_box_tag "add_user_include_inactive_user", "true", false
                = " Include Inactive User"
            .col
              .form_group
                label
                  | Assignment Expiration date
                = date_field_tag "expiration_date", "", id: "expiration_date", class: "form-control"
                | * expiration date is optional (unspecified means permanent)
            .col
              br
              = submit_tag "Add User", class: "form-control btn-md btn-primary", disabled: true            
  br
  .card
    .card-body
      h6.card-title VPN Access
      .table-responsive
        table.table.table-striped
          thead
            tr
              th Vpn Details
              th Vpn name
              th Vpn Host name
              th
          tbody
            - @group.vpns.sort_by{ |vpn| vpn.name}.uniq{|vpn| vpn.id}.each do |vpn|
              tr
                td
                  = link_to "#{vpn.name}", vpn_path(vpn)
                td
                  = "#{vpn.name}"
                td
                  = "#{vpn.host_name}"
                td
                  - if current_user.admin?
                    = link_to "Delete", [@group, vpn], method: :delete, data: {confirm: 'Are you sure to remove this vpn from the group?'}
      br
      = "*This group does not have any VPNs associated with it" if @group.vpns.count == 0
      br
      - if current_user.admin? 
        = form_tag  add_vpn_to_group_path(@group.id), method: :post do
          .row
            .col 
              | Assign VPNs
            .col
            .col
              = text_field_tag "vpn_id", "", id: "add_vpn_vpn_id", class: "form-control"
            .col
              = submit_tag "Add Vpn", class: "form-control btn-md btn-primary", disabled: true
  br
  .card
    .card-body
      h6.card-title Group access hosts
      .table-responsive
        table.table.table-striped
          thead
            tr
              th Host Name
              th 
          tbody
            - @group.host_machines.each do |machine|
              tr
                td
                  = link_to "#{machine.name}", host_machine_path(machine)
                td
                  = link_to "Delete", [@group, machine], method: :delete, data: {confirm: 'Are you sure to remove this machine from the group??'} if current_user.admin?
      br
      = "*This group does not have any host access" if @group.host_machines.count == 0
      br
      hr
      - if current_user.admin?
        = form_tag  add_machine_to_group_path(@group.id), method: :post do
          .row
            .col 
              | Assign Hosts
            .col
            .col
              = text_field_tag "machine_id", "", id: "add_machine_machine_id", class: "form-control"
            .col
              = submit_tag "Add Host", class: "form-control btn-md btn-primary", disabled: true

javascript:
  $("#group-index").addClass("active");


================================================
FILE: app/views/home/index.html.slim
================================================
.form-signin
  .row
    .col
      h1 Gate-SSO 
  br
  .row
    .col
      h3 Single Sign-On
  br
  br
  br
  br
  - case ENV['SIGN_IN_TYPE']
  - when 'form'
    = form_tag user_sign_in_path, method: :post do
      .row
        .col-sm-10.offset-sm-1
          = text_field_tag :name, '', class: 'form-control', placeholder: 'Name', required: true
          = text_field_tag :email, '', class: 'form-control', placeholder: 'Email', required: true, type: 'email'
          = submit_tag 'Sign in', class: 'form-control btn-md btn-primary'
  - else
    .row
      .col
      .col
        a.btn.btn-block.btn-social.btn-google href="#{url_for user_google_oauth2_omniauth_authorize_path}"
          span.fa.fa-google
          | Sign in with Google
      .col
 


================================================
FILE: app/views/host_machines/index.html.slim
================================================
.container-fluid.col-md-6.col-md-offset-2
  = form_tag host_machines_path, method: 'get', class: "p-2" do
    .input-group
      = text_field_tag :host_machine_search, params[:host_machine_search], class: "form-control" , autofocus: true, placeholder: "Search host..."
      .input-group-append
        = submit_tag "Search", class: "button btn btn-secondary", name: nil




.container-fluid.col-md-6.col-md-offset-2
  - if @host_machines.count > 0
    .table-responsive
      table.table.table-striped
        thead
          tr
            th Name
        tbody
          - @host_machines.each do |host_machine|
            tr
              td
                = link_to "#{host_machine.name}", host_machine
  br

javascript:
  $("#hostmachine-index").addClass("active");


================================================
FILE: app/views/host_machines/new.html.slim
================================================
.container-fluid.col-md-4.col-md-offset-4
  = form_tag host_machines_path, method: 'post' do
    .input-group 
      = text_field_tag "host_machine[name]", "", class: "form-control", autofocus:  true
    .input-group
      = submit_tag "Add Host machine", class: "form-control btn btn-sm btn-primary", name: nil



================================================
FILE: app/views/host_machines/show.html.slim
================================================
.container.col-md-8
  h5.mb-3 Host Details
  hr
  .row
    .col 
      = @host_machine.name
  - if current_user.admin?
    .card
      .card-body
        h6.card-title Configurations 
        hr
        = form_tag "/host_machines/#{@host_machine.id}", method: 'put' do
          .row
            .col
              .custom-control.custom-checkbox
                = check_box "host_machine", "default_admins", class: "custom-control-input", id: "admin-checkbox" 
                label.custom-control-label for="admin-checkbox" Default Admins?
            .col
              = submit_tag "Update", class: "form-control btn-md btn-primary"
  br

  .card
    .card-body
      .table-responsive
        table.table.table-striped
          thead
            tr
              th Group Name
              th 
          tbody
            - @host_machine.groups.each do |group|
              tr
                td
                  = link_to "#{group.name}", group_path(group)
                td
                  = link_to "Delete", [@machine, group], method: :delete, data: {confirm: 'Are you sure to remove this machine from the group??'} if current_user.admin?
      br
      = "*This host does not have any group" if @host_machine.groups.count == 0
      br
      hr
      - if current_user.admin?
        = form_tag  add_group_to_machine_path, method: :post do
          .row
            .col 
              | Assign group
            .col
            .col
              = text_field_tag "group_id", "", class: "form-control"
            .col
              = submit_tag "Add group", class: "form-control btn-md btn-primary", disabled: true



================================================
FILE: app/views/layouts/application.html.slim
================================================
doctype html
html lang="en"
  head
    meta charset="utf-8"
    meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"
    meta name="viewport" content="width=device-width, initial-scale=1.0"
    title= content_for?(:title) ? yield(:title) : "Gate"
    = csrf_meta_tags
    = stylesheet_link_tag "application", :media => "all"
    = stylesheet_link_tag "general", :media => "all"
    = stylesheet_link_tag "//cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.4/css/selectize.bootstrap3.min.css"
    = javascript_include_tag "application"
    = javascript_include_tag "//cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.4/js/standalone/selectize.min.js"
    /! Le HTML5 sh
Download .txt
gitextract_ouflf_22/

├── .deepsource.toml
├── .dockerignore
├── .gitignore
├── .rspec
├── .rubocop.yml
├── .ruby-version
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── VERSION
├── api_blueprint/
│   ├── bin/
│   │   └── dredd_server.sh
│   ├── group.apib
│   ├── hooks/
│   │   └── dredd_hooks.rb
│   ├── user.apib
│   └── vpns.apib
├── app/
│   ├── assets/
│   │   ├── config/
│   │   │   └── manifest.js
│   │   ├── images/
│   │   │   └── .keep
│   │   ├── javascripts/
│   │   │   ├── admin.coffee
│   │   │   ├── api_resources.coffee
│   │   │   ├── application.js
│   │   │   ├── bootstrap.js.coffee
│   │   │   ├── group.coffee
│   │   │   ├── groups.coffee
│   │   │   ├── home.coffee
│   │   │   ├── host_access_groups.coffee
│   │   │   ├── host_machine_groups.coffee
│   │   │   ├── host_machines.coffee
│   │   │   ├── nss.coffee
│   │   │   ├── omniauth_callbacks.coffee
│   │   │   ├── profile.coffee
│   │   │   ├── users.coffee
│   │   │   ├── utilities.coffee
│   │   │   └── viewport.js
│   │   └── stylesheets/
│   │       ├── application.scss
│   │       ├── bootstrap-social.scss
│   │       ├── general.css
│   │       ├── home.scss.erb
│   │       ├── profile.css
│   │       └── scaffolds.scss
│   ├── clients/
│   │   └── data_dog_client.rb
│   ├── controllers/
│   │   ├── admin_controller.rb
│   │   ├── api/
│   │   │   └── v1/
│   │   │       ├── base_controller.rb
│   │   │       ├── endpoints_controller.rb
│   │   │       ├── groups_controller.rb
│   │   │       ├── users_controller.rb
│   │   │       └── vpns_controller.rb
│   │   ├── api_resources_controller.rb
│   │   ├── application_controller.rb
│   │   ├── concerns/
│   │   │   └── .keep
│   │   ├── groups_controller.rb
│   │   ├── home_controller.rb
│   │   ├── host_controller.rb
│   │   ├── host_machine_groups_controller.rb
│   │   ├── host_machines_controller.rb
│   │   ├── nss_controller.rb
│   │   ├── organisations_controller.rb
│   │   ├── pings_controller.rb
│   │   ├── profile_controller.rb
│   │   ├── saml_idp_controller.rb
│   │   ├── users/
│   │   │   ├── auth_controller.rb
│   │   │   └── omniauth_callbacks_controller.rb
│   │   ├── users_controller.rb
│   │   ├── vpn_domain_name_servers_controller.rb
│   │   └── vpns_controller.rb
│   ├── helpers/
│   │   ├── admin_helper.rb
│   │   ├── api_resources_helper.rb
│   │   ├── application_helper.rb
│   │   ├── group_helper.rb
│   │   ├── groups_helper.rb
│   │   ├── home_helper.rb
│   │   ├── host_access_groups_helper.rb
│   │   ├── host_machine_groups_helper.rb
│   │   ├── host_machines_helper.rb
│   │   ├── nss_helper.rb
│   │   ├── omniauth_callbacks_helper.rb
│   │   ├── profile_helper.rb
│   │   └── users_helper.rb
│   ├── lib/
│   │   ├── datadog.rb
│   │   └── saml_app.rb
│   ├── mailers/
│   │   └── .keep
│   ├── models/
│   │   ├── .keep
│   │   ├── access_token.rb
│   │   ├── api_resource.rb
│   │   ├── application_record.rb
│   │   ├── concerns/
│   │   │   ├── .keep
│   │   │   └── ms_chap_auth.rb
│   │   ├── endpoint.rb
│   │   ├── group.rb
│   │   ├── group_admin.rb
│   │   ├── group_association.rb
│   │   ├── group_endpoint.rb
│   │   ├── host.rb
│   │   ├── host_access_group.rb
│   │   ├── host_machine.rb
│   │   ├── ip_address.rb
│   │   ├── organisation.rb
│   │   ├── saml_app_config.rb
│   │   ├── user.rb
│   │   ├── vpn.rb
│   │   ├── vpn_domain_name_server.rb
│   │   ├── vpn_group_association.rb
│   │   ├── vpn_group_user_association.rb
│   │   ├── vpn_search_domain.rb
│   │   └── vpn_supplemental_match_domain.rb
│   ├── validators/
│   │   └── email_validator.rb
│   └── views/
│       ├── admin/
│       │   └── index.html.slim
│       ├── api/
│       │   └── v1/
│       │       └── users/
│       │           └── show.json.jbuilder
│       ├── api_resources/
│       │   ├── _api_resource.json.jbuilder
│       │   ├── _form.html.slim
│       │   ├── edit.html.slim
│       │   ├── index.html.slim
│       │   ├── index.json.jbuilder
│       │   ├── new.html.slim
│       │   ├── show.html.slim
│       │   └── show.json.jbuilder
│       ├── application/
│       │   ├── _admin.html.slim
│       │   ├── _groups_header.html.slim
│       │   └── _host_header.html.slim
│       ├── common/
│       │   └── errors.json.jbuilder
│       ├── groups/
│       │   ├── _form.html.slim
│       │   ├── index.html.slim
│       │   ├── new.html.slim
│       │   └── show.html.slim
│       ├── home/
│       │   └── index.html.slim
│       ├── host_machines/
│       │   ├── index.html.slim
│       │   ├── new.html.slim
│       │   └── show.html.slim
│       ├── layouts/
│       │   ├── application.html.slim
│       │   ├── home.html.slim
│       │   └── profile.html.slim.disabled
│       ├── nss/
│       │   └── add_host.json.jbuilder
│       ├── organisations/
│       │   ├── _form.html.slim
│       │   ├── config_saml_app.html.slim
│       │   ├── index.html.slim
│       │   ├── new.html.slim
│       │   ├── saml_apps/
│       │   │   └── _datadog.html.erb
│       │   └── show.html.slim
│       ├── profile/
│       │   ├── _group_search.html.slim
│       │   ├── _user_search.html.slim
│       │   ├── group_admin.html.slim
│       │   ├── list.html.slim
│       │   ├── public_key.html.slim
│       │   ├── show.html.slim
│       │   ├── user.html.slim
│       │   └── user_admin.html.slim
│       ├── saml_idp/
│       │   └── idp/
│       │       └── new.html.erb
│       ├── users/
│       │   ├── _search.html.slim
│       │   ├── index.html.slim
│       │   ├── new.html.erb
│       │   └── show.html.slim
│       └── vpns/
│           ├── _form.html.slim
│           ├── edit.html.slim
│           ├── index.html.slim
│           ├── new.html.slim
│           └── show.html.slim
├── bin/
│   ├── bundle
│   ├── rails
│   ├── rake
│   ├── setup
│   └── update
├── config/
│   ├── application.rb
│   ├── boot.rb
│   ├── cable.yml
│   ├── database.yml
│   ├── environment.rb
│   ├── environments/
│   │   ├── development.rb
│   │   ├── integration.rb
│   │   ├── production.rb
│   │   └── test.rb
│   ├── initializers/
│   │   ├── application_controller_renderer.rb
│   │   ├── assets.rb
│   │   ├── backtrace_silencers.rb
│   │   ├── content_security_policy.rb
│   │   ├── cookies_serializer.rb
│   │   ├── devise.rb
│   │   ├── dotenv.rb
│   │   ├── filter_parameter_logging.rb
│   │   ├── inflections.rb
│   │   ├── mime_types.rb
│   │   ├── new_framework_defaults.rb
│   │   ├── new_framework_defaults_7_0.rb
│   │   ├── permissions_policy.rb
│   │   ├── session_store.rb
│   │   └── wrap_parameters.rb
│   ├── locales/
│   │   ├── devise.en.yml
│   │   ├── en.bootstrap.yml
│   │   └── en.yml
│   ├── newrelic.yml
│   ├── puma.rb
│   ├── redis.yml
│   ├── routes.rb
│   ├── schedule.rb
│   ├── secrets.yml
│   ├── spring.rb
│   └── storage.yml
├── config.ru
├── db/
│   ├── migrate/
│   │   ├── 20160419122430_devise_create_users.rb
│   │   ├── 20160419132647_add_provider_to_users.rb
│   │   ├── 20160419144739_add_name_to_users.rb
│   │   ├── 20160427123146_add_auth_key_to_user.rb
│   │   ├── 20160427123233_add_provisioning_uri_to_user.rb
│   │   ├── 20160519042340_add_active_to_users.rb
│   │   ├── 20160519064340_add_default_value_to_users.rb
│   │   ├── 20160615044834_create_hosts.rb
│   │   ├── 20160615045052_add_admin_to_user.rb
│   │   ├── 20160615112805_add_user_to_host.rb
│   │   ├── 20160628140022_add_deleted_at_to_host.rb
│   │   ├── 20160628140440_add_deleted_by_to_host.rb
│   │   ├── 20160629043358_add_homedir_to_user.rb
│   │   ├── 20160629043415_add_shell_to_user.rb
│   │   ├── 20160629075435_create_groups.rb
│   │   ├── 20160701090045_create_group_associations.rb
│   │   ├── 20160701112600_add_deleted_properties_to_group.rb
│   │   ├── 20160707115313_create_access_tokens.rb
│   │   ├── 20160714115228_add_public_key_to_user.rb
│   │   ├── 20160908081651_create_host_machines.rb
│   │   ├── 20161003145832_create_host_access_groups.rb
│   │   ├── 20170803140620_add_user_login_id_to_user.rb
│   │   ├── 20171013115441_create_versions.rb
│   │   ├── 20171016064705_remove_index_group_name.rb
│   │   ├── 20171016071526_add_unique_index_on_groups_name.rb
│   │   ├── 20171031060034_create_group_admin.rb
│   │   ├── 20171031060217_add_foreign_key_ref_on_group_admin.rb
│   │   ├── 20171031100758_create_vpns.rb
│   │   ├── 20171031101026_create_vpn_group_association.rb
│   │   ├── 20171031103518_add_foreign_key_ref_on_vpn_group_association.rb
│   │   ├── 20171031113123_create_vpn_group_user_association.rb
│   │   ├── 20171031121702_add_foreign_key_ref_on_vpn_group_user_association.rb
│   │   ├── 20171102071909_add_ip_address_to_vpns.rb
│   │   ├── 20171107114249_remove_url_from_vpns.rb
│   │   ├── 20171108130234_add_user_id_to_access_token.rb
│   │   ├── 20171108130353_add_foreign_key_ref_on_access_tokens.rb
│   │   ├── 20171124090240_add_uuid_to_vpns.rb
│   │   ├── 20171124114427_create_vpn_domain_name_servers.rb
│   │   ├── 20171124114830_create_vpn_search_domains.rb
│   │   ├── 20171124115925_create_vpn_supplemental_match_domains.rb
│   │   ├── 20180104081814_add_product_name_to_users.rb
│   │   ├── 20180202102206_create_saml_service_providers.rb
│   │   ├── 20180214050204_add_api_key_to_host_machines.rb
│   │   ├── 20180214052451_create_ip_addresses.rb
│   │   ├── 20180214052644_add_host_machine_to_ip_address.rb
│   │   ├── 20180219150818_add_description_to_group.rb
│   │   ├── 20180222135930_add_access_key_to_host_machine.rb
│   │   ├── 20180222140000_add_access_key_to_user.rb
│   │   ├── 20180227051732_create_api_resources.rb
│   │   ├── 20180301010021_add_user_to_api_resources.rb
│   │   ├── 20180301010035_add_group_to_api_resources.rb
│   │   ├── 20180306231200_add_deactivated_at_to_users.rb
│   │   ├── 20180311082600_rename_access_key_in_api_resources.rb
│   │   ├── 20180311161200_rename_token_in_access_tokens.rb
│   │   ├── 20180318083000_create_indexes_to_speedup_nss_controller.rb
│   │   ├── 20180613074108_create_organisations.rb
│   │   ├── 20180613165050_drop_saml_service_providers.rb
│   │   ├── 20180723175600_update_organisations_for_saml.rb
│   │   ├── 20181002023107_add_default_admins_to_host_machines.rb
│   │   ├── 20181016093315_create_saml_app_configs.rb
│   │   ├── 20181208184236_add_fields_to_user.rb
│   │   ├── 20190624024930_add_expiration_date_to_group_associations.rb
│   │   ├── 20190820070910_create_endpoints.rb
│   │   ├── 20190820075040_create_group_endpoints.rb
│   │   ├── 20190820080624_add_foreign_key_ref_on_group_endpoints.rb
│   │   ├── 20200113065717_add_sessions_table.rb
│   │   ├── 20220926001848_add_service_name_to_active_storage_blobs.active_storage.rb
│   │   ├── 20220926001849_create_active_storage_variant_records.active_storage.rb
│   │   └── 20220926001850_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
│   ├── schema.rb
│   ├── seeds/
│   │   ├── development.rb
│   │   ├── production.rb
│   │   └── test.rb
│   └── seeds.rb
├── docker-compose.yaml
├── docs/
│   ├── additional_setup.md
│   ├── administration.md
│   ├── dredd_setup.md
│   ├── newrelic.md
│   └── oauth_setup.md
├── dredd.yml
├── lib/
│   ├── assets/
│   │   └── .keep
│   ├── tasks/
│   │   ├── .keep
│   │   ├── app.rake
│   │   ├── setup.rake
│   │   ├── users.rake
│   │   └── vpn.rake
│   └── vpn/
│       ├── mobileconfig.erb
│       ├── mobileconfig.rb
│       └── namespace.rb
├── public/
│   ├── 404.html
│   ├── 422.html
│   ├── 500.html
│   └── robots.txt
├── scripts/
│   ├── gen-client-conf
│   └── gen-client-keys
├── setup.sh
└── spec/
    ├── clients/
    │   └── data_dog_client_spec.rb
    ├── controllers/
    │   ├── admin_controller_spec.rb
    │   ├── api/
    │   │   └── v1/
    │   │       ├── api_controller_spec.rb
    │   │       ├── endpoints_controller_spec.rb
    │   │       ├── groups_controller_spec.rb
    │   │       ├── users_controller_spec.rb
    │   │       └── vpns_controller_spec.rb
    │   ├── api_resources_controller_spec.rb
    │   ├── groups_controller_spec.rb
    │   ├── home_controller_spec.rb
    │   ├── host_machine_groups_controller_spec.rb
    │   ├── host_machines_controller_spec.rb
    │   ├── nss_controller_spec.rb
    │   ├── omniauth_callbacks_controller_spec.rb
    │   ├── organisations_controller_spec.rb
    │   ├── profile_controller_spec.rb
    │   ├── users/
    │   │   └── auth_controller_spec.rb
    │   ├── users_controller_spec.rb
    │   └── vpns_controller_spec.rb
    ├── factories/
    │   ├── access_tokens.rb
    │   ├── api_resources.rb
    │   ├── endpoints.rb
    │   ├── group_associations.rb
    │   ├── groups.rb
    │   ├── host_access_groups.rb
    │   ├── host_machine_groups.rb
    │   ├── host_machines.rb
    │   ├── ip_addresses.rb
    │   ├── organisations.rb
    │   ├── saml_app_configs.rb
    │   ├── user_host_access_groups.rb
    │   ├── users.rb
    │   └── vpns.rb
    ├── features/
    │   ├── layout_spec.rb
    │   ├── organisations/
    │   │   ├── add_user_saml_app_spec.rb
    │   │   ├── config_saml_app_spec.rb
    │   │   ├── create_spec.rb
    │   │   ├── list_spec.rb
    │   │   ├── list_user_saml_app_spec.rb
    │   │   ├── remove_user_saml_app_spec.rb
    │   │   ├── save_config_saml_app_spec.rb
    │   │   ├── setup_saml_spec.rb
    │   │   └── update_spec.rb
    │   ├── saml/
    │   │   └── show_spec.rb
    │   └── users/
    │       ├── create_user_spec.rb
    │       └── regenerate_auth_spec.rb
    ├── helpers/
    │   └── application_helper_spec.rb
    ├── lib/
    │   ├── datadog_spec.rb
    │   ├── saml_app_spec.rb
    │   └── tasks/
    │       └── users_rake_spec.rb
    ├── models/
    │   ├── access_token_spec.rb
    │   ├── api_resource_spec.rb
    │   ├── endpoint_spec.rb
    │   ├── group_association_spec.rb
    │   ├── group_endpoint_spec.rb
    │   ├── group_spec.rb
    │   ├── host_machine_spec.rb
    │   ├── host_spec.rb
    │   ├── ip_address_spec.rb
    │   ├── organisation_spec.rb
    │   ├── user_spec.rb
    │   └── vpn_spec.rb
    ├── rails_helper.rb
    ├── routing/
    │   └── api_resources_routing_spec.rb
    ├── spec_helper.rb
    ├── support/
    │   └── helpers/
    │       └── x509_certificate_helper.rb
    └── views/
        ├── api_resources/
        │   ├── edit.html.slim_spec.rb
        │   ├── index.html.slim_spec.rb
        │   ├── new.html.slim_spec.rb
        │   └── show.html.slim_spec.rb
        ├── groups/
        │   └── show.html.slim_spec.rb
        └── layouts/
            └── home.html.slim_spec.rb
Download .txt
SYMBOL INDEX (488 symbols across 136 files)

FILE: app/clients/data_dog_client.rb
  class DataDogClient (line 1) | class DataDogClient
    method initialize (line 5) | def initialize(app_key, api_key)
    method get_user (line 15) | def get_user(email)
    method new_user (line 25) | def new_user(email)
    method activate_user (line 35) | def activate_user(email)
    method deactivate_user (line 45) | def deactivate_user(email)
    method append_auth (line 57) | def append_auth(str)

FILE: app/controllers/admin_controller.rb
  class AdminController (line 1) | class AdminController < ApplicationController
    method index (line 2) | def index; end

FILE: app/controllers/api/v1/base_controller.rb
  class ::Api::V1::BaseController (line 1) | class ::Api::V1::BaseController < ActionController::Base
    method authenticate_user_from_token! (line 5) | def authenticate_user_from_token!
    method current_user (line 13) | def current_user
    method get_token (line 20) | def get_token
    method raise_unauthorized (line 30) | def raise_unauthorized

FILE: app/controllers/api/v1/endpoints_controller.rb
  class ::Api::V1::EndpointsController (line 1) | class ::Api::V1::EndpointsController < ::Api::V1::BaseController
    method create (line 4) | def create
    method add_group (line 17) | def add_group
    method authorize_user (line 35) | def authorize_user
    method group_param (line 41) | def group_param
    method endpoint_param (line 45) | def endpoint_param

FILE: app/controllers/api/v1/groups_controller.rb
  class ::Api::V1::GroupsController (line 1) | class ::Api::V1::GroupsController < ::Api::V1::BaseController
    method create (line 2) | def create
    method add_user (line 29) | def add_user
    method group_params (line 45) | def group_params

FILE: app/controllers/api/v1/users_controller.rb
  class ::Api::V1::UsersController (line 1) | class ::Api::V1::UsersController < ::Api::V1::BaseController
    method create (line 4) | def create
    method show (line 15) | def show
    method deactivate (line 29) | def deactivate
    method update (line 46) | def update
    method set_user (line 54) | def set_user
    method user_params (line 69) | def user_params

FILE: app/controllers/api/v1/vpns_controller.rb
  class ::Api::V1::VpnsController (line 1) | class ::Api::V1::VpnsController < ::Api::V1::BaseController
    method create (line 4) | def create
    method assign_group (line 21) | def assign_group
    method set_vpn (line 31) | def set_vpn
    method vpn_params (line 35) | def vpn_params

FILE: app/controllers/api_resources_controller.rb
  class ApiResourcesController (line 1) | class ApiResourcesController < ApplicationController
    method index (line 8) | def index
    method show (line 15) | def show; end
    method authenticate (line 17) | def authenticate
    method new (line 27) | def new
    method edit (line 32) | def edit; end
    method create (line 36) | def create
    method update (line 57) | def update
    method destroy (line 71) | def destroy
    method search (line 81) | def search
    method regenerate_access_key (line 93) | def regenerate_access_key
    method set_api_resource (line 109) | def set_api_resource
    method api_resource_params (line 114) | def api_resource_params
    method authorize_user (line 118) | def authorize_user

FILE: app/controllers/application_controller.rb
  class ApplicationController (line 1) | class ApplicationController < ActionController::Base
    method render_404 (line 6) | def render_404
    method authenticate_access_token! (line 14) | def authenticate_access_token!
    method render_error (line 20) | def render_error(errors, status = 400)

FILE: app/controllers/groups_controller.rb
  class GroupsController (line 1) | class GroupsController < ApplicationController
    method index (line 7) | def index
    method create (line 17) | def create
    method new (line 35) | def new
    method show (line 39) | def show
    method delete_machine (line 53) | def delete_machine
    method delete_user (line 62) | def delete_user
    method add_user (line 74) | def add_user
    method add_machine (line 94) | def add_machine
    method add_admin (line 108) | def add_admin
    method remove_admin (line 120) | def remove_admin
    method add_vpn (line 132) | def add_vpn
    method delete_vpn (line 144) | def delete_vpn
    method add_group (line 157) | def add_group
    method delete_group (line 172) | def delete_group
    method list (line 186) | def list
    method search (line 198) | def search
    method set_group (line 210) | def set_group
    method group_params (line 215) | def group_params
    method expiration_date_param (line 219) | def expiration_date_param

FILE: app/controllers/home_controller.rb
  class HomeController (line 1) | class HomeController < ApplicationController
    method check_signed_in (line 5) | def check_signed_in
    method index (line 9) | def index; end

FILE: app/controllers/host_controller.rb
  class HostController (line 1) | class HostController < ApplicationController
    method add_host (line 3) | def add_host
    method delete_host (line 15) | def delete_host

FILE: app/controllers/host_machine_groups_controller.rb
  class HostMachineGroupsController (line 1) | class HostMachineGroupsController < ApplicationController
    method show (line 2) | def show
    method create (line 6) | def create
    method set_host_machine (line 18) | def set_host_machine
    method host_machine_params (line 23) | def host_machine_params

FILE: app/controllers/host_machines_controller.rb
  class HostMachinesController (line 1) | class HostMachinesController < ApplicationController
    method index (line 6) | def index
    method create (line 18) | def create
    method show (line 31) | def show
    method update (line 36) | def update
    method add_group (line 41) | def add_group
    method delete_group (line 56) | def delete_group
    method search (line 63) | def search
    method set_host_machine (line 75) | def set_host_machine
    method host_machine_params (line 80) | def host_machine_params
    method authorize_user (line 84) | def authorize_user

FILE: app/controllers/nss_controller.rb
  class NssController (line 1) | class NssController < ApplicationController
    method host (line 5) | def host
    method add_host (line 23) | def add_host
    method group (line 40) | def group
    method passwd (line 56) | def passwd
    method shadow (line 71) | def shadow
    method groups_list (line 97) | def groups_list

FILE: app/controllers/organisations_controller.rb
  class OrganisationsController (line 1) | class OrganisationsController < ApplicationController
    method index (line 12) | def index
    method new (line 16) | def new
    method config_saml_app (line 20) | def config_saml_app
    method save_config_saml_app (line 33) | def save_config_saml_app
    method remove_user_saml_app (line 45) | def remove_user_saml_app
    method add_user_saml_app (line 56) | def add_user_saml_app
    method create (line 67) | def create
    method update (line 78) | def update
    method show (line 89) | def show
    method setup_saml (line 93) | def setup_saml
    method authorize_user (line 105) | def authorize_user
    method load_org (line 114) | def load_org
    method validate_app_name (line 122) | def validate_app_name
    method organisation_params (line 129) | def organisation_params

FILE: app/controllers/pings_controller.rb
  class PingsController (line 1) | class PingsController < ApplicationController
    method show (line 2) | def show

FILE: app/controllers/profile_controller.rb
  class ProfileController (line 1) | class ProfileController < ApplicationController
    method regen_auth (line 7) | def regen_auth
    method show (line 12) | def show
    method user_admin (line 19) | def user_admin
    method group_admin (line 40) | def group_admin
    method user_id (line 61) | def user_id
    method download_vpn (line 71) | def download_vpn
    method download_vpn_for_ios_and_mac (line 84) | def download_vpn_for_ios_and_mac
    method download_vpn_for_user (line 99) | def download_vpn_for_user
    method authenticate (line 103) | def authenticate
    method authenticate_ms_chap (line 112) | def authenticate_ms_chap
    method authenticate_cas (line 117) | def authenticate_cas
    method authenticate_pam (line 135) | def authenticate_pam
    method verify (line 144) | def verify
    method list (line 158) | def list
    method admin (line 167) | def admin
    method update (line 188) | def update
    method user_edit (line 196) | def user_edit; end
    method public_key_update (line 198) | def public_key_update
    method public_key (line 207) | def public_key
    method user (line 214) | def user
    method admin_active (line 233) | def admin_active

FILE: app/controllers/saml_idp_controller.rb
  class SamlIdpController (line 4) | class SamlIdpController < ApplicationController
    method show (line 8) | def show
    method idp_authenticate (line 21) | def idp_authenticate(email, password)
    method idp_make_saml_response (line 26) | def idp_make_saml_response(found_user)
    method idp_logout (line 30) | def idp_logout
    method setup_saml_configuration (line 35) | def setup_saml_configuration

FILE: app/controllers/users/auth_controller.rb
  class Users::AuthController (line 1) | class Users::AuthController < ApplicationController
    method log_in (line 3) | def log_in

FILE: app/controllers/users/omniauth_callbacks_controller.rb
  class Users::OmniauthCallbacksController (line 1) | class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksCont...
    method google_oauth2 (line 2) | def google_oauth2

FILE: app/controllers/users_controller.rb
  class UsersController (line 1) | class UsersController < ApplicationController
    method index (line 5) | def index
    method show (line 11) | def show
    method new (line 44) | def new
    method create (line 53) | def create
    method update (line 69) | def update
    method search (line 81) | def search
    method regenerate_token (line 101) | def regenerate_token
    method user_params (line 125) | def user_params
    method form_response (line 131) | def form_response(message)
    method product_name (line 137) | def product_name
    method authorize_user (line 141) | def authorize_user

FILE: app/controllers/vpn_domain_name_servers_controller.rb
  class VpnsController (line 1) | class VpnsController < ApplicationController
    method destroy (line 5) | def destroy; end
  class VpnDomainNameServersController (line 9) | class VpnDomainNameServersController < ApplicationController

FILE: app/controllers/vpns_controller.rb
  class VpnsController (line 1) | class VpnsController < ApplicationController
    method index (line 14) | def index
    method update (line 18) | def update
    method create (line 29) | def create
    method new (line 43) | def new
    method add_dns_server (line 47) | def add_dns_server
    method add_search_domain (line 54) | def add_search_domain
    method add_supplemental_match_domain (line 61) | def add_supplemental_match_domain
    method remove_dns_server (line 71) | def remove_dns_server
    method remove_search_domain (line 78) | def remove_search_domain
    method remove_supplemental_match_domain (line 85) | def remove_supplemental_match_domain
    method assign_group (line 92) | def assign_group
    method show (line 100) | def show
    method user_associated_groups (line 105) | def user_associated_groups
    method group_associated_users (line 122) | def group_associated_users
    method create_group_associated_users (line 142) | def create_group_associated_users
    method destroy (line 176) | def destroy
    method search (line 187) | def search
    method set_vpn (line 198) | def set_vpn
    method vpn_params (line 202) | def vpn_params
    method authorize_user (line 206) | def authorize_user

FILE: app/helpers/admin_helper.rb
  type AdminHelper (line 1) | module AdminHelper

FILE: app/helpers/api_resources_helper.rb
  type ApiResourcesHelper (line 1) | module ApiResourcesHelper

FILE: app/helpers/application_helper.rb
  type ApplicationHelper (line 1) | module ApplicationHelper
    function add_placeholder_to_list (line 2) | def add_placeholder_to_list(list, placeholder, string_convert: 'titlei...

FILE: app/helpers/group_helper.rb
  type GroupHelper (line 1) | module GroupHelper

FILE: app/helpers/groups_helper.rb
  type GroupsHelper (line 1) | module GroupsHelper

FILE: app/helpers/home_helper.rb
  type HomeHelper (line 1) | module HomeHelper

FILE: app/helpers/host_access_groups_helper.rb
  type HostAccessGroupsHelper (line 1) | module HostAccessGroupsHelper

FILE: app/helpers/host_machine_groups_helper.rb
  type HostMachineGroupsHelper (line 1) | module HostMachineGroupsHelper

FILE: app/helpers/host_machines_helper.rb
  type HostMachinesHelper (line 1) | module HostMachinesHelper

FILE: app/helpers/nss_helper.rb
  type NssHelper (line 1) | module NssHelper

FILE: app/helpers/omniauth_callbacks_helper.rb
  type OmniauthCallbacksHelper (line 1) | module OmniauthCallbacksHelper

FILE: app/helpers/profile_helper.rb
  type ProfileHelper (line 1) | module ProfileHelper

FILE: app/helpers/users_helper.rb
  type UsersHelper (line 1) | module UsersHelper

FILE: app/lib/datadog.rb
  class Datadog (line 1) | class Datadog < SamlApp
    method initialize (line 3) | def initialize(org_id)
    method save_config (line 16) | def save_config(sso_url, config = {})
    method add_user (line 21) | def add_user(email)
    method remove_user (line 31) | def remove_user(email)

FILE: app/lib/saml_app.rb
  class SamlApp (line 1) | class SamlApp
    method initialize (line 5) | def initialize(org_id)
    method save_config (line 11) | def save_config(sso_url, config = {})
    method add_user (line 20) | def add_user(email)
    method remove_user (line 29) | def remove_user(email)

FILE: app/models/access_token.rb
  class AccessToken (line 1) | class AccessToken < ApplicationRecord
    method find_token (line 9) | def self.find_token challenge_token
    method valid_token (line 13) | def self.valid_token challenge_token
    method hash_token! (line 19) | def hash_token!

FILE: app/models/api_resource.rb
  class ApiResource (line 1) | class ApiResource < ApplicationRecord
    method authenticate (line 11) | def self.authenticate access_key, access_token
    method hash_access_key! (line 19) | def hash_access_key!

FILE: app/models/application_record.rb
  class ApplicationRecord (line 1) | class ApplicationRecord < ActiveRecord::Base

FILE: app/models/concerns/ms_chap_auth.rb
  type MsChapAuth (line 1) | module MsChapAuth
    function hexlify (line 3) | def hexlify(msg)
    function unhexlify (line 7) | def unhexlify(msg)
    function test_key (line 11) | def test_key key, challenge
    function ntlm_challenge_response (line 20) | def ntlm_challenge_response word, challenge
    function md4_hash (line 34) | def md4_hash word
    function set_key_odd_parity (line 39) | def set_key_odd_parity key
    function key56_to_key64 (line 51) | def key56_to_key64 key_raw
    function nt_password_hash (line 76) | def nt_password_hash password
    function get_nt_key (line 81) | def get_nt_key password
    function authenticate_ms_chap (line 88) | def authenticate_ms_chap password, challenge, response
    function authenticate_ms_chap_with_drift (line 95) | def authenticate_ms_chap_with_drift passwords, challenge, response

FILE: app/models/endpoint.rb
  class Endpoint (line 1) | class Endpoint < ApplicationRecord

FILE: app/models/group.rb
  class Group (line 1) | class Group < ApplicationRecord
    method burst_host_cache (line 27) | def burst_host_cache
    method add_admin (line 39) | def add_admin(user)
    method set_lower_case_name (line 43) | def set_lower_case_name
    method add_gid (line 47) | def add_gid
    method get_name_response (line 52) | def self.get_name_response(name)
    method get_all_response (line 64) | def self.get_all_response
    method get_gid_response (line 78) | def self.get_gid_response(gid)
    method admin? (line 85) | def admin?(user)
    method member? (line 89) | def member?(user)
    method generate_group_response (line 93) | def self.generate_group_response(name, gid, members)
    method group_response (line 102) | def group_response
    method group_nss_response (line 106) | def self.group_nss_response(name)
    method get_sysadmins_and_groups (line 123) | def self.get_sysadmins_and_groups(sysadmins, default_admins = true)
    method get_user_ids (line 152) | def get_user_ids
    method get_default_sysadmin_group_for_host (line 164) | def self.get_default_sysadmin_group_for_host(sysadmins_login_ids, defa...
    method add_user (line 180) | def add_user(user_id)
    method add_user_with_expiration (line 184) | def add_user_with_expiration(user_id, expiration_date)
    method remove_user (line 189) | def remove_user(user_id)

FILE: app/models/group_admin.rb
  class GroupAdmin (line 1) | class GroupAdmin < ApplicationRecord

FILE: app/models/group_association.rb
  class GroupAssociation (line 1) | class GroupAssociation < ApplicationRecord
    method revoke_expired (line 5) | def self.revoke_expired(date = Date.today)

FILE: app/models/group_endpoint.rb
  class GroupEndpoint (line 1) | class GroupEndpoint < ApplicationRecord

FILE: app/models/host.rb
  class Host (line 1) | class Host < ApplicationRecord

FILE: app/models/host_access_group.rb
  class HostAccessGroup (line 1) | class HostAccessGroup < ApplicationRecord

FILE: app/models/host_machine.rb
  class HostMachine (line 1) | class HostMachine < ApplicationRecord
    method set_host_access_key (line 11) | def set_host_access_key
    method set_lower_case_name (line 15) | def set_lower_case_name
    method get_group_response (line 19) | def self.get_group_response name
    method sysadmins (line 28) | def sysadmins
    method add_host_group (line 37) | def add_host_group(name)
    method add_group (line 45) | def add_group(name)

FILE: app/models/ip_address.rb
  class IpAddress (line 1) | class IpAddress < ApplicationRecord

FILE: app/models/organisation.rb
  class Organisation (line 1) | class Organisation < ApplicationRecord
    method find_by_slug (line 17) | def self.find_by_slug(slug)
    method setup (line 21) | def self.setup(attrs = {})
    method update_profile (line 29) | def update_profile(attrs = {})
    method saml_setup? (line 36) | def saml_setup?
    method setup_saml_certs (line 40) | def setup_saml_certs

FILE: app/models/saml_app_config.rb
  class SamlAppConfig (line 1) | class SamlAppConfig < ApplicationRecord
    method get_config (line 7) | def self.get_config(app_name, org_id)

FILE: app/models/user.rb
  class User (line 1) | class User < ApplicationRecord
    method add_user (line 30) | def self.add_user(first_name, last_name, user_role, domain)
    method generate_login_id (line 43) | def generate_login_id
    method generate_uid (line 47) | def generate_uid(uid_buffer = 5000)
    method initialise_host_and_group (line 52) | def initialise_host_and_group
    method generate_two_factor_auth (line 61) | def generate_two_factor_auth(force_create = false)
    method create_user (line 70) | def self.create_user(name, email)
    method add_temp_user (line 83) | def self.add_temp_user(name, email)
    method update_profile (line 90) | def update_profile(attrs = {})
    method name_email (line 101) | def name_email
    method get_sysadmins (line 105) | def self.get_sysadmins user_ids
    method purge! (line 135) | def purge!
    method includes_restricted_characters? (line 141) | def self.includes_restricted_characters? input_string
    method check_email_address (line 154) | def self.check_email_address email_address
    method valid_domain? (line 158) | def self.valid_domain? domain
    method verify (line 163) | def self.verify params
    method authenticate_pam (line 175) | def self.authenticate_pam params
    method check_user_host (line 189) | def self.check_user_host email, address_array
    method permitted_vpns? (line 194) | def permitted_vpns? address_array
    method permitted_hosts? (line 203) | def permitted_hosts? address_array
    method authenticate_cas (line 219) | def self.authenticate_cas encoded_string
    method authenticate (line 232) | def self.authenticate params
    method get_user (line 238) | def self.get_user user_login_id
    method find_and_validate_saml_user (line 242) | def self.find_and_validate_saml_user(email, password, app_name)
    method valid_otp? (line 252) | def valid_otp?(password)
    method find_and_check_user (line 260) | def self.find_and_check_user email, token
    method get_user_pass_attributes (line 271) | def self.get_user_pass_attributes params
    method get_shadow_name_response (line 279) | def self.get_shadow_name_response name
    method get_shadow_hash (line 286) | def get_shadow_hash
    method get_all_shadow_response (line 300) | def self.get_all_shadow_response
    method get_all_passwd_response (line 308) | def self.get_all_passwd_response
    method get_passwd_name_response (line 316) | def self.get_passwd_name_response name
    method response_array (line 322) | def self.response_array response
    method find_active_user_by_email (line 328) | def self.find_active_user_by_email(email)
    method group_names_list (line 332) | def group_names_list
    method reset_login_limit (line 336) | def reset_login_limit
    method within_limits? (line 341) | def within_limits?
    method ms_chap_auth (line 348) | def self.ms_chap_auth params
    method get_user_otp (line 375) | def get_user_otp
    method get_user_otp_at (line 379) | def get_user_otp_at time
    method user_passwd_response (line 383) | def user_passwd_response
    method group_admin? (line 406) | def group_admin?
    method permitted_endpoint? (line 410) | def permitted_endpoint?(endpoint)
    method remove_default_admin (line 422) | def remove_default_admin
    method validate_email_domain (line 429) | def validate_email_domain
    method revoke_admin_when_inactive (line 435) | def revoke_admin_when_inactive
    method set_deactivated_at_when_inactive (line 439) | def set_deactivated_at_when_inactive

FILE: app/models/vpn.rb
  class Vpn (line 1) | class Vpn < ApplicationRecord
    method administrator? (line 15) | def self.administrator?(user)
    method managed_vpns (line 21) | def self.managed_vpns(user)
    method user_vpns (line 32) | def self.user_vpns user
    method migrate_to_new_group (line 43) | def migrate_to_new_group

FILE: app/models/vpn_domain_name_server.rb
  class VpnDomainNameServer (line 1) | class VpnDomainNameServer < ApplicationRecord

FILE: app/models/vpn_group_association.rb
  class VpnGroupAssociation (line 1) | class VpnGroupAssociation < ApplicationRecord

FILE: app/models/vpn_group_user_association.rb
  class VpnGroupUserAssociation (line 1) | class VpnGroupUserAssociation < ApplicationRecord

FILE: app/models/vpn_search_domain.rb
  class VpnSearchDomain (line 1) | class VpnSearchDomain < ApplicationRecord

FILE: app/models/vpn_supplemental_match_domain.rb
  class VpnSupplementalMatchDomain (line 1) | class VpnSupplementalMatchDomain < ApplicationRecord

FILE: app/validators/email_validator.rb
  class EmailValidator (line 1) | class EmailValidator < ActiveModel::EachValidator
    method validate_each (line 2) | def validate_each(record, attribute, value)

FILE: config/application.rb
  type Gate (line 9) | module Gate
    class Application (line 10) | class Application < Rails::Application

FILE: db/migrate/20160419122430_devise_create_users.rb
  class DeviseCreateUsers (line 1) | class DeviseCreateUsers < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160419132647_add_provider_to_users.rb
  class AddProviderToUsers (line 1) | class AddProviderToUsers < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160419144739_add_name_to_users.rb
  class AddNameToUsers (line 1) | class AddNameToUsers < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160427123146_add_auth_key_to_user.rb
  class AddAuthKeyToUser (line 1) | class AddAuthKeyToUser < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160427123233_add_provisioning_uri_to_user.rb
  class AddProvisioningUriToUser (line 1) | class AddProvisioningUriToUser < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160519042340_add_active_to_users.rb
  class AddActiveToUsers (line 1) | class AddActiveToUsers < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160519064340_add_default_value_to_users.rb
  class AddDefaultValueToUsers (line 1) | class AddDefaultValueToUsers < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160615044834_create_hosts.rb
  class CreateHosts (line 1) | class CreateHosts < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160615045052_add_admin_to_user.rb
  class AddAdminToUser (line 1) | class AddAdminToUser < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160615112805_add_user_to_host.rb
  class AddUserToHost (line 1) | class AddUserToHost < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160628140022_add_deleted_at_to_host.rb
  class AddDeletedAtToHost (line 1) | class AddDeletedAtToHost < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160628140440_add_deleted_by_to_host.rb
  class AddDeletedByToHost (line 1) | class AddDeletedByToHost < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160629043358_add_homedir_to_user.rb
  class AddHomedirToUser (line 1) | class AddHomedirToUser < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160629043415_add_shell_to_user.rb
  class AddShellToUser (line 1) | class AddShellToUser < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160629075435_create_groups.rb
  class CreateGroups (line 1) | class CreateGroups < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160701090045_create_group_associations.rb
  class CreateGroupAssociations (line 1) | class CreateGroupAssociations < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160701112600_add_deleted_properties_to_group.rb
  class AddDeletedPropertiesToGroup (line 1) | class AddDeletedPropertiesToGroup < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160707115313_create_access_tokens.rb
  class CreateAccessTokens (line 1) | class CreateAccessTokens < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160714115228_add_public_key_to_user.rb
  class AddPublicKeyToUser (line 1) | class AddPublicKeyToUser < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20160908081651_create_host_machines.rb
  class CreateHostMachines (line 1) | class CreateHostMachines < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20161003145832_create_host_access_groups.rb
  class CreateHostAccessGroups (line 1) | class CreateHostAccessGroups < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20170803140620_add_user_login_id_to_user.rb
  class AddUserLoginIdToUser (line 1) | class AddUserLoginIdToUser < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171013115441_create_versions.rb
  class CreateVersions (line 3) | class CreateVersions < ActiveRecord::Migration[5.0]
    method change (line 11) | def change

FILE: db/migrate/20171016064705_remove_index_group_name.rb
  class RemoveIndexGroupName (line 1) | class RemoveIndexGroupName < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171016071526_add_unique_index_on_groups_name.rb
  class AddUniqueIndexOnGroupsName (line 1) | class AddUniqueIndexOnGroupsName < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171031060034_create_group_admin.rb
  class CreateGroupAdmin (line 1) | class CreateGroupAdmin < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171031060217_add_foreign_key_ref_on_group_admin.rb
  class AddForeignKeyRefOnGroupAdmin (line 1) | class AddForeignKeyRefOnGroupAdmin < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171031100758_create_vpns.rb
  class CreateVpns (line 1) | class CreateVpns < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171031101026_create_vpn_group_association.rb
  class CreateVpnGroupAssociation (line 1) | class CreateVpnGroupAssociation < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171031103518_add_foreign_key_ref_on_vpn_group_association.rb
  class AddForeignKeyRefOnVpnGroupAssociation (line 1) | class AddForeignKeyRefOnVpnGroupAssociation < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171031113123_create_vpn_group_user_association.rb
  class CreateVpnGroupUserAssociation (line 1) | class CreateVpnGroupUserAssociation < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171031121702_add_foreign_key_ref_on_vpn_group_user_association.rb
  class AddForeignKeyRefOnVpnGroupUserAssociation (line 1) | class AddForeignKeyRefOnVpnGroupUserAssociation < ActiveRecord::Migratio...
    method change (line 2) | def change

FILE: db/migrate/20171102071909_add_ip_address_to_vpns.rb
  class AddIpAddressToVpns (line 1) | class AddIpAddressToVpns < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171107114249_remove_url_from_vpns.rb
  class RemoveUrlFromVpns (line 1) | class RemoveUrlFromVpns < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171108130234_add_user_id_to_access_token.rb
  class AddUserIdToAccessToken (line 1) | class AddUserIdToAccessToken < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171108130353_add_foreign_key_ref_on_access_tokens.rb
  class AddForeignKeyRefOnAccessTokens (line 1) | class AddForeignKeyRefOnAccessTokens < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171124090240_add_uuid_to_vpns.rb
  class AddUuidToVpns (line 1) | class AddUuidToVpns < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171124114427_create_vpn_domain_name_servers.rb
  class CreateVpnDomainNameServers (line 1) | class CreateVpnDomainNameServers < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171124114830_create_vpn_search_domains.rb
  class CreateVpnSearchDomains (line 1) | class CreateVpnSearchDomains < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20171124115925_create_vpn_supplemental_match_domains.rb
  class CreateVpnSupplementalMatchDomains (line 1) | class CreateVpnSupplementalMatchDomains < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180104081814_add_product_name_to_users.rb
  class AddProductNameToUsers (line 1) | class AddProductNameToUsers < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180202102206_create_saml_service_providers.rb
  class CreateSamlServiceProviders (line 1) | class CreateSamlServiceProviders < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180214050204_add_api_key_to_host_machines.rb
  class AddApiKeyToHostMachines (line 1) | class AddApiKeyToHostMachines < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180214052451_create_ip_addresses.rb
  class CreateIpAddresses (line 1) | class CreateIpAddresses < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180214052644_add_host_machine_to_ip_address.rb
  class AddHostMachineToIpAddress (line 1) | class AddHostMachineToIpAddress < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180219150818_add_description_to_group.rb
  class AddDescriptionToGroup (line 1) | class AddDescriptionToGroup < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180222135930_add_access_key_to_host_machine.rb
  class AddAccessKeyToHostMachine (line 1) | class AddAccessKeyToHostMachine < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180222140000_add_access_key_to_user.rb
  class AddAccessKeyToUser (line 1) | class AddAccessKeyToUser < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180227051732_create_api_resources.rb
  class CreateApiResources (line 1) | class CreateApiResources < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180301010021_add_user_to_api_resources.rb
  class AddUserToApiResources (line 1) | class AddUserToApiResources < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180301010035_add_group_to_api_resources.rb
  class AddGroupToApiResources (line 1) | class AddGroupToApiResources < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180306231200_add_deactivated_at_to_users.rb
  class AddDeactivatedAtToUsers (line 1) | class AddDeactivatedAtToUsers < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180311082600_rename_access_key_in_api_resources.rb
  class RenameAccessKeyInApiResources (line 1) | class RenameAccessKeyInApiResources < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180311161200_rename_token_in_access_tokens.rb
  class RenameTokenInAccessTokens (line 1) | class RenameTokenInAccessTokens < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180318083000_create_indexes_to_speedup_nss_controller.rb
  class CreateIndexesToSpeedupNssController (line 1) | class CreateIndexesToSpeedupNssController < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180613074108_create_organisations.rb
  class CreateOrganisations (line 1) | class CreateOrganisations < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180613165050_drop_saml_service_providers.rb
  class DropSamlServiceProviders (line 1) | class DropSamlServiceProviders < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20180723175600_update_organisations_for_saml.rb
  class UpdateOrganisationsForSaml (line 1) | class UpdateOrganisationsForSaml < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20181002023107_add_default_admins_to_host_machines.rb
  class AddDefaultAdminsToHostMachines (line 1) | class AddDefaultAdminsToHostMachines < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20181016093315_create_saml_app_configs.rb
  class CreateSamlAppConfigs (line 1) | class CreateSamlAppConfigs < ActiveRecord::Migration[5.0]
    method change (line 2) | def change

FILE: db/migrate/20181208184236_add_fields_to_user.rb
  class AddFieldsToUser (line 1) | class AddFieldsToUser < ActiveRecord::Migration[5.1]
    method change (line 2) | def change

FILE: db/migrate/20190624024930_add_expiration_date_to_group_associations.rb
  class AddExpirationDateToGroupAssociations (line 1) | class AddExpirationDateToGroupAssociations < ActiveRecord::Migration[5.1]
    method change (line 2) | def change

FILE: db/migrate/20190820070910_create_endpoints.rb
  class CreateEndpoints (line 1) | class CreateEndpoints < ActiveRecord::Migration[5.1]
    method change (line 2) | def change

FILE: db/migrate/20190820075040_create_group_endpoints.rb
  class CreateGroupEndpoints (line 1) | class CreateGroupEndpoints < ActiveRecord::Migration[5.1]
    method change (line 2) | def change

FILE: db/migrate/20190820080624_add_foreign_key_ref_on_group_endpoints.rb
  class AddForeignKeyRefOnGroupEndpoints (line 1) | class AddForeignKeyRefOnGroupEndpoints < ActiveRecord::Migration[5.1]
    method change (line 2) | def change

FILE: db/migrate/20200113065717_add_sessions_table.rb
  class AddSessionsTable (line 1) | class AddSessionsTable < ActiveRecord::Migration[5.1]
    method change (line 2) | def change

FILE: db/migrate/20220926001848_add_service_name_to_active_storage_blobs.active_storage.rb
  class AddServiceNameToActiveStorageBlobs (line 2) | class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]
    method up (line 3) | def up
    method down (line 17) | def down

FILE: db/migrate/20220926001849_create_active_storage_variant_records.active_storage.rb
  class CreateActiveStorageVariantRecords (line 2) | class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
    method change (line 3) | def change
    method primary_key_type (line 17) | def primary_key_type
    method blobs_primary_key_type (line 22) | def blobs_primary_key_type

FILE: db/migrate/20220926001850_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
  class RemoveNotNullOnActiveStorageBlobsChecksum (line 2) | class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migratio...
    method change (line 3) | def change

FILE: lib/vpn/mobileconfig.rb
  class Mobileconfig (line 5) | class Mobileconfig
    method generate (line 6) | def generate (vpns, user)
    method sign_mobileconfig (line 44) | def sign_mobileconfig(mobileconfig)

FILE: lib/vpn/namespace.rb
  class Namespace (line 1) | class Namespace
    method initialize (line 2) | def initialize(hash)
    method get_binding (line 7) | def get_binding

FILE: spec/controllers/api/v1/api_controller_spec.rb
  function index (line 6) | def index

FILE: spec/support/helpers/x509_certificate_helper.rb
  class CertificateHelper (line 1) | class CertificateHelper
    method initialize (line 2) | def initialize
    method get_cert (line 19) | def get_cert
    method get_private_key (line 23) | def get_private_key
    method get_fingerprint (line 27) | def get_fingerprint
Condensed preview — 370 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (534K chars).
[
  {
    "path": ".deepsource.toml",
    "chars": 106,
    "preview": "version = 1\n\n[[analyzers]]\nname = \"ruby\"\nenabled = true\n\n[[analyzers]]\nname = \"javascript\"\nenabled = true\n"
  },
  {
    "path": ".dockerignore",
    "chars": 87,
    "preview": "/log/*\n!/log/.keep\n/tmp\n.idea\n.env\n*.swp\nconfig/application.yml\ndump.rdb\ncoverage\n.git\n"
  },
  {
    "path": ".gitignore",
    "chars": 583,
    "preview": "# See https://help.github.com/articles/ignoring-files for more about ignoring files.\n#\n# If you find yourself ignoring t"
  },
  {
    "path": ".rspec",
    "chars": 53,
    "preview": "--color\n--format documentation\n--require spec_helper\n"
  },
  {
    "path": ".rubocop.yml",
    "chars": 19585,
    "preview": "---\nrequire: \n  - rubocop-rails\n  - rubocop-performance\n  - rubocop-faker\n\nAllCops:\n  NewCops: enable\n  Exclude:\n  - con"
  },
  {
    "path": ".ruby-version",
    "chars": 11,
    "preview": "ruby-3.1.2\n"
  },
  {
    "path": ".travis.yml",
    "chars": 5080,
    "preview": "dist: xenial\nsudo: required\nlanguage: ruby\nrvm:\n  - ruby-2.4.4\nservices:\n  - redis-server\n  - mysql\n\nenv:\n  global:\n  - "
  },
  {
    "path": "CHANGELOG.md",
    "chars": 2523,
    "preview": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changel"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 447,
    "preview": "# Contributing Guidelines\n\n## Releasing Gate\n\n> Gate uses semantic versioning, check [this page](https://semver.org/) fo"
  },
  {
    "path": "Dockerfile",
    "chars": 365,
    "preview": "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 && ap"
  },
  {
    "path": "Gemfile",
    "chars": 1200,
    "preview": "source 'https://rubygems.org'\n\ngem 'rails', '7.1.3.2'\n\ngem 'activerecord-session_store'\ngem 'ansi'\ngem 'bootstrap'\ngem '"
  },
  {
    "path": "LICENSE",
    "chars": 1075,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2016 gate-sso\n\nPermission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "README.md",
    "chars": 5334,
    "preview": "# Gate is now Gate-WireGuard \n\n## DEPRECATED\n\n#### Please use [Gate-WireGuard](https://github.com/gate-sso/gate-wireguar"
  },
  {
    "path": "Rakefile",
    "chars": 249,
    "preview": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they wil"
  },
  {
    "path": "VERSION",
    "chars": 6,
    "preview": "1.1.8\n"
  },
  {
    "path": "api_blueprint/bin/dredd_server.sh",
    "chars": 170,
    "preview": "#!/bin/bash\n# dredd_server.sh\nkill -9 $(lsof -i tcp:9865 -t)\nexport RAILS_ENV=test\nexport LOG_LEVEL=info\nrake db:drop\nra"
  },
  {
    "path": "api_blueprint/group.apib",
    "chars": 4372,
    "preview": "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)"
  },
  {
    "path": "api_blueprint/hooks/dredd_hooks.rb",
    "chars": 639,
    "preview": "ENV['RAILS_ENV'] ||= 'test'\n\nrequire File.expand_path('../../config/environment', __dir__)\nrequire 'dredd_hooks/methods'"
  },
  {
    "path": "api_blueprint/user.apib",
    "chars": 6560,
    "preview": "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"
  },
  {
    "path": "api_blueprint/vpns.apib",
    "chars": 1601,
    "preview": "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    +"
  },
  {
    "path": "app/assets/config/manifest.js",
    "chars": 210,
    "preview": "//= link application.css\n//= link apple-touch-icon-144x144-precomposed.png\n//= link apple-touch-icon-72x72-precomposed.p"
  },
  {
    "path": "app/assets/images/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/assets/javascripts/admin.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/api_resources.coffee",
    "chars": 1132,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/application.js",
    "chars": 707,
    "preview": "// This is a manifest file that'll be compiled into application.js, which will include all the files\n// listed below.\n//"
  },
  {
    "path": "app/assets/javascripts/bootstrap.js.coffee",
    "chars": 104,
    "preview": "jQuery ->\n  $(\"a[rel~=popover], .has-popover\").popover()\n  $(\"a[rel~=tooltip], .has-tooltip\").tooltip()\n"
  },
  {
    "path": "app/assets/javascripts/group.coffee",
    "chars": 3553,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/groups.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/home.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/host_access_groups.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/host_machine_groups.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/host_machines.coffee",
    "chars": 924,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/nss.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/omniauth_callbacks.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/profile.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/users.coffee",
    "chars": 908,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/utilities.coffee",
    "chars": 1039,
    "preview": "# Utility functions\n\n@append_error_msg = (elem, res, msg) ->\n  if !res\n    elem.addClass('is-invalid')\n    elem.next('.i"
  },
  {
    "path": "app/assets/javascripts/viewport.js",
    "chars": 641,
    "preview": "/*!\n * IE10 viewport hack for Surface/desktop Windows 8 bug\n * Copyright 2014-2015 Twitter, Inc.\n * Licensed under MIT ("
  },
  {
    "path": "app/assets/stylesheets/application.scss",
    "chars": 1589,
    "preview": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below"
  },
  {
    "path": "app/assets/stylesheets/bootstrap-social.scss",
    "chars": 17512,
    "preview": "/*\n * Social Buttons for Bootstrap\n *\n * Copyright 2013-2016 Panayiotis Lipiridis\n * Licensed under the MIT License\n *\n "
  },
  {
    "path": "app/assets/stylesheets/general.css",
    "chars": 30,
    "preview": "body {\n  padding-top: 70px;\n}\n"
  },
  {
    "path": "app/assets/stylesheets/home.scss.erb",
    "chars": 1376,
    "preview": "// Place all the styles related to the Home controller here.\n// They will automatically be included in application.css.\n"
  },
  {
    "path": "app/assets/stylesheets/profile.css",
    "chars": 140,
    "preview": "/* Everything but the jumbotron gets side spacing for mobile first views */\n\n.container-profile {\n  max-width: 730px;\n  "
  },
  {
    "path": "app/assets/stylesheets/scaffolds.scss",
    "chars": 1031,
    "preview": "body {\n  background-color: #fff;\n  color: #333;\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 13px;"
  },
  {
    "path": "app/clients/data_dog_client.rb",
    "chars": 1501,
    "preview": "class DataDogClient\n  include HTTParty\n  base_uri 'https://api.datadoghq.com/api/v1'\n\n  def initialize(app_key, api_key)"
  },
  {
    "path": "app/controllers/admin_controller.rb",
    "chars": 67,
    "preview": "class AdminController < ApplicationController\n  def index; end\nend\n"
  },
  {
    "path": "app/controllers/api/v1/base_controller.rb",
    "chars": 713,
    "preview": "class ::Api::V1::BaseController < ActionController::Base\n  protect_from_forgery with: :null_session\n  before_action :aut"
  },
  {
    "path": "app/controllers/api/v1/endpoints_controller.rb",
    "chars": 985,
    "preview": "class ::Api::V1::EndpointsController < ::Api::V1::BaseController\n  before_action :authorize_user\n\n  def create\n    endpo"
  },
  {
    "path": "app/controllers/api/v1/groups_controller.rb",
    "chars": 1309,
    "preview": "class ::Api::V1::GroupsController < ::Api::V1::BaseController\n  def create\n    if current_user.admin?\n      @group = Gro"
  },
  {
    "path": "app/controllers/api/v1/users_controller.rb",
    "chars": 2158,
    "preview": "class ::Api::V1::UsersController < ::Api::V1::BaseController\n  before_action :set_user, only: %i[show update]\n\n  def cre"
  },
  {
    "path": "app/controllers/api/v1/vpns_controller.rb",
    "chars": 886,
    "preview": "class ::Api::V1::VpnsController < ::Api::V1::BaseController\n  before_action :set_vpn, only: [:assign_group]\n\n  def creat"
  },
  {
    "path": "app/controllers/api_resources_controller.rb",
    "chars": 4412,
    "preview": "class ApiResourcesController < ApplicationController\n  before_action :set_api_resource, only: %i[show edit update destro"
  },
  {
    "path": "app/controllers/application_controller.rb",
    "chars": 706,
    "preview": "class ApplicationController < ActionController::Base\n  # Prevent CSRF attacks by raising an exception.\n  # For APIs, you"
  },
  {
    "path": "app/controllers/concerns/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/controllers/groups_controller.rb",
    "chars": 6072,
    "preview": "class GroupsController < ApplicationController\n  before_action :set_group, only: %i[show edit update destroy\n           "
  },
  {
    "path": "app/controllers/home_controller.rb",
    "chars": 173,
    "preview": "class HomeController < ApplicationController\n\n  before_action :check_signed_in\n\n  def check_signed_in\n    redirect_to pr"
  },
  {
    "path": "app/controllers/host_controller.rb",
    "chars": 564,
    "preview": "class HostController < ApplicationController\n  before_action :authenticate_user!\n  def add_host\n    @user = User.find(pa"
  },
  {
    "path": "app/controllers/host_machine_groups_controller.rb",
    "chars": 764,
    "preview": "class HostMachineGroupsController < ApplicationController\n  def show\n    @host_machines = HostMachine.all\n  end\n\n  def c"
  },
  {
    "path": "app/controllers/host_machines_controller.rb",
    "chars": 2620,
    "preview": "class HostMachinesController < ApplicationController\n  before_action :set_host_machine, only: %i[add_group show edit upd"
  },
  {
    "path": "app/controllers/nss_controller.rb",
    "chars": 3797,
    "preview": "class NssController < ApplicationController\n  skip_before_action :verify_authenticity_token, only: %i[add_host add_user_"
  },
  {
    "path": "app/controllers/organisations_controller.rb",
    "chars": 3727,
    "preview": "class OrganisationsController < ApplicationController\n  before_action :authorize_user, except: [:index]\n  before_action "
  },
  {
    "path": "app/controllers/pings_controller.rb",
    "chars": 92,
    "preview": "class PingsController < ApplicationController\n  def show\n    render plain: 'pong'\n  end\nend\n"
  },
  {
    "path": "app/controllers/profile_controller.rb",
    "chars": 6344,
    "preview": "class ProfileController < ApplicationController\n  require 'vpn/mobileconfig'\n\n  skip_before_action :verify_authenticity_"
  },
  {
    "path": "app/controllers/saml_idp_controller.rb",
    "chars": 2071,
    "preview": "#TODO rename back to SAMLController\n\n#class SamlIdpController < SamlIdp::IdpController\nclass SamlIdpController < Applica"
  },
  {
    "path": "app/controllers/users/auth_controller.rb",
    "chars": 500,
    "preview": "class Users::AuthController < ApplicationController\n\n  def log_in\n    unless ENV['SIGN_IN_TYPE'] == 'form'\n      return "
  },
  {
    "path": "app/controllers/users/omniauth_callbacks_controller.rb",
    "chars": 733,
    "preview": "class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController\n  def google_oauth2\n    # You need to imp"
  },
  {
    "path": "app/controllers/users_controller.rb",
    "chars": 4114,
    "preview": "class UsersController < ApplicationController\n  before_action :authenticate_user!, except: %i[user_id verify authenticat"
  },
  {
    "path": "app/controllers/vpn_domain_name_servers_controller.rb",
    "chars": 306,
    "preview": "class VpnsController < ApplicationController\n  before_action :authorize_user\n  before_action :set_vpn, only: %i[show edi"
  },
  {
    "path": "app/controllers/vpns_controller.rb",
    "chars": 6323,
    "preview": "class VpnsController < ApplicationController\n  before_action :authorize_user, except: %i[create_group_associated_users s"
  },
  {
    "path": "app/helpers/admin_helper.rb",
    "chars": 23,
    "preview": "module AdminHelper\nend\n"
  },
  {
    "path": "app/helpers/api_resources_helper.rb",
    "chars": 30,
    "preview": "module ApiResourcesHelper\nend\n"
  },
  {
    "path": "app/helpers/application_helper.rb",
    "chars": 268,
    "preview": "module ApplicationHelper\n  def add_placeholder_to_list(list, placeholder, string_convert: 'titleize')\n    (list.map do |"
  },
  {
    "path": "app/helpers/group_helper.rb",
    "chars": 23,
    "preview": "module GroupHelper\nend\n"
  },
  {
    "path": "app/helpers/groups_helper.rb",
    "chars": 24,
    "preview": "module GroupsHelper\nend\n"
  },
  {
    "path": "app/helpers/home_helper.rb",
    "chars": 22,
    "preview": "module HomeHelper\nend\n"
  },
  {
    "path": "app/helpers/host_access_groups_helper.rb",
    "chars": 34,
    "preview": "module HostAccessGroupsHelper\nend\n"
  },
  {
    "path": "app/helpers/host_machine_groups_helper.rb",
    "chars": 35,
    "preview": "module HostMachineGroupsHelper\nend\n"
  },
  {
    "path": "app/helpers/host_machines_helper.rb",
    "chars": 30,
    "preview": "module HostMachinesHelper\nend\n"
  },
  {
    "path": "app/helpers/nss_helper.rb",
    "chars": 21,
    "preview": "module NssHelper\nend\n"
  },
  {
    "path": "app/helpers/omniauth_callbacks_helper.rb",
    "chars": 35,
    "preview": "module OmniauthCallbacksHelper\nend\n"
  },
  {
    "path": "app/helpers/profile_helper.rb",
    "chars": 25,
    "preview": "module ProfileHelper\nend\n"
  },
  {
    "path": "app/helpers/users_helper.rb",
    "chars": 23,
    "preview": "module UsersHelper\nend\n"
  },
  {
    "path": "app/lib/datadog.rb",
    "chars": 851,
    "preview": "class Datadog < SamlApp\n\n  def initialize(org_id)\n    @app_name = 'datadog'\n    super(org_id)\n    if @config.persisted?\n"
  },
  {
    "path": "app/lib/saml_app.rb",
    "chars": 805,
    "preview": "class SamlApp\n\n  attr_accessor :config, :app_name\n\n  def initialize(org_id)\n    @config = SamlAppConfig.find_or_initiali"
  },
  {
    "path": "app/mailers/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/models/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/models/access_token.rb",
    "chars": 452,
    "preview": "class AccessToken < ApplicationRecord\n  attr_accessor :token\n\n\n  belongs_to :user\n\n  before_save :hash_token!\n\n  def sel"
  },
  {
    "path": "app/models/api_resource.rb",
    "chars": 707,
    "preview": "class ApiResource < ApplicationRecord\n  attr_accessor :access_key\n\n  validates :name, format: { with: /\\A[a-zA-Z0-9_-]+\\"
  },
  {
    "path": "app/models/application_record.rb",
    "chars": 78,
    "preview": "class ApplicationRecord < ActiveRecord::Base\n  self.abstract_class = true\nend\n"
  },
  {
    "path": "app/models/concerns/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/models/concerns/ms_chap_auth.rb",
    "chars": 2927,
    "preview": "module MsChapAuth\n\n  def hexlify(msg)\n    msg.unpack('H*').first\n  end\n\n  def unhexlify(msg)\n    msg.scan(/../).collect "
  },
  {
    "path": "app/models/endpoint.rb",
    "chars": 344,
    "preview": "class Endpoint < ApplicationRecord\n\n\n  has_many :group_endpoints\n  has_many :groups, through: :group_endpoints\n\n  valida"
  },
  {
    "path": "app/models/group.rb",
    "chars": 5307,
    "preview": "class Group < ApplicationRecord\n\n  has_many :group_admins, dependent: :destroy\n  has_many :group_associations\n  has_many"
  },
  {
    "path": "app/models/group_admin.rb",
    "chars": 80,
    "preview": "class GroupAdmin < ApplicationRecord\n  belongs_to :user\n  belongs_to :group\nend\n"
  },
  {
    "path": "app/models/group_association.rb",
    "chars": 189,
    "preview": "class GroupAssociation < ApplicationRecord\n  belongs_to :user\n  belongs_to :group\n\n  def self.revoke_expired(date = Date"
  },
  {
    "path": "app/models/group_endpoint.rb",
    "chars": 208,
    "preview": "class GroupEndpoint < ApplicationRecord\n\n\n  belongs_to :group\n  belongs_to :endpoint\n\n  validates :group, uniqueness: { "
  },
  {
    "path": "app/models/host.rb",
    "chars": 73,
    "preview": "class Host < ApplicationRecord\n  belongs_to :user\n  acts_as_paranoid\nend\n"
  },
  {
    "path": "app/models/host_access_group.rb",
    "chars": 93,
    "preview": "class HostAccessGroup < ApplicationRecord\n  belongs_to :host_machine\n  belongs_to :group\nend\n"
  },
  {
    "path": "app/models/host_machine.rb",
    "chars": 1272,
    "preview": "class HostMachine < ApplicationRecord\n\n  has_many :host_access_groups\n  has_many :groups, through: :host_access_groups\n "
  },
  {
    "path": "app/models/ip_address.rb",
    "chars": 40,
    "preview": "class IpAddress < ApplicationRecord\nend\n"
  },
  {
    "path": "app/models/organisation.rb",
    "chars": 2303,
    "preview": "class Organisation < ApplicationRecord\n  validates :name, :website, :domain, :country, :state, :address,\n            :ad"
  },
  {
    "path": "app/models/saml_app_config.rb",
    "chars": 262,
    "preview": "class SamlAppConfig < ApplicationRecord\n  belongs_to :group\n  belongs_to :organisation\n\n  serialize :config, JSON\n\n  def"
  },
  {
    "path": "app/models/user.rb",
    "chars": 12507,
    "preview": "class User < ApplicationRecord\n\n\n  include MsChapAuth\n  devise :timeoutable, :omniauthable, omniauth_providers: [:google"
  },
  {
    "path": "app/models/vpn.rb",
    "chars": 1981,
    "preview": "class Vpn < ApplicationRecord\n  belongs_to :user\n  belongs_to :group\n\n  has_many :vpn_group_associations\n  has_many :gro"
  },
  {
    "path": "app/models/vpn_domain_name_server.rb",
    "chars": 68,
    "preview": "class VpnDomainNameServer < ApplicationRecord\n  belongs_to :vpn\nend\n"
  },
  {
    "path": "app/models/vpn_group_association.rb",
    "chars": 89,
    "preview": "class VpnGroupAssociation < ApplicationRecord\n  belongs_to :vpn\n  belongs_to :group\nend\n\n"
  },
  {
    "path": "app/models/vpn_group_user_association.rb",
    "chars": 93,
    "preview": "class VpnGroupUserAssociation < ApplicationRecord\n  belongs_to :vpn\n  belongs_to :user\nend\n\n\n"
  },
  {
    "path": "app/models/vpn_search_domain.rb",
    "chars": 64,
    "preview": "class VpnSearchDomain < ApplicationRecord\n  belongs_to :vpn\nend\n"
  },
  {
    "path": "app/models/vpn_supplemental_match_domain.rb",
    "chars": 75,
    "preview": "class VpnSupplementalMatchDomain < ApplicationRecord\n  belongs_to :vpn\nend\n"
  },
  {
    "path": "app/validators/email_validator.rb",
    "chars": 265,
    "preview": "class EmailValidator < ActiveModel::EachValidator\n  def validate_each(record, attribute, value)\n    unless value.to_s.ma"
  },
  {
    "path": "app/views/admin/index.html.slim",
    "chars": 9,
    "preview": "h1 hello\n"
  },
  {
    "path": "app/views/api/v1/users/show.json.jbuilder",
    "chars": 152,
    "preview": "json.(@user, :email, :uid, :name, :active, :admin, :home_dir, :shell, :public_key, :user_login_id, :product_name)\njson.g"
  },
  {
    "path": "app/views/api_resources/_api_resource.json.jbuilder",
    "chars": 147,
    "preview": "json.extract! api_resource, :id, :name, :description, :access_key, :created_at, :updated_at\njson.url api_resource_url(ap"
  },
  {
    "path": "app/views/api_resources/_form.html.slim",
    "chars": 782,
    "preview": "= form_for @api_resource, :class => 'form-horizontal', html: {novalidate: 'true'} do |f|\n  - if @api_resource.errors.any"
  },
  {
    "path": "app/views/api_resources/edit.html.slim",
    "chars": 118,
    "preview": "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",
    "chars": 918,
    "preview": ".container-fluid.col-md-8\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n        = notice\n\n\n"
  },
  {
    "path": "app/views/api_resources/index.json.jbuilder",
    "chars": 85,
    "preview": "json.array! @api_resources, partial: 'api_resources/api_resource', as: :api_resource\n"
  },
  {
    "path": "app/views/api_resources/new.html.slim",
    "chars": 279,
    "preview": ".container-fluid.col-md-4.col-md-offset-4\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n   "
  },
  {
    "path": "app/views/api_resources/show.html.slim",
    "chars": 988,
    "preview": ".container-fluid.col-md-4.col-md-offset-4\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n   "
  },
  {
    "path": "app/views/api_resources/show.json.jbuilder",
    "chars": 72,
    "preview": "json.partial! \"api_resources/api_resource\", api_resource: @api_resource\n"
  },
  {
    "path": "app/views/application/_admin.html.slim",
    "chars": 124,
    "preview": "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=\"#\" Gro"
  },
  {
    "path": "app/views/application/_groups_header.html.slim",
    "chars": 581,
    "preview": "#notice\n  = notice\n- if current_user.admin?\n  = form_tag groups_path, method: 'post' do\n    .row\n      .col-md-8\n       "
  },
  {
    "path": "app/views/application/_host_header.html.slim",
    "chars": 336,
    "preview": "ul.nav.nav-tabs\n  li#host_machine role=\"presentation\" \n    = link_to \"MyHosts\", host_machines_path\n  li#group role=\"pres"
  },
  {
    "path": "app/views/common/errors.json.jbuilder",
    "chars": 38,
    "preview": "json.success false\njson.errors errors\n"
  },
  {
    "path": "app/views/groups/_form.html.slim",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/views/groups/index.html.slim",
    "chars": 1044,
    "preview": ".container-fluid.col.container-profile\n  - if current_user.admin?\n    = form_tag groups_path, method: 'get', class: \"p-2"
  },
  {
    "path": "app/views/groups/new.html.slim",
    "chars": 642,
    "preview": ".container-fluid.col-md-4.col-md-offset-4\n  h5 Add new group\n  = form_for @group, :class => 'form-horizontal' do |f|\n   "
  },
  {
    "path": "app/views/groups/show.html.slim",
    "chars": 5789,
    "preview": ".container.col-md-8\n  h5.mb-3 Group Details\n  hr\n  .row\n    .col \n      = \"Group Name: #{@group.name}\"\n    .col\n      = "
  },
  {
    "path": "app/views/home/index.html.slim",
    "chars": 757,
    "preview": ".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  - cas"
  },
  {
    "path": "app/views/host_machines/index.html.slim",
    "chars": 773,
    "preview": ".container-fluid.col-md-6.col-md-offset-2\n  = form_tag host_machines_path, method: 'get', class: \"p-2\" do\n    .input-gro"
  },
  {
    "path": "app/views/host_machines/new.html.slim",
    "chars": 313,
    "preview": ".container-fluid.col-md-4.col-md-offset-4\n  = form_tag host_machines_path, method: 'post' do\n    .input-group \n      = t"
  },
  {
    "path": "app/views/host_machines/show.html.slim",
    "chars": 1637,
    "preview": ".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 "
  },
  {
    "path": "app/views/layouts/application.html.slim",
    "chars": 2981,
    "preview": "doctype html\nhtml lang=\"en\"\n  head\n    meta charset=\"utf-8\"\n    meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge,chrom"
  },
  {
    "path": "app/views/layouts/home.html.slim",
    "chars": 1148,
    "preview": "doctype html\nhtml lang=\"en\"\n  head\n    link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type="
  },
  {
    "path": "app/views/layouts/profile.html.slim.disabled",
    "chars": 1211,
    "preview": "doctype html\nhtml lang=\"en\"\n  head\n    meta charset=\"utf-8\"\n    meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge,chrom"
  },
  {
    "path": "app/views/nss/add_host.json.jbuilder",
    "chars": 106,
    "preview": "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",
    "chars": 976,
    "preview": "- if flash.key?(:errors)\n  .alert.alert-danger#organisation_form_errors\n    b Issue creating application\n    br\n    - fl"
  },
  {
    "path": "app/views/organisations/config_saml_app.html.slim",
    "chars": 266,
    "preview": ".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"
  },
  {
    "path": "app/views/organisations/index.html.slim",
    "chars": 1680,
    "preview": ".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 "
  },
  {
    "path": "app/views/organisations/new.html.slim",
    "chars": 224,
    "preview": ".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    = ren"
  },
  {
    "path": "app/views/organisations/saml_apps/_datadog.html.erb",
    "chars": 6384,
    "preview": "<ul class=\"nav nav-tabs\" id=\"configAppTabs\" role=\"tablist\">\n  <li class=\"nav-item\">\n    <a class=\"nav-link active\" href="
  },
  {
    "path": "app/views/organisations/show.html.slim",
    "chars": 224,
    "preview": ".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    = ren"
  },
  {
    "path": "app/views/profile/_group_search.html.slim",
    "chars": 331,
    "preview": ".container.container-profile\n  = form_tag profile_group_admin_path, method: 'get' do\n    .row \n      .col-md-8\n        ="
  },
  {
    "path": "app/views/profile/_user_search.html.slim",
    "chars": 326,
    "preview": ".container.container-profile\n  = form_tag profile_user_admin_path, method: 'get' do\n    .row \n      .col-md-8\n        = "
  },
  {
    "path": "app/views/profile/group_admin.html.slim",
    "chars": 33,
    "preview": "= render partial: \"group_search\"\n"
  },
  {
    "path": "app/views/profile/list.html.slim",
    "chars": 511,
    "preview": "= render partial: \"user_search\"\n- if @users.count > 0\n  .table-responsive\n    table.table.table-striped\n      thead\n    "
  },
  {
    "path": "app/views/profile/public_key.html.slim",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/views/profile/show.html.slim",
    "chars": 2674,
    "preview": ".container.container-profile\n  .row.mt-3\n  .jumbotron\n    .row.marketing\n      .col-lg-6\n        .row\n          .col-lg-"
  },
  {
    "path": "app/views/profile/user.html.slim",
    "chars": 3227,
    "preview": ".container.container-profile\n  .row\n    .col-md-4.text-primary Name\n    .col-md-8\n      = @user.name\n  .row\n    .col-md-"
  },
  {
    "path": "app/views/profile/user_admin.html.slim",
    "chars": 32,
    "preview": "= render partial: \"user_search\"\n"
  },
  {
    "path": "app/views/saml_idp/idp/new.html.erb",
    "chars": 2486,
    "preview": "<!DOCTYPE html>\n<html xmlns:layout=\"http://www.ultraq.net.nz/thymeleaf/layout\">\n<head>\n  <meta charset=\"UTF-8\"/>\n  <meta"
  },
  {
    "path": "app/views/users/_search.html.slim",
    "chars": 353,
    "preview": ".container-fluid.col-md-4.col-md-offset-4\n  = form_tag users_path, method: 'get', class: \"p-2\" do\n    .input-group\n     "
  },
  {
    "path": "app/views/users/index.html.slim",
    "chars": 559,
    "preview": "= render partial: \"search\"\n- if @users.count > 0\n  .table-responsive\n    table.table.table-striped\n      thead\n        t"
  },
  {
    "path": "app/views/users/new.html.erb",
    "chars": 1593,
    "preview": "<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  "
  },
  {
    "path": "app/views/users/show.html.slim",
    "chars": 5014,
    "preview": ".container.col-md-8\n  h5.mb-3 User Profile \n  hr\n  - if flash.key?(:success)\n    .alert.alert-success\n      = flash[:suc"
  },
  {
    "path": "app/views/vpns/_form.html.slim",
    "chars": 945,
    "preview": "  = form_for @vpn, :class => 'form-horizontal' do |f|\n    - if @vpn.errors.any?\n      #error_explanation\n        h2 = \"#"
  },
  {
    "path": "app/views/vpns/edit.html.slim",
    "chars": 231,
    "preview": ".container-fluid.col-md-4.col-md-offset-4\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n   "
  },
  {
    "path": "app/views/vpns/index.html.slim",
    "chars": 986,
    "preview": ".container-fluid.col-md-8.col-md-offset-4\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n   "
  },
  {
    "path": "app/views/vpns/new.html.slim",
    "chars": 225,
    "preview": ".container-fluid.col-md-4.col-md-offset-4\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n   "
  },
  {
    "path": "app/views/vpns/show.html.slim",
    "chars": 3906,
    "preview": ".container.container-profile\n  - if notice.present?\n    .alert.alert-primary role=\"alert\"\n      #notice\n        = notice"
  },
  {
    "path": "bin/bundle",
    "chars": 129,
    "preview": "#!/usr/bin/env ruby\nENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)\nload Gem.bin_path('bundler', '"
  },
  {
    "path": "bin/rails",
    "chars": 141,
    "preview": "#!/usr/bin/env ruby\nAPP_PATH = File.expand_path(\"../config/application\", __dir__)\nrequire_relative \"../config/boot\"\nrequ"
  },
  {
    "path": "bin/rake",
    "chars": 90,
    "preview": "#!/usr/bin/env ruby\nrequire_relative \"../config/boot\"\nrequire \"rake\"\nRake.application.run\n"
  },
  {
    "path": "bin/setup",
    "chars": 1010,
    "preview": "#!/usr/bin/env ruby\nrequire \"fileutils\"\n\n# path to your application root.\nAPP_ROOT = File.expand_path(\"..\", __dir__)\n\nde"
  },
  {
    "path": "bin/update",
    "chars": 782,
    "preview": "#!/usr/bin/env ruby\nrequire 'pathname'\nrequire 'fileutils'\ninclude FileUtils\n\n# path to your application root.\nAPP_ROOT "
  },
  {
    "path": "config/application.rb",
    "chars": 701,
    "preview": "require_relative \"boot\"\n\nrequire \"rails/all\"\n\n# Require the gems listed in Gemfile, including any gems\n# you've limited "
  },
  {
    "path": "config/boot.rb",
    "chars": 128,
    "preview": "ENV[\"BUNDLE_GEMFILE\"] ||= File.expand_path(\"../Gemfile\", __dir__)\n\nrequire \"bundler/setup\" # Set up gems listed in the G"
  },
  {
    "path": "config/cable.yml",
    "chars": 116,
    "preview": "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",
    "chars": 682,
    "preview": "default: &default\n  adapter: mysql2\n  pool: 5\n  timeout: 5000\n  host: <%= ENV['GATE_DB_HOST'] %>\n  port: <%= ENV['GATE_D"
  },
  {
    "path": "config/environment.rb",
    "chars": 128,
    "preview": "# Load the Rails application.\nrequire_relative \"application\"\n\n# Initialize the Rails application.\nRails.application.init"
  },
  {
    "path": "config/environments/development.rb",
    "chars": 2454,
    "preview": "require \"active_support/core_ext/integer/time\"\n\nRails.application.configure do\n  # Settings specified here will take pre"
  },
  {
    "path": "config/environments/integration.rb",
    "chars": 3342,
    "preview": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  "
  },
  {
    "path": "config/environments/production.rb",
    "chars": 3934,
    "preview": "require \"active_support/core_ext/integer/time\"\n\nRails.application.configure do\n  # Settings specified here will take pre"
  },
  {
    "path": "config/environments/test.rb",
    "chars": 2445,
    "preview": "require \"active_support/core_ext/integer/time\"\n\n# The test environment is used exclusively to run your application's\n# t"
  },
  {
    "path": "config/initializers/application_controller_renderer.rb",
    "chars": 162,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# ApplicationController.renderer.defaults.merge!(\n#   http_"
  },
  {
    "path": "config/initializers/assets.rb",
    "chars": 502,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Version of your assets, change this if you want to expire"
  },
  {
    "path": "config/initializers/backtrace_silencers.rb",
    "chars": 404,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're"
  },
  {
    "path": "config/initializers/content_security_policy.rb",
    "chars": 1044,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Define an application-wide content security policy.\n# See"
  },
  {
    "path": "config/initializers/cookies_serializer.rb",
    "chars": 244,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Specify a serializer for the signed and encrypted cookie "
  },
  {
    "path": "config/initializers/devise.rb",
    "chars": 13554,
    "preview": "# Use this hook to configure devise mailer, warden hooks and so forth.\n# Many of these configuration options can be set "
  },
  {
    "path": "config/initializers/dotenv.rb",
    "chars": 254,
    "preview": "begin\n  Dotenv.require_keys('GATE_DB_HOST',\n    'GATE_DB_PORT',\n    'GATE_DB_USER',\n    'GATE_DB_PASSWORD',\n    'CACHE_H"
  },
  {
    "path": "config/initializers/filter_parameter_logging.rb",
    "chars": 396,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Configure parameters to be filtered from the log file. Us"
  },
  {
    "path": "config/initializers/inflections.rb",
    "chars": 649,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format. Infl"
  },
  {
    "path": "config/initializers/mime_types.rb",
    "chars": 156,
    "preview": "# 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::"
  },
  {
    "path": "config/initializers/new_framework_defaults.rb",
    "chars": 882,
    "preview": "# Be sure to restart your server when you modify this file.\n#\n# This file contains migration options to ease your Rails "
  },
  {
    "path": "config/initializers/new_framework_defaults_7_0.rb",
    "chars": 6107,
    "preview": "# Be sure to restart your server when you modify this file.\n#\n# This file eases your Rails 7.0 framework defaults upgrad"
  },
  {
    "path": "config/initializers/permissions_policy.rb",
    "chars": 384,
    "preview": "# Define an application-wide HTTP permissions policy. For further\n# information see https://developers.google.com/web/up"
  },
  {
    "path": "config/initializers/session_store.rb",
    "chars": 143,
    "preview": "# Be sure to restart your server when you modify this file.\n\nRails.application.config.session_store :active_record_store"
  },
  {
    "path": "config/initializers/wrap_parameters.rb",
    "chars": 485,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# This file contains settings for ActionController::ParamsW"
  },
  {
    "path": "config/locales/devise.en.yml",
    "chars": 4084,
    "preview": "# Additional translations at https://github.com/plataformatec/devise/wiki/I18n\n\nen:\n  devise:\n    confirmations:\n      c"
  },
  {
    "path": "config/locales/en.bootstrap.yml",
    "chars": 579,
    "preview": "# Sample localization file for English. Add more files in this directory for other locales.\n# See https://github.com/sve"
  },
  {
    "path": "config/locales/en.yml",
    "chars": 634,
    "preview": "# Files in the config/locales directory are used for internationalization\n# and are automatically loaded by Rails. If yo"
  },
  {
    "path": "config/newrelic.yml",
    "chars": 1059,
    "preview": "common: &default_settings\n  # Required license key associated with your New Relic account.\n  license_key: <%= ENV['NEWRE"
  },
  {
    "path": "config/puma.rb",
    "chars": 2005,
    "preview": "# Puma can serve each request in a thread from an internal thread pool.\n# The `threads` method setting takes two numbers"
  },
  {
    "path": "config/redis.yml",
    "chars": 204,
    "preview": "default: &default\n  host: <%= ENV['CACHE_HOST'] %>\n  port: <%= ENV['CACHE_PORT'] %>\n  limit: 20\n\ndevelopment:\n  <<: *def"
  },
  {
    "path": "config/routes.rb",
    "chars": 7622,
    "preview": "Rails.application.routes.draw do\n  devise_for :users, :controllers => { :omniauth_callbacks => \"users/omniauth_callbacks"
  },
  {
    "path": "config/schedule.rb",
    "chars": 589,
    "preview": "# Use this file to easily define all of your cron jobs.\n#\n# It's helpful, but not entirely necessary to understand cron "
  },
  {
    "path": "config/secrets.yml",
    "chars": 1122,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Your secret key is used for verifying the integrity of si"
  },
  {
    "path": "config/spring.rb",
    "chars": 111,
    "preview": "%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",
    "chars": 1152,
    "preview": "test:\n  service: Disk\n  root: <%= Rails.root.join(\"tmp/storage\") %>\n\nlocal:\n  service: Disk\n  root: <%= Rails.root.join("
  }
]

// ... and 170 more files (download for full content)

About this extraction

This page contains the full source code of the gate-sso/gate GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 370 files (479.5 KB), approximately 139.5k tokens, and a symbol index with 488 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!