Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add helper methods, basic tests, usage examples, and rubocop linting … #42

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
23 changes: 21 additions & 2 deletions .github/workflows/ruby-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,32 @@ on:
- master

jobs:
ruby-ci-lint:
name: Ruby CI - lint
runs-on: ubuntu-latest

strategy:
matrix:
ruby-version: [3.1]

steps:
- uses: actions/checkout@v4
- name: Set up Ruby ${{ matrix.ruby-version }}
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
- name: Install dependencies
run: bundle install
- name: Lint
run: rake rubocop

ruby-ci:
name: Ruby CI - test
runs-on: ubuntu-latest

strategy:
matrix:
ruby-version: [3.0, 3.1, 3.2]
ruby-version: [3.0, 3.1, 3.2, 3.3, 3.4]

steps:
- uses: actions/checkout@v4
Expand All @@ -25,4 +44,4 @@ jobs:
- name: Install dependencies
run: bundle install
- name: Test
run: rake
run: rake test
74 changes: 74 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
##
# AllCops (Global)
#
AllCops:
NewCops: enable
SuggestExtensions: false
Exclude:
- vendor/**/*

##
# Layout
#
Layout/FirstHashElementIndentation:
EnforcedStyle: consistent

Layout/HashAlignment:
EnforcedLastArgumentHashStyle: ignore_implicit

Layout/SpaceBeforeBlockBraces:
EnforcedStyle: no_space

##
# Metrics
#
Metrics/AbcSize:
Max: 20
CountRepeatedAttributes: false
Exclude:
- test/**/*
AllowedMethods:
- request
- get_all

Metrics/BlockLength:
Enabled: false

Metrics/ClassLength:
Enabled: false

Metrics/CyclomaticComplexity:
AllowedMethods:
- get_all

Metrics/MethodLength:
Enabled: false

Metrics/ParameterLists:
Max: 6
CountKeywordArgs: false

Metrics/PerceivedComplexity:
Enabled: false

