Merge branch 'allow-unregister' into 'master'

Christopher Vollick created

Unregister when asked

See merge request soprani.ca/sgx-bwmsgsv2!7

Change summary

.rubocop.yml             | 11 ++++-
Gemfile                  |  4 ++
lib/registration_repo.rb | 63 ++++++++++++++++++++++++++++++++
r2s-bwmsgsv2.rb          |  3 +
sgx-bwmsgsv2.rb          | 82 +++++++++++++----------------------------
5 files changed, 104 insertions(+), 59 deletions(-)

Detailed changes

.rubocop.yml 🔗

@@ -1,8 +1,10 @@
 Metrics/LineLength:
   Max: 80
 
-Layout/Tab:
+Layout/IndentationStyle:
   Enabled: false
+  EnforcedStyle: tabs
+  IndentationWidth: 2
 
 Layout/IndentationWidth:
   Width: 1 # one tab
@@ -40,10 +42,13 @@ Metrics/ParameterLists:
 Metrics/PerceivedComplexity:
   Max: 30
 
+Naming/MethodParameterName:
+  Enabled: false
+
 Style/AndOr:
   Enabled: false
 
-Layout/AlignParameters:
+Layout/ParameterAlignment:
   Enabled: false
 
 Style/BlockDelimiters:
@@ -110,7 +115,7 @@ Style/MultilineBlockChain:
 Layout/SpaceAroundEqualsInParameterDefault:
   EnforcedStyle: no_space
 
-Layout/IndentArray:
+Layout/FirstArrayElementIndentation:
   EnforcedStyle: consistent
 
 Style/SymbolArray:

Gemfile 🔗

@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 source 'https://rubygems.org'
 
 gem 'activesupport', '<5.0.0'
@@ -7,7 +9,9 @@ gem 'em-http-request'
 gem 'em_promise.rb'
 gem 'eventmachine'
 gem 'goliath'
+gem 'lazy_object'
 gem 'log4r'
+gem 'rack', '< 2'
 gem 'redis'
 
 group :development do

lib/registration_repo.rb 🔗

@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'lazy_object'
+
+class RegistrationRepo
+	class Conflict < StandardError; end
+
+	def initialize(redis: LazyObject.new { REDIS })
+		@redis = redis
+	end
+
+	def find(jid)
+		REDIS.lrange(cred_key(jid), 0, 3)
+	end
+
+	def find_jid(tel)
+		REDIS.get(jid_key(tel))
+	end
+
+	def put(jid, *creds)
+		tel = creds.last
+
+		EMPromise.all([
+			find(jid),
+			REDIS.set(
+				jid_key(tel),
+				Blather::JID.new(jid).stripped.to_s,
+				"NX", "GET"
+			)
+		]).then { |(oldcreds, oldjid)|
+			if oldjid && oldjid != jid.stripped.to_s
+				raise Conflict, "Another user exists for #{tel}"
+			end
+
+			if !oldcreds.empty? && oldcreds != creds
+				REDIS.set(jid_key(tel), oldjid).then do
+					raise Conflict, "Another user exists for #{jid}"
+				end
+			end
+		}.then {
+			REDIS.rpush(cred_key(jid), *creds)
+		}
+	end
+
+	def delete(jid)
+		find(jid).then { |creds|
+			REDIS.del(
+				cred_key(jid),
+				jid_key(creds.last)
+			)
+		}
+	end
+
+protected
+
+	def cred_key(jid)
+		"catapult_cred-#{Blather::JID.new(jid).stripped}"
+	end
+
+	def jid_key(tel)
+		"catapult_jid-#{tel}"
+	end
+end

r2s-bwmsgsv2.rb 🔗

@@ -1,5 +1,6 @@
 #!/usr/bin/env ruby
-#
+# frozen_string_literal: true
+
 # Copyright (C) 2020  Denver Gingerich <denver@ossguy.com>
 #
 # This file is part of sgx-bwmsgsv2.

sgx-bwmsgsv2.rb 🔗

@@ -1,5 +1,6 @@
 #!/usr/bin/env ruby
-#
+# frozen_string_literal: true
+
 # Copyright (C) 2017-2020  Denver Gingerich <denver@ossguy.com>
 # Copyright (C) 2017  Stephen Paul Weber <singpolyma@singpolyma.net>
 #
@@ -33,6 +34,8 @@ require 'log4r'
 
 require 'em_promise'
 
+require_relative 'lib/registration_repo'
+
 def panic(e)
 	puts "Shutting down gateway due to exception: #{e.message}"
 	puts e.backtrace
@@ -83,6 +86,7 @@ end
 module SGXbwmsgsv2
 	extend Blather::DSL
 
