smile-identity-core 2.2.5 → 2.3.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2afcbbd6545c88d03ff0b594301cecbadde5254f80596f1ea0eb0618b503675
4
- data.tar.gz: f61285ea5a4a1bd2100c9a7595df42f99e6d45a83bb937669fdb7850b08c2742
3
+ metadata.gz: 39966ea73f1a7ad317cf4bb110622c9b5c67efcf5246487b2a7a4124d418a8f6
4
+ data.tar.gz: cc969bff36cdb9a725e4776d0879b8159cb88b22f2e7d316a9b43848c7ddc388
5
5
  SHA512:
6
- metadata.gz: 5c907e4c87b263502515cdf0481a74efa8610017d3205b3e5fcd026521ad511c2b6831b23d24485aa6fad62388cd5a207fe77b9530c77ecc0f8d75b33160e5bb
7
- data.tar.gz: 696a8eb8bb44d8cb2aec5f97b4f5ecf51789f1310d85e62bdb0f99542d3242fd78980b21740871af592bf7e8616aeafa0c3f6daa40550607f22404a16342de23
6
+ metadata.gz: db43ee16f48ec3f8c87069f21aac2b5ba1cc509abfa54531efd4695a9d56c98beeb3d78b45ec1841e386d48c4847d116d6464545fca5bf4fbd33fafc5e508242
7
+ data.tar.gz: 8827bbbf277fbb9687aae8cdbf567de94e1f3deeeea9d9913a8088c4a0d47a0399d5c02a80d2acc9a218f0a577122a22e397457dbebb9075f55ebc593d07b8cc
@@ -10,7 +10,7 @@ jobs:
10
10
  needs: test
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
- - uses: actions/checkout@v4
13
+ - uses: actions/checkout@v6
14
14
  with:
15
15
  set-safe-directory: false
16
16
  - name: Release Gem