##
# Naming
#
Naming/AccessorMethodName:
Exclude:
- lib/duo_api/*

##
# Style
#
Style/NumericLiterals:
Enabled: false

##
# Gemspec
#
Gemspec/DevelopmentDependencies:
EnforcedStyle: gemspec

Gemspec/RequireMFA:
Enabled: false
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

source 'https://rubygems.org'

gemspec
46 changes: 28 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,22 @@

**Accounts** - https://www.duosecurity.com/docs/accountsapi

## Tested Against Ruby Versions:
* 3.0
# Compatibility
While the gem should work for Ruby versions >= 2.5, tests and linting may only work properly on Ruby versions >= 3.0.

Tests are only run on currently supported Ruby versions.

### Tested Against Ruby Versions:
* 3.1
* 3.2
* 3.3
* 3.4

## TLS 1.2 and 1.3 Support
### TLS 1.2 and 1.3 Support

Duo_api_ruby uses the Ruby openssl extension for TLS operations.
duo_api_ruby uses the Ruby openssl extension for TLS operations.

All currently supported Ruby versions (2.7 and higher) support TLS 1.2 and 1.3.
All Ruby versions compatible with this gem (2.5 and higher) support TLS 1.2 and 1.3.

# Installing

Expand All @@ -45,27 +51,31 @@ gem 'duo_api', '~> 1.0'
```

# Using

TODO
- Examples of doing things [the hard way](/examples/the_hard_way.md)
- Examples of doing things [the less hard way](/examples/the_less_hard_way.md)
- Examples of doing things [the simple way](/examples/the_simple_way.md)

# Testing

###### (Testing and Linting can be done simultaneously by running `rake` without specifying a task)
```
$ rake
$ rake test
Loaded suite /usr/lib/ruby/vendor_ruby/rake/rake_test_loader
Started
........

Finished in 0.002024715 seconds.
--------------------------------------------------------------------------------------------------------
8 tests, 10 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
Finished in 0.123992 seconds.
----------------------------------------------------------------------------------------------------------------------------
368 tests, 1098 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
--------------------------------------------------------------------------------------------------------
3951.17 tests/s, 4938.97 assertions/s
----------------------------------------------------------------------------------------------------------------------------
2967.93 tests/s, 8855.41 assertions/s
```

# Linting

###### (Testing and Linting can be done simultaneously by running `rake` without specifying a task)
```
$ rubocop
$ rake rubocop
Running RuboCop...
Inspecting 15 files
...............

15 files inspected, no offenses detected
```
17 changes: 13 additions & 4 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
# frozen_string_literal: true

require 'rake/testtask'
require 'rubocop/rake_task'

Rake::TestTask.new do |t|
t.libs << 'test'
end
task default: %i[test rubocop]

desc 'Run tests'
task :default => :test
task :test do
Rake::TestTask.new{ |t| t.libs << 'test' }
end

desc 'Run rubocop'
task lint: %i[rubocop]
task :rubocop do
RuboCop::RakeTask.new
end
24 changes: 17 additions & 7 deletions duo_api.gemspec
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
# frozen_string_literal: true

Gem::Specification.new do |s|
s.name = 'duo_api'
s.version = '1.4.0'
s.version = '1.5.0'
s.summary = 'Duo API Ruby'
s.description = 'A Ruby implementation of the Duo API.'
s.email = '[email protected]'
s.homepage = 'https://github.com/duosecurity/duo_api_ruby'
s.license = 'BSD-3-Clause'
s.authors = ['Duo Security']
s.files = [
'ca_certs.pem',
'lib/duo_api.rb',
'ca_certs.pem'
'lib/duo_api/accounts.rb',
'lib/duo_api/admin.rb',
'lib/duo_api/auth.rb',
'lib/duo_api/client.rb',
'lib/duo_api/device.rb',
'lib/duo_api/helpers.rb'
]
s.add_development_dependency 'rake', '~> 12.0'
s.add_development_dependency 'rubocop', '~> 0.49.0'
s.add_development_dependency 'test-unit', '~> 3.2'
s.add_development_dependency 'mocha', '~> 1.8.0'
s.add_development_dependency 'ostruct', '~> 0.1.0'
s.required_ruby_version = '>= 2.5'
s.add_dependency 'base64', '~> 0.2.0'
s.add_development_dependency 'mocha', '~> 2.7.1'
s.add_development_dependency 'ostruct', '~> 0.6.1'
s.add_development_dependency 'rake', '~> 13.2.1'
s.add_development_dependency 'rubocop', '~> 1.73.1'
s.add_development_dependency 'test-unit', '~> 3.6.7'
end
39 changes: 0 additions & 39 deletions examples.rb

This file was deleted.

43 changes: 43 additions & 0 deletions examples/the_hard_way.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Doing Things The Hard Way

### Making requests using `request()`
###### - This method returns a raw `Net::HTTPResponse` object, which gives you more control at the expense of simplicity
```
require 'duo_api'

# Initialize the api
client = DuoApi.new(IKEY, SKEY, HOST)

# EXAMPLE 1: Get the first 100 users
resp = client.request('GET', '/admin/v1/users', { limit: '100', offset: '0' })
# print out some info from the response
puts resp.code # Response status code
puts resp.to_hash # Response headers hash
puts resp.message # Response message
puts resp.http_version # Response HTTP version
puts resp.body # Response body

# EXAMPLE 2: retreive the user 'john'
resp2 = client.request('GET', '/admin/v1/users', { username: 'john' })
puts resp2.body

# EXAMPLE 3: create a new user
resp3 = client.request('POST', '/admin/v1/users', { username: 'john2' })
puts resp3.body

# EXAMPLE 4: delete user with user_id: 'DUAE0W526W52YHOBMDO6'
resp4 = client.request('DELETE', '/admin/v1/users/DUAE0W526W52YHOBMDO6')
puts resp4.body

# EXAMPLE 5: Authlog V2. Pagination with next_offset.
resp5 = client.request(
'GET', '/admin/v2/logs/authentication',
{ limit: '1', mintime: '1546371049194', maxtime: '1548963049000' })
puts resp5.body
resp5_body = JSON.parse(resp5.body, symbolize_names: true)
resp6 = client.request(
'GET', '/admin/v2/logs/authentication',
{ limit: '1', mintime: '1546371049194', maxtime: '1548963049000',
next_offset: resp5_body[:response][:metadata][:next_offset].join(',') })
puts resp6.body
```
45 changes: 45 additions & 0 deletions examples/the_less_hard_way.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Doing Things The Less Hard Way

### Making requests using `get()`, `post()`, `put()`, and `delete()`
###### - These methods return a Hash (with symbol keys) of the parsed JSON response body
```
require 'duo_api'

# Initialize the api
client = DuoApi.new(IKEY, SKEY, HOST)

# EXAMPLE 1: Get single user by username
user = client.get('/admin/v1/users', { username: 'john' })[:response]

# EXAMPLE 2: Create new user
new_user = client.post('/admin/v1/users', { username: 'john2' })[:response]

TODO: MORE EXAMPLES HERE
```

### Making requests using `get_all()`
###### - This method handles paginated responses automatically and returns a Hash (with symbol keys) of the combined parsed JSON response bodies
```
require 'duo_api'

# Initialize the api
client = DuoApi.new(IKEY, SKEY, HOST)

# EXAMPLE 1: Get all users
users = client.get_all('/admin/v1/users')[:response]

TODO: MORE EXAMPLES HERE
```

### Making requests using `get_image()`
###### - This method expects an image content-type and returns the raw response body
```
require 'duo_api'

# Initialize the api
client = DuoApi.new(IKEY, SKEY, HOST)

# EXAMPLE 1: Download logo from Admin API and write to disk
image_data = client.get_image('/admin/v1/logo')
File.write('logo.png', image_data)
```
Loading