zuora_connect 3.0.2.pre.p → 3.0.2.pre.q

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10ba53dbb68c71e4355316f0446236cec11abb0e0a1c1fb50db56f932aa566a1
4
- data.tar.gz: 5fc99abb2ed44c998fff5ee1b80e02e0b7ccea9f8b905d4057ad9cdb50dbdf6c
3
+ metadata.gz: 01f35a8df0d99f5782e2a488d77986dfef279609aac6d88dc0687cb61ca2bc96
4
+ data.tar.gz: dc67e0d6e8557534ebadc62943e9746f7f6d53397b995b8d9c8708ce88233ba5
5
5
  SHA512:
6
- metadata.gz: e1258e6b3f018f184992fdbd2d956de1c4e5575ddf3ce531087cbda43e72703bc257a46bffdd8b14d7840b997109fd2e18f4c3b3c1a25144d8304debe6d96ddb
7
- data.tar.gz: 0ae63bcaee8a665c53f1b43b065111621d66550130c42632431b685be9332eab9452db43afc22bcd20a9a44260a6e4d200fb0e44bdc14c826084461cba18a0ea
6
+ metadata.gz: '082eef57746ff61b3dcf38b3ab9737c3f96c0e1437698e5e8d47cdcea34e29e3e6bee0e68d3529e073699747b2db2b9cff3fd4ccb9a00ee70c6a4c8ee9922777'
7
+ data.tar.gz: e44388593aff7b52334dfcc35a1876ba689d66a84defed23b7d5e9f52e55c6d932b35433fe9f8b5b94617e91d435337a6a9bfd48ed03ec5334ac0a2088afe7eb
@@ -1,8 +1,33 @@
1
1
  module ZuoraConnect
2
2
  class ApplicationController < ActionController::Base
3
3
  protect_from_forgery with: :exception
4
- before_action :authenticate_connect_app_request
5
- after_action :persist_connect_app_session
4
+ before_action :authenticate_connect_app_request, except: [:ldap_login]
5
+ after_action :persist_connect_app_session, except: [:ldap_login]
6
6
 
7
+ def ldap_login
8
+ require 'net-ldap'
9
+
10
+ username = request.parameters['ldap_username']
11
+ password = request.parameters['ldap_password']
12
+
13
+ begin
14
+ if ZuoraConnect::LDAP::Adapter.valid_credentials?(username, password)
15
+ session['ldapAdmin'] = true
16
+ respond_to do |format|
17
+ format.html { redirect_to '/admin/app_instances' }
18
+ end
19
+ else
20
+ render 'zuora_connect/application/ldap_login', locals: {
21
+ title: 'LDAP Authentication Failed',
22
+ message: 'Invalid username or password'
23
+ }
24
+ end
25
+ rescue Net::LDAP::Error
26
+ render 'zuora_connect/application/ldap_login', locals: {
27
+ title: 'LDAP Authentication Net Error',
28
+ message: 'Failed to connect to server while authenticating the LDAP credentials. Please retry later.'
29
+ }
30
+ end
31
+ end
7
32
  end
8
33
  end