@@ -0,0 +1,119 @@
1
+ name: Semgrep SAST
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+
8
+ permissions:
9
+ contents: read
10
+ actions: read
11
+ security-events: write
12
+ pull-requests: write
13
+
14
+ jobs:
15
+ semgrep:
16
+ name: Semgrep Security Scan
17
+ runs-on: ubuntu-latest
18
+ timeout-minutes: 15
19
+ container:
20
+ image: semgrep/semgrep:1.157.0
21
+ steps:
22
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
23
+
24
+ - name: Run Semgrep
25
+ continue-on-error: true
26
+ run: semgrep scan --config p/security-audit --sarif -o semgrep.sarif
27
+
28
+ - name: Upload SARIF to GitHub Security
29
+ if: always()
30
+ continue-on-error: true
31
+ uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4
32
+ with:
33
+ sarif_file: semgrep.sarif
34
+
35
+ - name: Comment findings on PR
36
+ if: github.event_name == 'pull_request' && always()
37
+ env:
38
+ GH_TOKEN: ${{ github.token }}
39
+ PR_NUMBER: ${{ github.event.pull_request.number }}
40
+ run: |
41
+ python3 << 'PYEOF'
42
+ import json, os, urllib.request
43
+
44
+ marker = "<!-- semgrep-results -->"
45
+ repo = os.environ.get("GITHUB_REPOSITORY", "")
46
+ pr = os.environ.get("PR_NUMBER", "")
47
+ token = os.environ.get("GH_TOKEN", "")
48
+ api = f"https://api.github.com/repos/{repo}/issues/{pr}/comments"
49
+ headers = {
50
+ "Authorization": f"Bearer {token}",
51
+ "Accept": "application/vnd.github+json",
52
+ "X-GitHub-Api-Version": "2022-11-28",
53
+ }
54
+
55
+ findings = []
56
+ try:
57
+ with open("semgrep.sarif") as f:
58
+ sarif = json.load(f)
59
+ for run in sarif.get("runs", []):
60
+ for result in run.get("results", []):
61
+ rule_id = result.get("ruleId", "unknown")
62
+ message = result.get("message", {}).get("text", "")
63
+ level = result.get("level", "warning")
64
+ for loc in result.get("locations", []):
65
+ phys = loc.get("physicalLocation", {})
66
+ path = phys.get("artifactLocation", {}).get("uri", "?")
67
+ line = phys.get("region", {}).get("startLine", "?")
68
+ findings.append({"rule": rule_id, "message": message,
69
+ "level": level, "file": path, "line": line})
70
+ except (FileNotFoundError, json.JSONDecodeError):
71
+ pass
72
+
73
+ if findings:
74
+ body = f"{marker}\n## 🔍 Semgrep Security Scan Results\n\n"
75
+ body += f"**{len(findings)} finding(s)** detected by "
76
+ body += "[`p/security-audit`](https://semgrep.dev/p/security-audit) ruleset.\n\n"
77
+ body += "| # | Severity | Rule | File | Line | Message |\n"
78
+ body += "|---|----------|------|------|------|---------|\n"
79
+ for i, f in enumerate(findings[:25], 1):
80
+ rule = f["rule"].split(".")[-1] if "." in f["rule"] else f["rule"]
81
+ msg = f["message"].replace("|", "\\|").replace("\n", " ")
82
+ msg = (msg[:80] + "...") if len(msg) > 80 else msg
83
+ body += f'| {i} | {f["level"].upper()} | `{rule}` '
84
+ body += f'| `{f["file"]}` | {f["line"]} | {msg} |\n'
85
+ if len(findings) > 25:
86
+ body += f"\n*... and {len(findings) - 25} more. "
87
+ body += "See full results in workflow logs.*\n"
88
+ body += "\n> ⚠️ These findings are **non-blocking**. "
89
+ body += "Please review and address as appropriate.\n"
90
+ else:
91
+ body = f"{marker}\n## 🔍 Semgrep Security Scan Results\n\n"
92
+ body += "✅ No security findings detected by "
93
+ body += "[`p/security-audit`](https://semgrep.dev/p/security-audit) ruleset.\n"
94
+
95
+ existing_id = None
96
+ try:
97
+ req = urllib.request.Request(api, headers=headers)
98
+ resp = urllib.request.urlopen(req)
99
+ for c in json.loads(resp.read()):
100
+ if marker in c.get("body", ""):
101
+ existing_id = c["id"]
102
+ break
103
+ except Exception:
104
+ pass
105
+
106
+ data = json.dumps({"body": body}).encode()
107
+ if existing_id:
108
+ url = f"https://api.github.com/repos/{repo}/issues/comments/{existing_id}"
109
+ req = urllib.request.Request(url, data=data, headers=headers, method="PATCH")
110
+ else:
111
+ req = urllib.request.Request(api, data=data, headers=headers, method="POST")
112
+
113
+ try:
114
+ urllib.request.urlopen(req)
115
+ action = "Updated" if existing_id else "Posted"
116
+ print(f"{action} comment with {len(findings)} finding(s)")
117
+ except Exception as e:
118
+ print(f"Warning: Could not comment on PR: {e}")
119
+ PYEOF
@@ -16,7 +16,7 @@ jobs:
16
16
  # See https://www.ruby-lang.org/en/downloads/ for latest stable releases.
17
17
  ruby: ['2.6', '2.7', '3.0', '3.1', '3.2']
18
18
  steps:
19
- - uses: actions/checkout@v4
19
+ - uses: actions/checkout@v6
20
20
  - uses: ruby/setup-ruby@v1
21
21
  with:
22
22
  ruby-version: ${{ matrix.ruby }}
@@ -25,7 +25,7 @@ jobs:
25
25
  lint:
26
26
  runs-on: ubuntu-latest
27
27
  steps:
28
- - uses: actions/checkout@v4
28
+ - uses: actions/checkout@v6
29
29
  - uses: ruby/setup-ruby@v1
30
30
  with:
31
31
  ruby-version: 3.2
data/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
+ ## [2.3.1] - 2026-04-24
9
+ ### Added
10
+ - Support for optional `aliases` parameter in AML Check (`AmlCheck#submit_job`) to allow secondary names in screening requests
11
+
12
+ ## [2.3.0] - 2024-12-10
13
+ ### Added
14
+ - Support for Address verification
15
+
8
16
  ## [2.2.5] - 2024-06-11
