wsaa-ruby 0.1.0 → 0.2.0
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 +4 -4
- data/AGENTS.md +124 -0
- data/CHANGELOG.md +20 -1
- data/CLAUDE.md +1 -0
- data/lib/wsaa/client.rb +22 -0
- data/lib/wsaa/cms_signer.rb +19 -0
- data/lib/wsaa/configuration.rb +20 -0
- data/lib/wsaa/credential_store.rb +27 -0
- data/lib/wsaa/credentials.rb +26 -0
- data/lib/wsaa/errors.rb +17 -0
- data/lib/wsaa/tra.rb +20 -0
- data/lib/wsaa/version.rb +3 -1
- data/lib/wsaa.rb +44 -0
- metadata +32 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 708f1b5afef609eb4e0dfde437af7905f84db50ac5ae910aa6cb6e0af0253864
|
|
4
|
+
data.tar.gz: 25dba3b78a08508479cdf2a4fb1485011ef1cb5083ee490683f4b06199ee90f9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3c3f0bfa7e1abec8065f1f1bec28a28e51adc9ed0fc61a602927286d3bd74c28c075427d06c4a2b4b3d81a4e82a3de405d6c31ab33b02f9a17d92837090d9294
|
|
7
|
+
data.tar.gz: 73f8b5adeb3313515cc713f0a679798d583e8fb96dc9d5693ec1b870bd6b737588b6763255f212a1f6fd7591df2c7d9ad539a277627ed97741cae73851b402bb
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Writing Convention
|
|
6
|
+
|
|
7
|
+
Use ASD-STE100 (Simplified Technical English) for all communication:
|
|
8
|
+
|
|
9
|
+
- Write short sentences (20 words maximum)
|
|
10
|
+
- Use active voice
|
|
11
|
+
- Use approved vocabulary only
|
|
12
|
+
- Use one meaning per word
|
|
13
|
+
- Write instructions as commands
|
|
14
|
+
|
|
15
|
+
## Coding Conventions
|
|
16
|
+
|
|
17
|
+
### RDoc Documentation
|
|
18
|
+
|
|
19
|
+
Add RDoc comments to all public classes and methods. Follow this format:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
##
|
|
23
|
+
# Short description of the class or method.
|
|
24
|
+
#
|
|
25
|
+
# @param name [Type] Description of the parameter.
|
|
26
|
+
# @return [Type] Description of the return value.
|
|
27
|
+
# @raise [ErrorClass] Description of when this error occurs.
|
|
28
|
+
#
|
|
29
|
+
# @example
|
|
30
|
+
# result = method_name(arg)
|
|
31
|
+
# # => expected output
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Apply this requirement to all new code and modified code.
|
|
35
|
+
|
|
36
|
+
### Git Commits
|
|
37
|
+
|
|
38
|
+
- Make commits atomic: include only one coherent change or fix
|
|
39
|
+
- Do not mix unrelated work in a single commit
|
|
40
|
+
- Write succinct commit messages that describe the change
|
|
41
|
+
- Add both co-authors to each commit message:
|
|
42
|
+
```
|
|
43
|
+
Co-Authored-By: Leandro Marcucci <leanucci@gmail.com>
|
|
44
|
+
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Project Overview
|
|
48
|
+
|
|
49
|
+
wsaa-ruby is a Ruby gem for authenticating with AFIP's WSAA (Web Service de Autenticación y Autorización). It handles the login process to obtain TOKEN and SIGN credentials needed to call other AFIP services like WSFE (electronic invoicing).
|
|
50
|
+
|
|
51
|
+
## Build & Test Commands
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
bundle install # Install dependencies
|
|
55
|
+
bundle exec rspec # Run all RSpec tests
|
|
56
|
+
bundle exec rake # Run default task (specs)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Run a single test file:
|
|
60
|
+
```bash
|
|
61
|
+
bundle exec rspec spec/wsaa/client_spec.rb
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Required Environment
|
|
65
|
+
|
|
66
|
+
- Ruby >= 2.7.0 (see gemspec)
|
|
67
|
+
- Valid AFIP X.509 certificate and private key
|
|
68
|
+
- Certificate must be registered with AFIP for the target service
|
|
69
|
+
|
|
70
|
+
## Architecture
|
|
71
|
+
|
|
72
|
+
### Core Classes
|
|
73
|
+
|
|
74
|
+
- **`Wsaa`** (`lib/wsaa.rb`) - Main module with configuration and authentication entry points
|
|
75
|
+
- **`Wsaa::Client`** (`lib/wsaa/client.rb`) - Orchestrates the authentication flow
|
|
76
|
+
- **`Wsaa::Tra`** (`lib/wsaa/tra.rb`) - Builds the Ticket de Requerimiento de Acceso XML
|
|
77
|
+
- **`Wsaa::CmsSigner`** (`lib/wsaa/cms_signer.rb`) - Signs TRA using OpenSSL PKCS#7
|
|
78
|
+
- **`Wsaa::Credentials`** (`lib/wsaa/credentials.rb`) - Immutable value object for TOKEN/SIGN
|
|
79
|
+
- **`Wsaa::CredentialStore`** (`lib/wsaa/credential_store.rb`) - File-based credential caching
|
|
80
|
+
- **`Wsaa::Configuration`** (`lib/wsaa/configuration.rb`) - Configuration with validation
|
|
81
|
+
- **`Wsaa::Errors`** (`lib/wsaa/errors.rb`) - Custom exceptions
|
|
82
|
+
|
|
83
|
+
### Authentication Flow
|
|
84
|
+
|
|
85
|
+
1. `Wsaa.authenticate` checks for cached credentials in `CredentialStore`
|
|
86
|
+
2. If no valid cache, builds TRA XML with service name and time boundaries
|
|
87
|
+
3. Signs TRA using CMS/PKCS#7 with certificate and private key
|
|
88
|
+
4. Calls WSAA `loginCms` SOAP operation via Savon
|
|
89
|
+
5. Parses response to extract TOKEN and SIGN
|
|
90
|
+
6. Caches credentials for reuse until expiration
|
|
91
|
+
|
|
92
|
+
### SOAP Communication
|
|
93
|
+
|
|
94
|
+
Uses Savon ~> 2.0 for AFIP web services:
|
|
95
|
+
- **Testing (Homologación)**: `https://wsaahomo.afip.gov.ar/ws/services/LoginCms`
|
|
96
|
+
- **Production**: `https://wsaa.afip.gov.ar/ws/services/LoginCms`
|
|
97
|
+
|
|
98
|
+
## Usage Example
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
require 'wsaa'
|
|
102
|
+
|
|
103
|
+
Wsaa.configure do |config|
|
|
104
|
+
config.pkey = 'path/to/private_key'
|
|
105
|
+
config.cert = 'path/to/certificate'
|
|
106
|
+
config.service = 'wsfe'
|
|
107
|
+
config.environment = :testing # or :production
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
credentials = Wsaa.authenticate
|
|
111
|
+
credentials.token # => "PD94bWwg..."
|
|
112
|
+
credentials.sign # => "GGG2XMe..."
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Key Dependencies
|
|
116
|
+
|
|
117
|
+
- **savon** (~> 2.0) - SOAP client
|
|
118
|
+
- **rack** (~> 2.0) - Pinned for httpi compatibility
|
|
119
|
+
- **openssl** - Ruby stdlib, for PKCS#7 signing
|
|
120
|
+
- **rexml** - Ruby stdlib, for XML building
|
|
121
|
+
|
|
122
|
+
## Documentation
|
|
123
|
+
|
|
124
|
+
- [WSAA Technical Specification (PDF)](https://www.afip.gov.ar/ws/WSAA/Especificacion_Tecnica_WSAA_1.2.0.pdf)
|
data/CHANGELOG.md
CHANGED
|
@@ -7,5 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2026-08-18
|
|
11
|
+
|
|
10
12
|
### Added
|
|
11
|
-
-
|
|
13
|
+
- RDoc documentation for all public classes and methods
|
|
14
|
+
- GitHub Actions CI workflow for running tests on PRs
|
|
15
|
+
- GitHub Actions release workflow for publishing to RubyGems
|
|
16
|
+
|
|
17
|
+
## [0.1.0] - 2026-08-18
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- Initial gem structure (2026-07-30)
|
|
21
|
+
- WSAA authentication client with SOAP support
|
|
22
|
+
- TRA (Ticket de Requerimiento de Acceso) XML builder
|
|
23
|
+
- CMS/PKCS#7 signing with OpenSSL
|
|
24
|
+
- Credentials caching with file-based storage
|
|
25
|
+
- Configuration with testing and production endpoints
|
|
26
|
+
- RSpec test suite with 81 examples
|
|
27
|
+
- Agent configuration files (CLAUDE.md, AGENTS.md)
|
|
28
|
+
|
|
29
|
+
### Published
|
|
30
|
+
- Released to RubyGems.org as wsaa-ruby 0.1.0
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
data/lib/wsaa/client.rb
CHANGED
|
@@ -2,13 +2,29 @@ require 'savon'
|
|
|
2
2
|
require 'rexml/document'
|
|
3
3
|
|
|
4
4
|
module Wsaa
|
|
5
|
+
##
|
|
6
|
+
# WSAA authentication client.
|
|
7
|
+
#
|
|
8
|
+
# Orchestrates the authentication flow: builds TRA, signs it, calls WSAA, and caches credentials.
|
|
9
|
+
#
|
|
10
|
+
# @attr_reader configuration [Configuration] The client configuration.
|
|
5
11
|
class Client
|
|
6
12
|
attr_reader :configuration
|
|
7
13
|
|
|
14
|
+
##
|
|
15
|
+
# Creates a new Client.
|
|
16
|
+
#
|
|
17
|
+
# @param configuration [Configuration] The client configuration.
|
|
8
18
|
def initialize(configuration)
|
|
9
19
|
@configuration = configuration
|
|
10
20
|
end
|
|
11
21
|
|
|
22
|
+
##
|
|
23
|
+
# Authenticates with WSAA using cached credentials if available.
|
|
24
|
+
#
|
|
25
|
+
# @return [Credentials] The authentication credentials.
|
|
26
|
+
# @raise [ConfigurationError] If the configuration is invalid.
|
|
27
|
+
# @raise [AuthenticationError] If authentication fails.
|
|
12
28
|
def authenticate
|
|
13
29
|
configuration.validate!
|
|
14
30
|
|
|
@@ -18,6 +34,12 @@ module Wsaa
|
|
|
18
34
|
authenticate!
|
|
19
35
|
end
|
|
20
36
|
|
|
37
|
+
##
|
|
38
|
+
# Authenticates with WSAA, ignoring any cached credentials.
|
|
39
|
+
#
|
|
40
|
+
# @return [Credentials] The authentication credentials.
|
|
41
|
+
# @raise [ConfigurationError] If the configuration is invalid.
|
|
42
|
+
# @raise [AuthenticationError] If authentication fails.
|
|
21
43
|
def authenticate!
|
|
22
44
|
configuration.validate!
|
|
23
45
|
|
data/lib/wsaa/cms_signer.rb
CHANGED
|
@@ -2,14 +2,33 @@ require 'openssl'
|
|
|
2
2
|
require 'base64'
|
|
3
3
|
|
|
4
4
|
module Wsaa
|
|
5
|
+
##
|
|
6
|
+
# Signs data using CMS/PKCS#7 format.
|
|
7
|
+
#
|
|
8
|
+
# Creates cryptographic signatures compatible with WSAA requirements.
|
|
9
|
+
#
|
|
10
|
+
# @attr_reader certificate [OpenSSL::X509::Certificate] The signing certificate.
|
|
11
|
+
# @attr_reader private_key [OpenSSL::PKey::RSA] The private key for signing.
|
|
5
12
|
class CmsSigner
|
|
6
13
|
attr_reader :certificate, :private_key
|
|
7
14
|
|
|
15
|
+
##
|
|
16
|
+
# Creates a new CmsSigner.
|
|
17
|
+
#
|
|
18
|
+
# @param cert_path [String] Path to the certificate file in PEM format.
|
|
19
|
+
# @param pkey_path [String] Path to the private key file in PEM format.
|
|
20
|
+
# @raise [SigningError] If the certificate or key cannot be loaded.
|
|
8
21
|
def initialize(cert_path:, pkey_path:)
|
|
9
22
|
@certificate = load_certificate(cert_path)
|
|
10
23
|
@private_key = load_private_key(pkey_path)
|
|
11
24
|
end
|
|
12
25
|
|
|
26
|
+
##
|
|
27
|
+
# Signs data and returns the base64-encoded CMS signature.
|
|
28
|
+
#
|
|
29
|
+
# @param data [String] The data to sign.
|
|
30
|
+
# @return [String] Base64-encoded PKCS#7/CMS signature.
|
|
31
|
+
# @raise [SigningError] If signing fails.
|
|
13
32
|
def sign(data)
|
|
14
33
|
flags = OpenSSL::PKCS7::BINARY | OpenSSL::PKCS7::NOSMIMECAP
|
|
15
34
|
pkcs7 = OpenSSL::PKCS7.sign(certificate, private_key, data, [], flags)
|
data/lib/wsaa/configuration.rb
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
module Wsaa
|
|
2
|
+
##
|
|
3
|
+
# Holds configuration for the WSAA client.
|
|
4
|
+
#
|
|
5
|
+
# @attr_accessor pkey [String] Path to the private key file.
|
|
6
|
+
# @attr_accessor cert [String] Path to the certificate file.
|
|
7
|
+
# @attr_accessor service [String] The AFIP service to authenticate for.
|
|
8
|
+
# @attr_accessor environment [Symbol] The environment (:testing or :production).
|
|
9
|
+
# @attr_accessor cache_dir [String] Directory for credential caching.
|
|
2
10
|
class Configuration
|
|
3
11
|
ENDPOINTS = {
|
|
4
12
|
testing: 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms',
|
|
@@ -7,18 +15,30 @@ module Wsaa
|
|
|
7
15
|
|
|
8
16
|
attr_accessor :pkey, :cert, :service, :environment, :cache_dir
|
|
9
17
|
|
|
18
|
+
##
|
|
19
|
+
# Creates a new Configuration with default values.
|
|
10
20
|
def initialize
|
|
11
21
|
@environment = :testing
|
|
12
22
|
@service = 'wsfe'
|
|
13
23
|
@cache_dir = '/tmp'
|
|
14
24
|
end
|
|
15
25
|
|
|
26
|
+
##
|
|
27
|
+
# Returns the WSAA endpoint URL for the current environment.
|
|
28
|
+
#
|
|
29
|
+
# @return [String] The endpoint URL.
|
|
30
|
+
# @raise [ConfigurationError] If the environment is invalid.
|
|
16
31
|
def endpoint
|
|
17
32
|
ENDPOINTS.fetch(environment) do
|
|
18
33
|
raise ConfigurationError, "Invalid environment: #{environment}. Must be :testing or :production"
|
|
19
34
|
end
|
|
20
35
|
end
|
|
21
36
|
|
|
37
|
+
##
|
|
38
|
+
# Validates the configuration.
|
|
39
|
+
#
|
|
40
|
+
# @return [true] If the configuration is valid.
|
|
41
|
+
# @raise [ConfigurationError] If required values are missing or files do not exist.
|
|
22
42
|
def validate!
|
|
23
43
|
raise ConfigurationError, "Private key path not configured" if pkey.nil? || pkey.empty?
|
|
24
44
|
raise ConfigurationError, "Certificate path not configured" if cert.nil? || cert.empty?
|
|
@@ -2,14 +2,30 @@ require 'yaml'
|
|
|
2
2
|
require 'time'
|
|
3
3
|
|
|
4
4
|
module Wsaa
|
|
5
|
+
##
|
|
6
|
+
# File-based cache for WSAA credentials.
|
|
7
|
+
#
|
|
8
|
+
# Stores credentials as YAML files with date-based filenames.
|
|
9
|
+
#
|
|
10
|
+
# @attr_reader cache_dir [String] Directory where cache files are stored.
|
|
11
|
+
# @attr_reader service [String] The service name used in the cache filename.
|
|
5
12
|
class CredentialStore
|
|
6
13
|
attr_reader :cache_dir, :service
|
|
7
14
|
|
|
15
|
+
##
|
|
16
|
+
# Creates a new CredentialStore.
|
|
17
|
+
#
|
|
18
|
+
# @param cache_dir [String] Directory for cache files.
|
|
19
|
+
# @param service [String] The service name.
|
|
8
20
|
def initialize(cache_dir:, service:)
|
|
9
21
|
@cache_dir = cache_dir
|
|
10
22
|
@service = service
|
|
11
23
|
end
|
|
12
24
|
|
|
25
|
+
##
|
|
26
|
+
# Reads cached credentials.
|
|
27
|
+
#
|
|
28
|
+
# @return [Credentials, nil] The cached credentials, or nil if not found or expired.
|
|
13
29
|
def read
|
|
14
30
|
return nil unless File.exist?(cache_file_path)
|
|
15
31
|
|
|
@@ -27,15 +43,26 @@ module Wsaa
|
|
|
27
43
|
nil
|
|
28
44
|
end
|
|
29
45
|
|
|
46
|
+
##
|
|
47
|
+
# Writes credentials to the cache.
|
|
48
|
+
#
|
|
49
|
+
# @param credentials [Credentials] The credentials to cache.
|
|
50
|
+
# @return [Credentials] The same credentials object.
|
|
30
51
|
def write(credentials)
|
|
31
52
|
File.write(cache_file_path, YAML.dump(credentials.to_h.transform_keys(&:to_s)))
|
|
32
53
|
credentials
|
|
33
54
|
end
|
|
34
55
|
|
|
56
|
+
##
|
|
57
|
+
# Deletes the cache file.
|
|
35
58
|
def clear
|
|
36
59
|
File.delete(cache_file_path) if File.exist?(cache_file_path)
|
|
37
60
|
end
|
|
38
61
|
|
|
62
|
+
##
|
|
63
|
+
# Returns the full path to the cache file.
|
|
64
|
+
#
|
|
65
|
+
# @return [String] The cache file path.
|
|
39
66
|
def cache_file_path
|
|
40
67
|
File.join(cache_dir, cache_filename)
|
|
41
68
|
end
|
data/lib/wsaa/credentials.rb
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
module Wsaa
|
|
2
|
+
##
|
|
3
|
+
# Immutable value object for WSAA authentication credentials.
|
|
4
|
+
#
|
|
5
|
+
# Holds the TOKEN and SIGN values returned by WSAA after successful authentication.
|
|
6
|
+
#
|
|
7
|
+
# @attr_reader token [String] The authentication token.
|
|
8
|
+
# @attr_reader sign [String] The authentication signature.
|
|
9
|
+
# @attr_reader expiration_time [Time] When the credentials expire.
|
|
2
10
|
class Credentials
|
|
3
11
|
attr_reader :token, :sign, :expiration_time
|
|
4
12
|
|
|
13
|
+
##
|
|
14
|
+
# Creates new Credentials.
|
|
15
|
+
#
|
|
16
|
+
# @param token [String] The authentication token from WSAA.
|
|
17
|
+
# @param sign [String] The authentication signature from WSAA.
|
|
18
|
+
# @param expiration_time [Time] When the credentials expire.
|
|
5
19
|
def initialize(token:, sign:, expiration_time:)
|
|
6
20
|
@token = token.freeze
|
|
7
21
|
@sign = sign.freeze
|
|
@@ -9,14 +23,26 @@ module Wsaa
|
|
|
9
23
|
freeze
|
|
10
24
|
end
|
|
11
25
|
|
|
26
|
+
##
|
|
27
|
+
# Checks if the credentials have expired.
|
|
28
|
+
#
|
|
29
|
+
# @return [Boolean] True if expired.
|
|
12
30
|
def expired?
|
|
13
31
|
Time.now > expiration_time
|
|
14
32
|
end
|
|
15
33
|
|
|
34
|
+
##
|
|
35
|
+
# Checks if the credentials are valid.
|
|
36
|
+
#
|
|
37
|
+
# @return [Boolean] True if not expired and has token and sign.
|
|
16
38
|
def valid?
|
|
17
39
|
!expired? && !token.nil? && !sign.nil?
|
|
18
40
|
end
|
|
19
41
|
|
|
42
|
+
##
|
|
43
|
+
# Converts credentials to a hash.
|
|
44
|
+
#
|
|
45
|
+
# @return [Hash] Hash with :token, :sign, and :expiration_time keys.
|
|
20
46
|
def to_h
|
|
21
47
|
{
|
|
22
48
|
token: token,
|
data/lib/wsaa/errors.rb
CHANGED
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
module Wsaa
|
|
2
|
+
##
|
|
3
|
+
# Base error class for all WSAA errors.
|
|
2
4
|
class Error < StandardError; end
|
|
3
5
|
|
|
6
|
+
##
|
|
7
|
+
# Raised when the configuration is invalid or incomplete.
|
|
4
8
|
class ConfigurationError < Error; end
|
|
5
9
|
|
|
10
|
+
##
|
|
11
|
+
# Raised when CMS/PKCS#7 signing fails.
|
|
6
12
|
class SigningError < Error; end
|
|
7
13
|
|
|
14
|
+
##
|
|
15
|
+
# Raised when WSAA authentication fails.
|
|
16
|
+
#
|
|
17
|
+
# @attr_reader fault_code [String, nil] The SOAP fault code from WSAA.
|
|
18
|
+
# @attr_reader fault_string [String, nil] The SOAP fault message from WSAA.
|
|
8
19
|
class AuthenticationError < Error
|
|
9
20
|
attr_reader :fault_code, :fault_string
|
|
10
21
|
|
|
22
|
+
##
|
|
23
|
+
# Creates a new AuthenticationError.
|
|
24
|
+
#
|
|
25
|
+
# @param message [String] The error message.
|
|
26
|
+
# @param fault_code [String, nil] The SOAP fault code.
|
|
27
|
+
# @param fault_string [String, nil] The SOAP fault message.
|
|
11
28
|
def initialize(message, fault_code: nil, fault_string: nil)
|
|
12
29
|
@fault_code = fault_code
|
|
13
30
|
@fault_string = fault_string
|
data/lib/wsaa/tra.rb
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
require 'rexml/document'
|
|
2
2
|
|
|
3
3
|
module Wsaa
|
|
4
|
+
##
|
|
5
|
+
# Builds the Ticket de Requerimiento de Acceso (TRA) XML document.
|
|
6
|
+
#
|
|
7
|
+
# The TRA is the access request ticket required by WSAA for authentication.
|
|
8
|
+
#
|
|
9
|
+
# @attr_reader service [String] The AFIP service name.
|
|
10
|
+
# @attr_reader generation_time [Time] When the TRA was generated.
|
|
11
|
+
# @attr_reader expiration_time [Time] When the TRA expires.
|
|
12
|
+
# @attr_reader unique_id [Integer] Unique identifier for the request.
|
|
4
13
|
class Tra
|
|
5
14
|
TIMEZONE_OFFSET = '-03:00'
|
|
6
15
|
|
|
7
16
|
attr_reader :service, :generation_time, :expiration_time, :unique_id
|
|
8
17
|
|
|
18
|
+
##
|
|
19
|
+
# Creates a new TRA.
|
|
20
|
+
#
|
|
21
|
+
# @param service [String] The AFIP service to authenticate for.
|
|
22
|
+
# @param generation_time [Time, nil] Start of validity period. Defaults to today 00:00:00.
|
|
23
|
+
# @param expiration_time [Time, nil] End of validity period. Defaults to today 23:59:59.
|
|
24
|
+
# @param unique_id [Integer, nil] Unique request identifier. Defaults to current timestamp.
|
|
9
25
|
def initialize(service:, generation_time: nil, expiration_time: nil, unique_id: nil)
|
|
10
26
|
@service = service
|
|
11
27
|
@unique_id = unique_id || Time.now.to_i
|
|
@@ -13,6 +29,10 @@ module Wsaa
|
|
|
13
29
|
@expiration_time = expiration_time || default_expiration_time
|
|
14
30
|
end
|
|
15
31
|
|
|
32
|
+
##
|
|
33
|
+
# Converts the TRA to an XML string.
|
|
34
|
+
#
|
|
35
|
+
# @return [String] The TRA as XML.
|
|
16
36
|
def to_xml
|
|
17
37
|
doc = REXML::Document.new
|
|
18
38
|
doc << REXML::XMLDecl.new('1.0', 'UTF-8')
|
data/lib/wsaa/version.rb
CHANGED
data/lib/wsaa.rb
CHANGED
|
@@ -7,24 +7,68 @@ require_relative 'wsaa/credentials'
|
|
|
7
7
|
require_relative 'wsaa/credential_store'
|
|
8
8
|
require_relative 'wsaa/client'
|
|
9
9
|
|
|
10
|
+
##
|
|
11
|
+
# Main module for WSAA authentication.
|
|
12
|
+
#
|
|
13
|
+
# Provides a simple interface to authenticate with AFIP's WSAA service.
|
|
14
|
+
#
|
|
15
|
+
# @example Configure and authenticate
|
|
16
|
+
# Wsaa.configure do |config|
|
|
17
|
+
# config.pkey = 'path/to/private_key'
|
|
18
|
+
# config.cert = 'path/to/certificate'
|
|
19
|
+
# config.service = 'wsfe'
|
|
20
|
+
# config.environment = :testing
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# credentials = Wsaa.authenticate
|
|
24
|
+
# credentials.token # => "PD94bWwg..."
|
|
25
|
+
# credentials.sign # => "GGG2XMe..."
|
|
10
26
|
module Wsaa
|
|
11
27
|
class << self
|
|
28
|
+
##
|
|
29
|
+
# Configures the WSAA client.
|
|
30
|
+
#
|
|
31
|
+
# @yield [Configuration] The configuration object.
|
|
32
|
+
#
|
|
33
|
+
# @example
|
|
34
|
+
# Wsaa.configure do |config|
|
|
35
|
+
# config.pkey = 'path/to/key'
|
|
36
|
+
# config.cert = 'path/to/cert'
|
|
37
|
+
# end
|
|
12
38
|
def configure
|
|
13
39
|
yield(configuration)
|
|
14
40
|
end
|
|
15
41
|
|
|
42
|
+
##
|
|
43
|
+
# Returns the current configuration.
|
|
44
|
+
#
|
|
45
|
+
# @return [Configuration] The configuration object.
|
|
16
46
|
def configuration
|
|
17
47
|
@configuration ||= Configuration.new
|
|
18
48
|
end
|
|
19
49
|
|
|
50
|
+
##
|
|
51
|
+
# Authenticates with WSAA using cached credentials if available.
|
|
52
|
+
#
|
|
53
|
+
# @return [Credentials] The authentication credentials.
|
|
54
|
+
# @raise [ConfigurationError] If the configuration is invalid.
|
|
55
|
+
# @raise [AuthenticationError] If authentication fails.
|
|
20
56
|
def authenticate
|
|
21
57
|
client.authenticate
|
|
22
58
|
end
|
|
23
59
|
|
|
60
|
+
##
|
|
61
|
+
# Authenticates with WSAA, ignoring any cached credentials.
|
|
62
|
+
#
|
|
63
|
+
# @return [Credentials] The authentication credentials.
|
|
64
|
+
# @raise [ConfigurationError] If the configuration is invalid.
|
|
65
|
+
# @raise [AuthenticationError] If authentication fails.
|
|
24
66
|
def authenticate!
|
|
25
67
|
client.authenticate!
|
|
26
68
|
end
|
|
27
69
|
|
|
70
|
+
##
|
|
71
|
+
# Resets the configuration and client state.
|
|
28
72
|
def reset!
|
|
29
73
|
@configuration = nil
|
|
30
74
|
@client = nil
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wsaa-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Leandro Marcucci
|
|
@@ -66,6 +66,34 @@ dependencies:
|
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '3.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: vcr
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '6.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '6.0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: webmock
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '3.0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '3.0'
|
|
69
97
|
description: Ruby implementation of AFIP WSAA (Web Service de Autenticación y Autorización)
|
|
70
98
|
for authenticating with Argentine tax authority web services
|
|
71
99
|
email:
|
|
@@ -74,7 +102,9 @@ executables: []
|
|
|
74
102
|
extensions: []
|
|
75
103
|
extra_rdoc_files: []
|
|
76
104
|
files:
|
|
105
|
+
- AGENTS.md
|
|
77
106
|
- CHANGELOG.md
|
|
107
|
+
- CLAUDE.md
|
|
78
108
|
- Gemfile
|
|
79
109
|
- LICENSE
|
|
80
110
|
- README.md
|
|
@@ -110,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
110
140
|
- !ruby/object:Gem::Version
|
|
111
141
|
version: '0'
|
|
112
142
|
requirements: []
|
|
113
|
-
rubygems_version: 3.
|
|
143
|
+
rubygems_version: 3.4.19
|
|
114
144
|
signing_key:
|
|
115
145
|
specification_version: 4
|
|
116
146
|
summary: Ruby client for AFIP WSAA authentication service
|