Skip to content

Commit da982a1

Browse files
refactor
1 parent 01446c7 commit da982a1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2700
-364
lines changed

.rubocop.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,12 @@ Style/StringLiteralsInInterpolation:
99

1010
Style/Documentation:
1111
Enabled: false
12+
13+
Naming/MethodParameterName:
14+
AllowedNames:
15+
- cn
16+
- uid
17+
18+
Metrics/ClassLength:
19+
Exclude:
20+
- 'test/**/*'

.simplecov

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# frozen_string_literal: true
2+
3+
SimpleCov.start do
4+
add_filter "/test/"
5+
enable_coverage :branch
6+
primary_coverage :branch
7+
end

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ gem "minitest", "~> 5.16"
1212

1313
gem "rubocop", "~> 1.21"
1414

15+
gem "simplecov"
16+
1517
gem "vcr", "~> 6.0"

Gemfile.lock

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ GEM
1010
ast (2.4.3)
1111
base64 (0.3.0)
1212
date (3.4.1)
13+
docile (1.4.1)
1314
erb (5.0.2)
1415
faraday (2.14.0)
1516
faraday-net_http (>= 2.0, < 3.5)
@@ -65,6 +66,12 @@ GEM
6566
parser (>= 3.3.7.2)
6667
prism (~> 1.4)
6768
ruby-progressbar (1.13.0)
69+
simplecov (0.22.0)
70+
docile (~> 1.1)
71+
simplecov-html (~> 0.11)
72+
simplecov_json_formatter (~> 0.1)
73+
simplecov-html (0.13.2)
74+
simplecov_json_formatter (0.1.4)
6875
stringio (3.1.7)
6976
tsort (0.2.0)
7077
unicode-display_width (3.2.0)
@@ -84,6 +91,7 @@ DEPENDENCIES
8491
minitest (~> 5.16)
8592
rake (~> 13.0)
8693
rubocop (~> 1.21)
94+
simplecov
8795
vcr (~> 6.0)
8896

8997
BUNDLED WITH

README.md

Lines changed: 117 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,13 @@ gem install fripa
2424

2525
## Configuration
2626

27-
Fripa offers multiple ways to configure your FreeIPA connection:
27+
Configure your FreeIPA server settings globally. These settings are shared across all client instances:
2828

2929
### With a block
3030

3131
```ruby
3232
Fripa.configure do |config|
3333
config.host = 'ipa.example.com'
34-
config.username = 'admin'
35-
config.password = 'secret'
3634
config.verify_ssl = true # default: true
3735
end
3836
```
@@ -42,8 +40,7 @@ end
4240
```ruby
4341
Fripa.config = {
4442
host: 'ipa.example.com',
45-
username: 'admin',
46-
password: 'secret'
43+
verify_ssl: true
4744
}
4845
```
4946