9
17
  ### Fixed
10
18
  - No changes, fixed deployment issue
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- smile-identity-core (2.2.5)
4
+ smile-identity-core (2.3.1)
5
5
  rubyzip (~> 1.2, >= 1.2.3)
6
6
  typhoeus (~> 1.0, >= 1.0.1)
7
7
 
@@ -21,8 +21,7 @@ GEM
21
21
  rainbow (3.1.1)
22
22
  rake (12.3.3)
23
23
  regexp_parser (2.6.0)
24
- rexml (3.2.8)
25
- strscan (>= 3.0.9)
24
+ rexml (3.4.2)
26
25
  rspec (3.8.0)
27
26
  rspec-core (~> 3.8.0)
28
27
  rspec-expectations (~> 3.8.0)
@@ -60,7 +59,6 @@ GEM
60
59
  simplecov_json_formatter (~> 0.1)
61
60
  simplecov-html (0.12.3)
62
61
  simplecov_json_formatter (0.1.4)
63
- strscan (3.1.0)
64
62
  typhoeus (1.4.0)
65
63
  ethon (>= 0.9.0)
66
64
  unicode-display_width (2.3.0)
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'smile-identity-core'
4
+ # See https://docs.usesmileid.com/products/for-individuals-kyc/address-verification for
5
+ # more information on address verification
6
+
7
+ # Initialize
8
+ partner_id = '' # login to the Smile Identity portal to view your partner id
9
+ api_key = '' # copy your API key from the Smile Identity portal
10
+ sid_server = '0' # Use 0 for the sandbox server, use 1 for production server
11
+
12
+ # Array of example cases for different countries
13
+ example_cases = [
14
+ {
15
+ description: 'Example for South Africa (ZA)',
16
+ request_params: {
17
+ country: 'ZA', # (Required) Must be 'NG' or 'ZA'.
18
+ address: 'Cape Town', # (Required) A valid and complete address.
19
+ id_number: '1234567891234', # (Required for ZA) The 13-digit national ID number.
20
+ full_name: 'Doe Joe Leo', # (Optional) Full name for additional verification.
21
+ callback_url: 'https://webhook.site', # (Required) Callback URL for the response.
22
+ },
23
+ },
24
+ {
25
+ description: 'Example for Nigeria (NG)',
26
+ request_params: {
27
+ country: 'NG', # (Required) Must be 'NG' or 'ZA'.
28
+ address: 'Lagos', # (Required) A valid and complete address.
29
+ utility_number: '12345678911', # (Required for NG) Utility account number.
30
+ utility_provider: 'IkejaElectric', # (Required for NG) Utility provider name.
31
+ full_name: 'John Doe', # (Optional) Full name for additional verification.
32
+ callback_url: 'https://webhook.site', # (Required) Callback URL for the response.
33
+ },
34
+ },
35
+ ]
36
+
37
+ # Create a connection object
38
+ connection = SmileIdentityCore::AddressVerification.new(partner_id, api_key, sid_server)
39
+
40
+ # Loop through the example cases and make requests
41
+ example_cases.each do |example|
42
+ puts example[:description]
43
+ response = connection.submit_job(example[:request_params])
44
+ pp response
45
+ end
@@ -20,6 +20,7 @@ request_params = {
20
20
  birth_year: '1984', # yyyy
21
21
  search_existing_user: false,
22
22
  strict_match: true, # optional - default is true
23
+ # aliases: ['Johnny Doe', 'J. Doe'], # optional - secondary names to broaden screening
23
24
  }
24
25
 