@@ -0,0 +1,16 @@
1
+ module ZuoraConnect
2
+ module LDAP
3
+ module Adapter
4
+ def self.valid_credentials?(login, password_plaintext)
5
+ options = {
6
+ login: login,
7
+ password: password_plaintext,
8
+ ldap_auth_username_builder: proc { |attribute, login, _| "#{attribute}=#{login}" },
9
+ admin: true
10
+ }
11
+ ldap_connection = ZuoraConnect::LDAP::Connection.new(options)
12
+ ldap_connection.authorized?
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,123 @@
1
+ # Copied from devise lib and deleted not useful functionality
2
+
3
+ module ZuoraConnect
4
+ module LDAP
5
+ class Connection
6
+ attr_reader :ldap, :login
7
+
8
+ def initialize(params = {})
9
+ ldap_config = YAML.load(ERB.new(File.read("#{Rails.root}/config/ldap.yml")).result)[Rails.env]
10
+ ldap_options = params
11
+
12
+ # Allow `ssl: true` shorthand in YAML, but enable more control with `encryption`
13
+ ldap_config['ssl'] = :simple_tls if ldap_config['ssl'] === true
14
+ ldap_options[:encryption] = ldap_config['ssl'].to_sym if ldap_config['ssl']
15
+ ldap_options[:encryption] = ldap_config['encryption'] if ldap_config['encryption']
16
+
17
+ @ldap = Net::LDAP.new(ldap_options)
18
+ @ldap.host = ldap_config['host']
19
+ @ldap.port = ldap_config['port']
20
+ @ldap.base = ldap_config['base']
21
+ @attribute = ldap_config['attribute']
22
+ @allow_unauthenticated_bind = ldap_config['allow_unauthenticated_bind']
23
+
24
+ @ldap_auth_username_builder = params[:ldap_auth_username_builder]
25
+
26
+ @group_base = ldap_config['group_base']
27
+ @check_group_membership = ldap_config.key?('check_group_membership') ? ldap_config['check_group_membership'] : false
28
+ @check_group_membership_without_admin = ldap_config.key?('check_group_membership_without_admin') ? ldap_config['check_group_membership_without_admin'] : false
29
+ @required_groups = ldap_config['required_groups']
30
+ @group_membership_attribute = ldap_config.key?('group_membership_attribute') ? ldap_config['group_membership_attribute'] : 'uniqueMember'
31
+ @required_attributes = ldap_config['require_attribute']
32
+ @required_attributes_presence = ldap_config['require_attribute_presence']
33
+
34
+ @ldap.auth ldap_config['admin_user'], ldap_config['admin_password'] if params[:admin]
35
+
36
+ @login = params[:login]
37
+ @password = params[:password]
38
+ @new_password = params[:new_password]
39
+ end
40
+
41
+ def dn
42
+ @dn ||= begin
43
+ ZuoraConnect::logger.debug("LDAP dn lookup: #{@attribute}=#{@login}")
44
+ ldap_entry = search_for_login
45
+ if ldap_entry.nil?
46
+ @ldap_auth_username_builder.call(@attribute,@login,@ldap)
47
+ else
48
+ ldap_entry.dn
49
+ end
50
+ end
51
+ end
52
+
53
+ def search_for_login
54
+ @login_ldap_entry ||= begin
55
+ ZuoraConnect::logger.debug("LDAP search for login: #{@attribute}=#{@login}")
56
+ filter = Net::LDAP::Filter.eq(@attribute.to_s, @login.to_s)
57
+ ldap_entry = nil
58
+ match_count = 0
59
+ @ldap.search(:filter => filter) {|entry| ldap_entry = entry; match_count+=1}
60
+ op_result= @ldap.get_operation_result
61
+ if op_result.code!=0
62
+ ZuoraConnect::logger.debug("LDAP Error #{op_result.code}: #{op_result.message}")
63
+ end
64
+ ZuoraConnect::logger.debug("LDAP search yielded #{match_count} matches")
65
+ ldap_entry
66
+ end
67
+ end
68
+
69
+ def authenticate!
70
+ return false unless @password.present? || @allow_unauthenticated_bind
71
+ @ldap.auth(dn, @password)
72
+ @ldap.bind
73
+ end
74
+
75
+ def authenticated?
76
+ authenticate!
77
+ end
78
+
79
+ def last_message_bad_credentials?
80
+ @ldap.get_operation_result.error_message.to_s.include? 'AcceptSecurityContext error, data 52e'
81
+ end
82
+
83
+ def last_message_expired_credentials?
84
+ @ldap.get_operation_result.error_message.to_s.include? 'AcceptSecurityContext error, data 773'
85
+ end
86
+
87
+ def authorized?
88
+ ZuoraConnect::logger.debug("Authorizing user #{dn}")
89
+ if !authenticated?
90
+ if last_message_bad_credentials?
91
+ ZuoraConnect::logger.debug('Not authorized because of invalid credentials.')
92
+ elsif last_message_expired_credentials?
93
+ ZuoraConnect::logger.debug('Not authorized because of expired credentials.')
94
+ else
95
+ ZuoraConnect::logger.debug('Not authorized because not authenticated.')
96
+ end
97
+
98
+ false
99
+ elsif !in_required_groups?
100
+ ZuoraConnect::logger.debug('Not authorized because not in required groups.')
101
+ false
102
+ else
103
+ true
104
+ end
105
+ end
106
+
107
+ def in_required_groups?
108
+ return true unless @check_group_membership || @check_group_membership_without_admin
109
+
110
+ return false if @required_groups.nil?
111
+
112
+ @required_groups.each do |group|
113
+ if group.is_a?(Array)
114
+ return false unless in_group?(group[1], group[0])
115
+ else
116
+ return false unless in_group?(group)
117
+ end
118
+ end
119
+ true
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,194 @@
1
+ <html>
2
+ <head>
3
+ <title>LDAP Log in</title>
4
+ <style>
5
+ * {
6
+ box-sizing: border-box;
7
+ margin: 0;
8
+ padding: 0;
9
+ font-family: Raleway, sans-serif;
10
+ }
11
+
12
+ body {
13
+ background: linear-gradient(90deg, #C7C5F4, #776BCC);
14
+ }
15
+
16
+ .container {
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ min-height: 100vh;
21
+ }
22
+
23
+ .screen {
24
+ background: linear-gradient(90deg, #5D54A4, #7C78B8);
25
+ position: relative;
26
+ height: 600px;
27
+ width: 360px;
28
+ box-shadow: 0px 0px 24px #5C5696;
29
+ }
30
+
31
+ .screen__content {
32
+ z-index: 1;
33
+ position: relative;
34
+ height: 100%;
35
+ }
36
+
37
+ .screen__background {
38
+ position: absolute;
39
+ top: 0;
40
+ left: 0;
41
+ right: 0;
42
+ bottom: 0;
43
+ z-index: 0;
44
+ -webkit-clip-path: inset(0 0 0 0);
45
+ clip-path: inset(0 0 0 0);
46
+ }
47
+
48
+ .screen__background__shape {
49
+ transform: rotate(45deg);
50
+ position: absolute;
51
+ }
52
+
53
+ .screen__background__shape1 {
54
+ height: 520px;
55
+ width: 520px;
56
+ background: #FFF;
57
+ top: -50px;
58
+ right: 120px;
59
+ border-radius: 0 72px 0 0;
60
+ }
61
+
62
+ .screen__background__shape2 {
63
+ height: 220px;
64
+ width: 220px;
65
+ background: #6C63AC;
66
+ top: -172px;
67
+ right: 0;
68
+ border-radius: 32px;
69
+ }
70
+
71
+ .screen__background__shape3 {
72
+ height: 540px;
73
+ width: 190px;
74
+ background: linear-gradient(270deg, #5D54A4, #6A679E);
75
+ top: -24px;
76
+ right: 0;
77
+ border-radius: 32px;
78
+ }
79
+
80
+ .screen__background__shape4 {
81
+ height: 400px;
82
+ width: 200px;
83
+ background: #7E7BB9;
84
+ top: 420px;
85
+ right: 50px;
86
+ border-radius: 60px;
87
+ }
88
+
89
+ .login {
90
+ width: 320px;
91
+ padding: 30px;
92
+ padding-top: 156px;
93
+ }
94
+
95
+ .login__field {
96
+ padding: 20px 0px;
97
+ position: relative;
98
+ }
99
+
100
+ .login__icon {
101
+ position: absolute;
102
+ top: 30px;
103
+ color: #7875B5;
104
+ }
105
+
106
+ .login__input {
107
+ border: none;
108
+ border-bottom: 2px solid #D1D1D4;
109
+ background: none;
110
+ padding: 10px;
111
+ padding-left: 24px;
112
+ font-weight: 700;
113
+ width: 75%;
114
+ transition: .2s;
115
+ }
116
+
117
+ .login__input:active,
118
+ .login__input:focus,
119
+ .login__input:hover {
120
+ outline: none;
121
+ border-bottom-color: #6A679E;
122
+ }
123
+
124
+ .login__submit {
125
+ background: #fff;
126
+ font-size: 14px;
127
+ margin-top: 30px;
128
+ padding: 16px 20px;
129
+ border-radius: 26px;
130
+ border: 1px solid #D4D3E8;
131
+ text-transform: uppercase;
132
+ font-weight: 700;
133
+ display: flex;
134
+ align-items: center;
135
+ width: 100%;
136
+ color: #4C489D;
137
+ box-shadow: 0px 2px 2px #5C5696;
138
+ cursor: pointer;
139
+ transition: .2s;
140
+ }
141
+
142
+ .login__submit:active,
143
+ .login__submit:focus,
144
+ .login__submit:hover {
145
+ border-color: #6A679E;
146
+ outline: none;
147
+ }
148
+
149
+ .error{
150
+ color: #D8000C;
151
+ background-color: #FFBABA;
152
+ }
153
+ </style>
154
+ </head>
155
+
156
+ <body>
157
+ <div class="container">
158
+ <div class="screen">
159
+ <div class="screen__content">
160
+
161
+ <%= form_with class: "login", url: "/connect/ldap_login", method: :post do |f| %>
162
+ <div class="login__field">
163
+ <%= f.text_field :ldap_username, placeholder: "Username", class: "login__input" %>
164
+ </div>
165
+
166
+ <div class="login__field">
167
+ <%= f.password_field :ldap_password, placeholder: "Password", class: "login__input" %>
168
+ </div>
169
+
170
+ <%= f.button "Log in", class: "button login__submit" %>
171
+
172
+ <% end %>
173
+
174
+ <% if defined?(message) && defined?(title) %>
175
+ <div class="error">
176
+ <h3><%= title %></h3>
177
+ <p><%= message.html_safe %></p>
178
+ </div>
179
+ <% end %>
180
+
181
+ </div>
182
+
183
+ <div class="screen__background">
184
+ <span class="screen__background__shape screen__background__shape4"></span>
185
+ <span class="screen__background__shape screen__background__shape3"></span>
186
+ <span class="screen__background__shape screen__background__shape2"></span>
187
+ <span class="screen__background__shape screen__background__shape1"></span>
188
+ </div>
189
+
190
+ </div>
191
+ </div>
192
+
193
+ </body>
194
+ </html>
data/config/routes.rb CHANGED
@@ -15,4 +15,6 @@ ZuoraConnect::Engine.routes.draw do
15
15
  end
16
16
  end
17
17
  end
18
+
19
+ post "ldap_login" => 'application#ldap_login'
18
20
  end
@@ -320,6 +320,11 @@ module ZuoraConnect
320
320
  elsif cookies['ZSession'].present?
321
321
  zuora_client = ZuoraAPI::Basic.new(url: "https://#{zuora_host}", session: cookies['ZSession'], entity_id: zuora_entity_id)
322
322
  auth_headers.merge!({'Authorization' => "ZSession-a3N2w #{zuora_client.get_session(prefix: false, auth_type: :basic)}"})
323
+ elsif session["ldapAdmin"]
324
+ ZuoraConnect::logger.debug("Admin session found")
325
+ elsif ZuoraConnect::AppInstance::INTERNAL_HOSTS.include?(request.headers.fetch("HOST", nil))
326
+ render "zuora_connect/application/ldap_login"
327
+ return
323
328
  else
324
329
  render "zuora_connect/static/error_handled", :locals => {
325
330
  :title => "Missing Authorization Token",
@@ -336,7 +341,7 @@ module ZuoraConnect
336
341
  missmatched_entity = session["ZuoraCurrentEntity"] != zuora_entity_id
337
342
  missing_identity = session["ZuoraCurrentIdentity"].blank?
338
343
 
339
- if (missing_identity || missmatched_entity || different_zsession)
344
+ if (missing_identity || missmatched_entity || different_zsession) && (!session["ldapAdmin"])
340
345
  zuora_details.merge!({'identity' => {'different_zsession' => different_zsession, 'missing_identity' => missing_identity, 'missmatched_entity' => missmatched_entity}})
341
346
  identity, response = zuora_client.rest_call(
342
347
  url: zuora_client.rest_endpoint("identity"),
@@ -367,7 +372,6 @@ module ZuoraConnect
367
372
  if is_different_user || params[:sidebar_launch].to_s.to_bool
368
373
  zuora_instance_id = nil
369
374
  ZuoraConnect.logger.debug("UI Authorization", zuora: zuora_details)
370
-
371
375
  client_describe, response = zuora_client.rest_call(
372
376
  url: zuora_client.rest_endpoint("genesis/user/info").gsub('v1/', ''),
373
377
  session_type: zuora_client.class == ZuoraAPI::Oauth ? :bearer : :basic,
@@ -378,8 +382,10 @@ module ZuoraConnect
378
382
  end
379
383
  end
380
384
 
385
+ if session["ldapAdmin"]
386
+ appinstances = ZuoraConnect::AppInstance.pluck(:id, :name)
381
387
  #Find matching app instances.
382
- if zuora_instance_id.present?
388
+ elsif zuora_instance_id.present?
383
389
  appinstances = ZuoraConnect::AppInstance.where("zuora_entity_ids ?& array[:entities] = true AND zuora_domain = :host AND id = :id", entities: [zuora_entity_id], host: zuora_client.rest_domain, id: zuora_instance_id.to_i).pluck(:id, :name)
384
390
  else
385
391
  #if app_instance_ids is present then permissions still controlled by connect
@@ -417,11 +423,22 @@ module ZuoraConnect
417
423
  appinstances ||= ZuoraConnect::AppInstance.where("zuora_entity_ids ?& array[:entities] = true AND zuora_domain = :host", entities: [zuora_entity_id], host: zuora_client.rest_domain).pluck(:id, :name)
418
424
  end
419
425
 
420
- zuora_user_id = cookies['Zuora-User-Id'] || session["ZuoraCurrentIdentity"]['userId'] || request.headers["Zuora-User-Id"]
426
+ if session["ldapAdmin"]
427
+ zuora_user_id = "3"
428
+ else
429
+ zuora_user_id = cookies['Zuora-User-Id'] || session["ZuoraCurrentIdentity"]['userId'] || request.headers["Zuora-User-Id"]
430
+ end
421
431
 
422
432
  if appinstances.size == 1
423
- ZuoraConnect.logger.debug("Instance is #{appinstances.to_h.keys.first}")
424
433
  @appinstance = ZuoraConnect::AppInstance.find(appinstances.to_h.keys.first)
434
+ session["appInstance"] = @appinstance.id
435
+ ZuoraConnect::ZuoraUser.current_user_id = zuora_user_id
436
+ end
437
+
438
+ if session["ldapAdmin"]
439
+ # Maybe error. Should we return because of condition?
440
+ session["#{@appinstance.id}::admin"] = true
441
+ return
425
442
  end
426
443
 
427
444
  # One deployed instance with credentials
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ZuoraConnect
4
- VERSION = "3.0.2-p"
4
+ VERSION = "3.0.2-q"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zuora_connect
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.2.pre.p
4
+ version: 3.0.2.pre.q
5
5
  platform: ruby
6
6
  authors:
7
7
  - Connect Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-24 00:00:00.000000000 Z
11
+ date: 2022-04-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: apartment
@@ -162,6 +162,20 @@ dependencies:
162
162
  - - "~>"
163
163
  - !ruby/object:Gem::Version
164
164
  version: '0.1'
165
+ - !ruby/object:Gem::Dependency
166
+ name: net-ldap
167
+ requirement: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ type: :runtime
173
+ prerelease: false
174
+ version_requirements: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
165
179
  - !ruby/object:Gem::Dependency
166
180
  name: rspec
167
181
  requirement: !ruby/object:Gem::Requirement
@@ -365,6 +379,8 @@ files:
365
379
  - app/controllers/zuora_connect/api/v1/app_instance_controller.rb
366
380
  - app/controllers/zuora_connect/application_controller.rb
367
381
  - app/controllers/zuora_connect/static_controller.rb
382
+ - app/helpers/zuora_connect/LDAP/adapter.rb
383
+ - app/helpers/zuora_connect/LDAP/connection.rb
368
384
  - app/helpers/zuora_connect/api/v1/app_instance_helper.rb
369
385
  - app/helpers/zuora_connect/application_helper.rb
370
386
  - app/models/concerns/zuora_connect/auditable.rb
@@ -375,6 +391,7 @@ files:
375
391
  - app/models/zuora_connect/zuora_user.rb
376
392
  - app/views/layouts/zuora_connect/application.html.erb
377
393
  - app/views/sql/refresh_aggregate_table.txt
394
+ - app/views/zuora_connect/application/ldap_login.html.erb
378
395
  - app/views/zuora_connect/static/error_handled.html.erb
379
396
  - app/views/zuora_connect/static/error_handled.js.erb
380
397
  - app/views/zuora_connect/static/error_unhandled.erb