+	@registration_repo = RegistrationRepo.new
 	@client = SGXClient.new
 	@gateway_features = [
 		"http://jabber.org/protocol/disco#info",
@@ -351,6 +355,7 @@ module SGXbwmsgsv2
 		EMPromise.resolve(m.to.node.to_s).then { |num_dest|
 			if num_dest =~ /\A\+?[0-9]+(?:;.*)?\Z/
 				next num_dest if num_dest[0] == '+'
+
 				shortcode = extract_shortcode(num_dest)
 				next shortcode if shortcode
 			end
@@ -365,8 +370,7 @@ module SGXbwmsgsv2
 	end
 
 	def self.fetch_catapult_cred_for(jid)
-		cred_key = "catapult_cred-#{jid.stripped}"
-		REDIS.lrange(cred_key, 0, 3).then { |creds|
+		@registration_repo.find(jid).then { |creds|
 			if creds.length < 4
 				# TODO: add text re credentials not registered
 				EMPromise.reject(
@@ -388,15 +392,13 @@ module SGXbwmsgsv2
 			validate_num(m),
 			fetch_catapult_cred_for(m.from)
 		]).then { |(num_dest, creds)|
-			jid_key = "catapult_jid-#{num_dest}"
-			REDIS.get(jid_key).then { |jid|
+			@registration_repo.find_jid(num_dest).then { |jid|
 				[jid, num_dest] + creds
 			}
 		}.then { |(jid, num_dest, *creds)|
 			if jid
-				cred_key = "catapult_cred-#{jid}"
-				REDIS.lrange(cred_key, 0, 0).then { |other_user|
-					[jid, num_dest] + creds + other_user
+				@registration_repo.find(jid).then { |other_user|
+					[jid, num_dest] + creds + other_user.first
 				}
 			else
 				[jid, num_dest] + creds + [nil]
@@ -522,44 +524,13 @@ module SGXbwmsgsv2
 	end
 
 	def self.check_then_register(i, *creds)
-		jid_key = "catapult_jid-#{creds.last}"
-		bare_jid = i.from.stripped
-		cred_key = "catapult_cred-#{bare_jid}"
-
-		REDIS.get(jid_key).then { |existing_jid|
-			if existing_jid && existing_jid != bare_jid
-				# TODO: add/log text: credentials exist already
-				EMPromise.reject([:cancel, 'conflict'])
-			end
-		}.then {
-			REDIS.lrange(cred_key, 0, 3)
-		}.then { |existing_creds|
-			# TODO: add/log text: credentials exist already
-			if existing_creds.length == 4 && creds != existing_creds
-				EMPromise.reject([:cancel, 'conflict'])
-			elsif existing_creds.length < 4
-				REDIS.rpush(cred_key, *creds).then { |length|
-					if length != 4
-						EMPromise.reject([
-							:cancel,
-							'internal-server-error'
-						])
-					end
-				}
-			end
-		}.then {
-			# not necessary if existing_jid non-nil, easier this way
-			REDIS.set(jid_key, bare_jid)
-		}.then { |result|
-			if result != 'OK'
-				# TODO: add txt re push failure
-				EMPromise.reject(
-					[:cancel, 'internal-server-error']
-				)
-			end
-		}.then {
-			write_to_stream i.reply
-		}
+		registration_repo
+			.put(i.from, *creds)
+			.catch_only(RegistrationRepo::Conflict) { |e|
+				EMPromise.reject([:cancel, 'conflict', e.message])
+			}.then {
+				write_to_stream i.reply
+			}
 	end
 
 	def self.creds_from_registration_query(qn)
@@ -568,6 +539,7 @@ module SGXbwmsgsv2
 		if xn
 			xn.children.each_with_object({}) do |field, h|
 				next if field.element_name != "field"
+
 				val = field.children.find { |v|
 					v.element_name == "value"
 				}
@@ -607,8 +579,9 @@ module SGXbwmsgsv2
 			qn.children.find { |v| v.element_name == "remove" }
 		).then { |rn|
 			if rn
-				puts "received <remove/> - ignoring for now..."
-				EMPromise.reject(:done)
+				@registration_repo.delete(i.from).then do
+					EMPromise.reject(:done)
+				end
 			else
 				creds_from_registration_query(qn)
 			end
@@ -735,9 +708,8 @@ module SGXbwmsgsv2
 			process_registration(i, qn)
 		when :get
 			bare_jid = i.from.stripped
-			cred_key = "catapult_cred-#{bare_jid}"
-			REDIS.lindex(cred_key, 3).then { |existing_number|
-				reply = registration_form(i.reply, existing_number)
+			@registration_repo.find(bare_jid).then { |creds|
+				reply = registration_form(i.reply, creds.last)
 				puts "RESPONSE2: #{reply.inspect}"
 				write_to_stream reply
 			}
@@ -773,6 +745,8 @@ end
 class WebhookHandler < Goliath::API
 	use Goliath::Rack::Params
 
+	@registration_repo = RegistrationRepo.new
+
 	def response(env)
 		# TODO: add timestamp grab here, and MUST include ./tai version
 
@@ -837,8 +811,7 @@ class WebhookHandler < Goliath::API
 				';phone-context=ca-us.phone-context.soprani.ca'
 		end
 
-		jid_key = "catapult_jid-#{users_num}"
-		bare_jid = REDIS.get(jid_key).promise.sync
+		bare_jid = @registration_repo.find_jid(users_num).sync
 
 		if !bare_jid
 			puts "jid_key (#{jid_key}) DNE; BW API misconfigured?"
@@ -950,8 +923,7 @@ class WebhookHandler < Goliath::API
 			end
 
 			if not msg
-				msg = Blather::Stanza::Message.new(bare_jid,
-					text)
+				msg = Blather::Stanza::Message.new(bare_jid, text)
 			end
 		else # per prior switch, this is:  jparams['direction'] == 'out'
 			tag_parts = jparams['tag'].split(/ /, 2)