25
26
  # Submit the job
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'typhoeus'
5
+ require_relative 'validations'
6
+
7
+ module SmileIdentityCore
8
+ ##
9
+ # The Address Verification Service allows you to verify address details provided by a user,
10
+ # by comparing the address details provided by the user to the address details on file with the authorities database.
11
+ # For more info visit https://docs.usesmileid.com/
12
+ class AddressVerification
13
+ include Validations
14
+
15
+ ###
16
+ # Initialize Address Verification
17
+ # @param [String] :partner_id A unique number assigned by Smile ID to your account. Can be found in the portal
18
+ # @param [String] :api_key Your API key from the Smile Identity portal
19
+ # @param [String] :sid_server Use 0 for the sandbox server, use 1 for production server
20
+ def initialize(partner_id, api_key, sid_server)
21
+ @api_key = api_key
22
+ @partner_id = partner_id.to_s
23
+ @sid_server = sid_server
24
+ @url = SmileIdentityCore::ENV.determine_url(sid_server)
25
+ end
26
+
27
+ ###
28
+ # Submit Address Verification
29
+ # @param [Hash] params the options to create a job with.
30
+ # @option params [String] :country (required) The user's country (e.g., NG or ZA).
31
+ # @option params [String] :address (required) The user's address.
32
+ # @option params [String] :utility_number (required for NG) The utility account number.
33
+ # @option params [String] :utility_provider (required for NG) The utility provider.
34
+ # @option params [String] :id_number (required for ZA) The national ID number.
35
+ # @option params [String] :full_name (optional) The user's full name.
36
+ # @option params [String] :callback_url (required) The callback URL.
37
+ def submit_job(params)
38
+ @params = symbolize_keys(params)
39
+ submit_requests
40
+ end
41
+
42
+ private
43
+
44
+ def symbolize_keys(params)
45
+ params.is_a?(Hash) ? params.transform_keys(&:to_sym) : params
46
+ end
47
+
48
+ def construct_and_validate_headers
49
+ signature = SmileIdentityCore::Signature.new(@partner_id, @api_key).generate_iso_timestamp_signature
50
+ {
51
+ 'smileid-source-sdk' => SmileIdentityCore::SOURCE_SDK,
52
+ 'smileid-source-sdk-version' => SmileIdentityCore::VERSION,
53
+ 'smileid-request-signature' => signature[:signature],
54
+ 'smileid-timestamp' => signature[:timestamp],
55
+ 'smileid-partner-id' => @partner_id,
56
+ }
57
+ end
58
+
59
+ def submit_requests
60
+ # Construct headers and body
61
+ headers = construct_and_validate_headers
62
+ body = @params.to_json
63
+
64
+ # Create and run the request
65
+ request = Typhoeus::Request.new("#{@url}/async-verify-address", method: 'POST',
66
+ headers: headers,
67
+ body: body)
68
+
69
+ # Handle the response
70
+ request.on_complete do |response|
71
+ raise " #{response.code}: #{response.body}" unless response.success?
72
+
73
+ return { success: true }.to_json
74
+ end
75
+
76
+ request.run
77
+ end
78
+ end
79
+ end
@@ -35,6 +35,8 @@ module SmileIdentityCore
35
35
  # @option opts [boolean] :search_existing_user If you intend to re-use the name and year of birth
36
36
  # @option opts [boolean] :strict_match If you want to perform a strict match on the serach criteria.
37
37
  # of a user’s previous KYC job
38
+ # @option opts [Array<String>] :aliases An optional list of secondary or alternative names
39
+ # (e.g. maiden names, transliterations, nicknames) to include in the screening search.
38
40
  # @option opts [Hash] :optional_info Any optional data, this will be returned
39
41
  # in partner_params.
40
42
  def submit_job(params)
@@ -29,5 +29,9 @@ module SmileIdentityCore
29
29
  # with an ID authority, and uses biometric checks to confirm they
30
30
  # belong to the user.
31
31
  ENHANCED_DOCUMENT_VERIFICATION = 11
32
+ # Verifies the authenticity of National IDs or Utility Numbers and confirms
33
+ # their validity with an ID authority, and retrieves user address information
34
+ # from the ID authority.
35
+ ADDRESS_VERIFICATION = 12
32
36
  end
33
37
  end
@@ -12,6 +12,25 @@ module SmileIdentityCore
12
12
  #
13
13
  # @return [Hash] containing both the signature and related timestamp
14
14
  def generate_signature(timestamp = Time.now.to_s)