@@ -52,8 +49,7 @@ Fripa.config = {
5249
```ruby
5350
config = Fripa::Configuration.new(
5451
host: 'ipa.example.com',
55-
username: 'admin',
56-
password: 'secret'
52+
verify_ssl: true
5753
)
5854
Fripa.config = config
5955
```
@@ -62,68 +58,158 @@ Fripa.config = config
6258

6359
```ruby
6460
Fripa.config.host = 'ipa.example.com'
65-
Fripa.config.username = 'admin'
66-
Fripa.config.password = 'secret'
61+
Fripa.config.verify_ssl = false
6762
```
6863

6964
## Usage
7065

71-
### Authentication
66+
### Creating a Client
7267

73-
After configuring Fripa, authenticate to obtain a session:
68+
Create a client instance with user credentials. Each client maintains its own session:
7469

7570
```ruby
76-
authenticator = Fripa::Authenticator.new
77-
authenticator.login!
71+
client = Fripa::Client.new(
72+
username: 'your-username',
73+
password: 'your-password'
74+
)
75+
```
76+
77+
The client will automatically authenticate when making API calls.
7878

79-
# The session cookie is now stored in the configuration
80-
puts Fripa.config.session_cookie
79+
### Manual Authentication (Optional)
80+
81+
If you need to authenticate manually:
82+
83+
```ruby
84+
client.authenticator.login!
85+
# The session cookie is now stored in the client
86+
puts client.session_cookie
8187
```
8288

8389
If authentication fails, a `Fripa::AuthenticationError` will be raised:
8490

8591
```ruby
8692
begin
87-
authenticator.login!
93+
client.authenticator.login!
8894
rescue Fripa::AuthenticationError => e
8995
puts "Authentication failed: #{e.message}"
9096
end
9197
```
9298

9399
### Making API Calls
94100

95-
Use the `Client` class to interact with the FreeIPA API:
101+
#### Using Resource Methods (Recommended)
102+
103+
The gem provides convenient resource methods with parameter validation:
104+
105+
##### Users
96106

97107
```ruby
98-
client = Fripa::Client.new
108+
client = Fripa::Client.new(username: 'admin', password: 'secret')
109+
110+
# Find users
111+
users = client.users.find # All users
112+
user = client.users.find("admin") # Find specific user
99113

100-
# Find a user (automatically authenticates if needed)
101-
result = client.call("user_find", ["admin"])
102-
puts result.dig("result", "count") # Number of users found
114+
# Show user details
115+
details = client.users.show("admin")
103116

104-
# Add a user with options
105-
client.call("user_add", ["newuser"], {
117+
# Add a user (validates required fields)
118+
client.users.add("newuser",
106119
givenname: "New",
107120
sn: "User",
121+
cn: "New User",
108122
userpassword: "TempPassword123"
109-
})
123+
)
110124

111125
# Modify a user
112-
client.call("user_mod", ["newuser"], {
113-
mail: "newuser@example.com"
114-
})
126+
client.users.mod("newuser", mail: "newuser@example.com")
127+
128+
# Change user password
129+
client.users.passwd("newuser", "NewPassword123", "TempPassword123")
115130

116131
# Delete a user
117-
client.call("user_del", ["newuser"])
132+
client.users.delete("newuser")
133+
```
134+
135+
##### Groups
136+
137+
```ruby
138+
client = Fripa::Client.new(username: 'admin', password: 'secret')
139+
140+
# Find groups
141+
groups = client.groups.find # All groups
142+
group = client.groups.find("admins") # Find specific group
143+
144+
# Show group details
145+
details = client.groups.show("admins")
146+
147+
# Add a group
148+
client.groups.add("developers", description: "Development Team")
149+
150+
# Modify a group
151+
client.groups.mod("developers", description: "Software Development Team")
152+
153+
# Add members to a group
154+
client.groups.add_member("developers", user: ["alice", "bob"])
155+
156+
# Remove members from a group
157+
client.groups.remove_member("developers", user: ["alice"])
158+
159+
# Add member managers (users who can manage group membership)
160+
client.groups.add_member_manager("developers", user: ["manager"])
161+
162+
# Remove member managers
163+
client.groups.remove_member_manager("developers", user: ["manager"])
164+
165+
# Delete a group
166+
client.groups.delete("developers")
118167
```
119168

120-
The client will automatically authenticate using the configured credentials if no session exists.
169+
#### Using Raw API Calls
170+
171+
For API methods without a resource wrapper, use the `call` method directly:
172+
173+
```ruby
174+
client = Fripa::Client.new(username: 'admin', password: 'secret')
175+
176+
# Any FreeIPA JSON-RPC method
177+
result = client.call("user_find", ["admin"], { all: true })
178+
puts result.dig("result", "count")
179+
```
180+
181+
The client will automatically authenticate if no session exists.
121182

122183
## Development
123184

124-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
185+
After checking out the repo, run `bin/setup` to install dependencies.
186+
187+
### Running Tests
188+
189+
To run the test suite with coverage:
190+
191+
```bash
192+
bin/test
193+
```
194+
195+
This will run all tests and generate a coverage report in `coverage/index.html`. The test suite includes:
196+
- Line coverage tracking
197+
- Branch coverage tracking
198+
- VCR cassettes for testing against FreeIPA sandbox
199+
200+
You can also run tests using rake:
201+
202+
```bash
203+
rake test
204+
```
205+
206+
### Other Development Commands
207+
208+
- `bin/console` - Interactive prompt for experimentation
209+
- `bundle exec rake install` - Install gem onto your local machine
210+
- `bundle exec rubocop` - Run code style checks
125211

126-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
212+
To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
127213

128214
## Contributing
129215

Rakefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
require "bundler/gem_tasks"
44
require "minitest/test_task"
55

6-
Minitest::TestTask.create
6+
Minitest::TestTask.create do |t|
7+
t.warning = false
8+
end
79

810
require "rubocop/rake_task"
911

bin/test

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
require "bundler/setup"
5+
6+
# Load SimpleCov first
7+
require "simplecov"
8+
9+
# Now load test helper (which loads the code)
10+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
11+
$LOAD_PATH.unshift File.expand_path("../test", __dir__)
12+
13+
require "test_helper"
14+
15+
# Require all test files
16+
Dir.glob("test/test_*.rb").each { |file| require File.expand_path("../#{file}", __dir__) }
17+
18+
# Run minitest
19+
require "minitest/autorun"

lib/fripa.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
require_relative "fripa/configuration"
55
require_relative "fripa/errors"
66
require_relative "fripa/authenticator"
7+
require_relative "fripa/resources/base"
8+
require_relative "fripa/resources/user"
9+
require_relative "fripa/resources/group"
710
require_relative "fripa/client"
811

912
module Fripa

lib/fripa/authenticator.rb

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ class Authenticator
88
LOGIN_PATH = "/ipa/session/login_password"
99
CONTENT_TYPE = "application/x-www-form-urlencoded"
1010

11-
attr_reader :config
11+
attr_reader :client
1212

13-
def initialize(config = nil)
14-
@config = config || Fripa.config
13+
def initialize(client)
14+
@client = client
1515
end
1616

1717
def login!
@@ -20,43 +20,30 @@ def login!
2020

2121
raise AuthenticationError, "Login failed: #{response.status}" unless response.success?
2222

23-
config.session_cookie = extract_session_cookie(response)
23+
response.headers["set-cookie"]
2424
end
2525

2626
private
2727

2828
def validate_credentials!
29-
raise ArgumentError, "Username cannot be blank" if config.username.to_s.strip.empty?
30-
raise ArgumentError, "Password cannot be blank" if config.password.to_s.strip.empty?
29+
raise ArgumentError, "Username cannot be blank" if client.username.to_s.strip.empty?
30+
raise ArgumentError, "Password cannot be blank" if client.password.to_s.strip.empty?
3131
end
3232

3333
def perform_login
3434
connection.post(LOGIN_PATH) do |request|
3535
request.headers["Content-Type"] = CONTENT_TYPE
36-
request.headers["Referer"] = "#{config.base_url}/ipa"
37-
request.body = URI.encode_www_form(user: config.username, password: config.password)
36+
request.headers["Referer"] = "#{client.config.base_url}/ipa"
37+
request.body = URI.encode_www_form(user: client.username, password: client.password)
3838
end
3939
end
4040

41-
def extract_session_cookie(response)
42-
cookie = response.headers["set-cookie"]
43-
raise AuthenticationError, "No session cookie received" unless cookie
44-
45-
cookie
46-
end
47-
4841
def connection
49-
@connection ||= Faraday.new(url: config.base_url.to_s) do |f|
42+
@connection ||= Faraday.new(url: client.config.base_url.to_s) do |f|
5043
f.request :url_encoded
5144
f.adapter Faraday.default_adapter
52-
f.ssl.verify = config.verify_ssl
45+
f.ssl.verify = client.config.verify_ssl
5346
end
5447
end
5548
end
56-
57-
class << self
58-
def authenticator
59-
@authenticator ||= Authenticator.new
60-
end
61-
end
6249
end

0 commit comments

Comments
 (0)