Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## [4.3.3] - 2026-04-22
- Switch to Basic Auth from TOTP auth

## [4.3.2] - 2026-02-09

### Dependency updates
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.3.2
4.3.3
2 changes: 0 additions & 2 deletions campact_user_service.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ Gem::Specification.new do |spec|
# Runtime dependencies
spec.add_dependency "faraday", "~> 2.14"
spec.add_dependency "json", "~> 2.1"
spec.add_dependency "rotp", "~> 6"

# Development dependencies
spec.add_development_dependency "byebug"
spec.add_development_dependency "faraday-detailed_logger", "~> 2.1"
Expand Down
27 changes: 3 additions & 24 deletions lib/campact_user_service/client.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
require 'faraday'
require 'json'
require 'rotp'
require 'campact_user_service/response_error'

module CampactUserService
class Client
TIMEOUT = 60.freeze
OPEN_TIMEOUT = 20.freeze

attr_reader :connection, :host, :port, :topt_authorization
attr_reader :connection, :host, :port, :basic_auth

def initialize(options)
@host = options.fetch(:host)
@port = options[:port]
@topt_authorization = options[:topt_authorization]
@basic_auth = options[:basic_auth]
faraday_options = default_faraday_options.merge(options.delete(:faraday) || {})
adapter = faraday_options.delete(:adapter) || Faraday.default_adapter

@connection = Faraday.new(endpoint, faraday_options) do |faraday|
faraday.adapter adapter
faraday.request :json
faraday.request :authorization, :basic, basic_auth[:user], basic_auth[:password] if basic_auth
end
end

Expand All @@ -44,9 +44,6 @@ def request(verb, path, options)
req.body = options[:body]
end

if topt_authorization
req.headers['authorization'] = authorization(topt_authorization)
end
end

case response.status
Expand Down Expand Up @@ -93,23 +90,5 @@ def format_cookies(cookies)
end
end

def authorization(totp_options)
user = totp_options.fetch(:user)
secret = totp_options.fetch(:secret)

token = [user, auth_pass(secret)].join(':')

"Token #{token}"
end

def auth_pass(secret)
totp_secret = ROTP::Base32.encode(secret)

ROTP::TOTP.new(totp_secret, {
digest: 'sha256',
digits: 8,
interval: 30
}).now
end
end
end
36 changes: 19 additions & 17 deletions spec/client_spec.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require 'base64'
require 'spec_helper'

describe CampactUserService::Client do
Expand Down Expand Up @@ -131,23 +132,6 @@
subject.get_request('/foo/bar', cookies: {'foo' => 'bar', 'xyz' => 'abc'})
end

it 'should set TOTP authorization header' do
allow(response).to receive(:status).and_return(200)
allow(response).to receive(:body).and_return(nil)

totp_secret = ROTP::Base32.encode('shh! a secret!')

totp = double
expect(totp).to receive(:now).and_return('totp_token')
expect(ROTP::TOTP).to receive(:new).with(totp_secret, hash_including(digest: 'sha256', digits: 8, interval: 30)).and_return(totp)

expect(headers_builder).to receive(:[]=).with('authorization', 'Token api_user:totp_token')

subject = CampactUserService::Client.new(host: 'demo.campact.de', topt_authorization: {user: 'api_user', secret: 'shh! a secret!'})

subject.get_request('/foo/bar')
end

it 'should parse JSON response on successful response' do
allow(response).to receive(:status).and_return(200)
allow(response).to receive(:body).and_return({a_field: 'foo', another_field: 'bar'}.to_json)
Expand All @@ -161,6 +145,24 @@
end
end

context 'with HTTP connection stubbed' do
before(:each) do
WebMock.disable_net_connect!
end

it 'should set Basic Auth authorization header' do
stub_get =
stub_request(:get, 'https://demo.campact.de/foo/bar')
.with(headers: {'Authorization' => 'Basic ' + Base64.strict_encode64('api_user:s3cr3t')})
.to_return(status: 200, body: '{}')

subject = CampactUserService::Client.new(host: 'demo.campact.de', basic_auth: {user: 'api_user', password: 's3cr3t'})
subject.get_request('/foo/bar')

assert_requested(stub_get)
end
end

describe '#delete_request' do
let(:connection) { double }
let(:request_builder) { double }
Expand Down
Loading