15
+ get_signature(timestamp)
16
+ end
17
+
18
+ def generate_iso_timestamp_signature(timestamp = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%S.%LZ'))
19
+ get_signature(timestamp)
20
+ end
21
+
22
+ # Confirms the signature against a newly generated signature based on the same timestamp
23
+ #
24
+ # @param [String] timestamp the timestamp to generate the signature from
25
+ # @param [String] msg_signature a previously generated signature, to be confirmed
26
+ # @return [Boolean] TRUE or FALSE
27
+ def confirm_signature(timestamp, msg_signature)
28
+ get_signature(timestamp)[:signature] == msg_signature
29
+ end
30
+
31
+ private
32
+
33
+ def get_signature(timestamp)
15
34
  hmac = OpenSSL::HMAC.new(@api_key, 'sha256')
16
35
  hmac.update(timestamp.to_s)
17
36
  hmac.update(@partner_id)
@@ -22,14 +41,5 @@ module SmileIdentityCore
22
41
  timestamp: timestamp.to_s,
23
42
  }
24
43
  end
25
-
26
- # Confirms the signature against a newly generated signature based on the same timestamp
27
- #
28
- # @param [String] timestamp the timestamp to generate the signature from
29
- # @param [String] msg_signature a previously generated signature, to be confirmed
30
- # @return [Boolean] TRUE or FALSE
31
- def confirm_signature(timestamp, msg_signature)
32
- generate_signature(timestamp)[:signature] == msg_signature
33
- end
34
44
  end
35
45
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SmileIdentityCore
4
- VERSION = '2.2.5'
4
+ VERSION = '2.3.1'
5
5
  SOURCE_SDK = 'Ruby'
6
6
  end
@@ -7,6 +7,7 @@ require_relative 'smile-identity-core/aml_check'
7
7
  require_relative 'smile-identity-core/signature'
8
8
  require_relative 'smile-identity-core/utilities'
9
9
  require_relative 'smile-identity-core/business_verification'
10
+ require_relative 'smile-identity-core/address_verification'
10
11
  require_relative 'smile-identity-core/constants/env'
11
12
  require_relative 'smile-identity-core/constants/image_type'
12
13
  require_relative 'smile-identity-core/constants/job_type'
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smile-identity-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.5
4
+ version: 2.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Smile Identity
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-06-12 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bundler
@@ -157,6 +156,7 @@ extra_rdoc_files: []
157
156
  files:
158
157
  - ".github/dependabot.yml"
159
158
  - ".github/workflows/release.yml"
159
+ - ".github/workflows/semgrep.yml"
160
160
  - ".github/workflows/test.yml"
161
161
  - ".gitignore"
162
162
  - ".rspec"
@@ -169,6 +169,7 @@ files:
169
169
  - Rakefile
170
170
  - bin/console
171
171
  - bin/setup
172
+ - examples/address_verification.rb
172
173
  - examples/aml_check.rb
173
174
  - examples/biometric_kyc.rb
174
175
  - examples/business_verification.rb
@@ -183,6 +184,7 @@ files:
183
184
  - examples/get_web_token.rb
184
185
  - examples/smart_selfie_authentication.rb
185
186
  - lib/smile-identity-core.rb
187
+ - lib/smile-identity-core/address_verification.rb
186
188
  - lib/smile-identity-core/aml_check.rb
187
189
  - lib/smile-identity-core/business_verification.rb
188
190
  - lib/smile-identity-core/constants/env.rb
@@ -204,7 +206,6 @@ metadata:
204
206
  documentation_uri: https://docs.usesmileid.com
205
207
  changelog_uri: https://github.com/smileidentity/smile-identity-core-ruby/blob/master/CHANGELOG.md
206
208
  rubygems_mfa_required: 'true'
207
- post_install_message:
208
209
  rdoc_options: []
209
210
  require_paths:
210
211
  - lib
@@ -219,8 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
219
220
  - !ruby/object:Gem::Version
220
221
  version: '0'
221
222
  requirements: []
222
- rubygems_version: 3.5.9
223
- signing_key:
223
+ rubygems_version: 3.6.9
224
224
  specification_version: 4
225
225
  summary: The Smile Identity Web API allows the user to access\ most of the features
226
226
  of the Smile Identity system through direct server to server queries.