ksef_client 0.1.0.rc1 → 0.1.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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1424 -0
  3. data/CONTRIBUTING.md +45 -6
  4. data/README.md +377 -25
  5. data/SECURITY.md +25 -2
  6. data/docs/REFERENCE.md +3099 -34
  7. data/docs/errors.md +33 -4
  8. data/docs/field_mapping.md +390 -0
  9. data/lib/ksef/auth/access_token.rb +150 -0
  10. data/lib/ksef/auth/authorization_policy.rb +88 -0
  11. data/lib/ksef/auth/challenge.rb +53 -0
  12. data/lib/ksef/auth/client.rb +143 -0
  13. data/lib/ksef/auth/initiation.rb +22 -0
  14. data/lib/ksef/auth/operation_status.rb +34 -0
  15. data/lib/ksef/auth/schema/schemat_auth_v2-0.xsd +109 -0
  16. data/lib/ksef/auth/schema/schemat_auth_v2-1.xsd +109 -0
  17. data/lib/ksef/auth/signature_template.rb +120 -0
  18. data/lib/ksef/auth/signer.rb +132 -0
  19. data/lib/ksef/auth/status.rb +69 -0
  20. data/lib/ksef/auth/token.rb +121 -0
  21. data/lib/ksef/auth/token_info.rb +27 -0
  22. data/lib/ksef/auth/token_request.rb +142 -0
  23. data/lib/ksef/auth/tokens.rb +24 -0
  24. data/lib/ksef/auth/validator.rb +84 -0
  25. data/lib/ksef/auth/xades.rb +40 -0
  26. data/lib/ksef/auth.rb +43 -0
  27. data/lib/ksef/client/receipt.rb +37 -0
  28. data/lib/ksef/client/session.rb +68 -0
  29. data/lib/ksef/client.rb +314 -0
  30. data/lib/ksef/crypto/certificate.rb +78 -0
  31. data/lib/ksef/crypto/digest.rb +25 -0
  32. data/lib/ksef/crypto/encryptor.rb +131 -0
  33. data/lib/ksef/crypto/public_keys.rb +146 -0
  34. data/lib/ksef/crypto.rb +66 -0
  35. data/lib/ksef/environments.rb +1 -1
  36. data/lib/ksef/errors.rb +35 -2
  37. data/lib/ksef/fa3/address.rb +64 -0
  38. data/lib/ksef/fa3/advance_checks.rb +55 -0
  39. data/lib/ksef/fa3/advance_invoice.rb +55 -0
  40. data/lib/ksef/fa3/advance_reader.rb +59 -0
  41. data/lib/ksef/fa3/attachment.rb +43 -0
  42. data/lib/ksef/fa3/attachment_checks.rb +121 -0
  43. data/lib/ksef/fa3/attachment_reader.rb +99 -0
  44. data/lib/ksef/fa3/attachment_table.rb +98 -0
  45. data/lib/ksef/fa3/builder/advances.rb +57 -0
  46. data/lib/ksef/fa3/builder/corrections.rb +46 -0
  47. data/lib/ksef/fa3/builder/subjects.rb +71 -0
  48. data/lib/ksef/fa3/builder.rb +149 -0
  49. data/lib/ksef/fa3/business_validator.rb +134 -0
  50. data/lib/ksef/fa3/canonical.rb +44 -0
  51. data/lib/ksef/fa3/corrected_invoice.rb +53 -0
  52. data/lib/ksef/fa3/correction.rb +151 -0
  53. data/lib/ksef/fa3/correction_checks.rb +106 -0
  54. data/lib/ksef/fa3/correction_reader.rb +88 -0
  55. data/lib/ksef/fa3/data_block.rb +75 -0
  56. data/lib/ksef/fa3/document_mapping.rb +142 -0
  57. data/lib/ksef/fa3/document_validator.rb +174 -0
  58. data/lib/ksef/fa3/element_tree.rb +49 -0
  59. data/lib/ksef/fa3/field_checks.rb +151 -0
  60. data/lib/ksef/fa3/formatting.rb +273 -0
  61. data/lib/ksef/fa3/generated/enums.rb +652 -0
  62. data/lib/ksef/fa3/generated/types.rb +3308 -0
  63. data/lib/ksef/fa3/invoice.rb +259 -0
  64. data/lib/ksef/fa3/issue.rb +32 -0
  65. data/lib/ksef/fa3/line.rb +162 -0
  66. data/lib/ksef/fa3/meta_entry.rb +45 -0
  67. data/lib/ksef/fa3/model_validator.rb +219 -0
  68. data/lib/ksef/fa3/nip.rb +59 -0
  69. data/lib/ksef/fa3/node_reader.rb +46 -0
  70. data/lib/ksef/fa3/order.rb +55 -0
  71. data/lib/ksef/fa3/order_line.rb +70 -0
  72. data/lib/ksef/fa3/parser.rb +208 -0
  73. data/lib/ksef/fa3/provenance.rb +137 -0
  74. data/lib/ksef/fa3/rounding_inference.rb +87 -0
  75. data/lib/ksef/fa3/row_reader.rb +66 -0
  76. data/lib/ksef/fa3/serializer.rb +140 -0
  77. data/lib/ksef/fa3/subject.rb +128 -0
  78. data/lib/ksef/fa3/subject_checks.rb +126 -0
  79. data/lib/ksef/fa3/subject_reader.rb +76 -0
  80. data/lib/ksef/fa3/summaries.rb +107 -0
  81. data/lib/ksef/fa3/summary_checks.rb +75 -0
  82. data/lib/ksef/fa3/table_column.rb +49 -0
  83. data/lib/ksef/fa3/totals.rb +119 -0
  84. data/lib/ksef/fa3/validator.rb +79 -0
  85. data/lib/ksef/fa3/vat_rate.rb +94 -0
  86. data/lib/ksef/fa3.rb +59 -0
  87. data/lib/ksef/http/connection.rb +47 -4
  88. data/lib/ksef/http/json_decoder.rb +53 -0
  89. data/lib/ksef/http/retry.rb +105 -0
  90. data/lib/ksef/invoices/client.rb +72 -0
  91. data/lib/ksef/ksef_number.rb +150 -0
  92. data/lib/ksef/sessions/invoice_codes.rb +70 -0
  93. data/lib/ksef/sessions/invoice_state.rb +68 -0
  94. data/lib/ksef/sessions/online.rb +169 -0
  95. data/lib/ksef/sessions/session_codes.rb +73 -0
  96. data/lib/ksef/sessions/session_state.rb +48 -0
  97. data/lib/ksef/sessions/status.rb +138 -0
  98. data/lib/ksef/sessions/upo_page.rb +36 -0
  99. data/lib/ksef/sessions.rb +91 -0
  100. data/lib/ksef/upo/client.rb +154 -0
  101. data/lib/ksef/upo/document.rb +74 -0
  102. data/lib/ksef/upo/schema/upo-v4-3.xsd +293 -0
  103. data/lib/ksef/upo/validation.rb +37 -0
  104. data/lib/ksef/upo/validator.rb +124 -0
  105. data/lib/ksef/upo.rb +55 -0
  106. data/lib/ksef/version.rb +1 -1
  107. data/lib/ksef.rb +18 -0
  108. metadata +105 -4
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ksef
4
+ module Auth
5
+ # The optional `AuthorizationPolicy` of an {TokenRequest} — a whitelist restricting
6
+ # which client IPs may use the resulting `accessToken`.
7
+ #
8
+ # Its own class rather than a Hash inside {TokenRequest} because the schema treats it
9
+ # as a distinct structure with its own rules: three list kinds, each capped at ten
10
+ # entries, in a fixed order, wrapped in an `AllowedIps` element that is mandatory once
11
+ # the policy is present (docs/REFERENCE.md §4.1).
12
+ #
13
+ # The IP values are deliberately **not** pattern-checked here. v2.1's patterns are
14
+ # correct and the schema will catch a malformed address, whereas v2.0's are broken
15
+ # outright (§14.4) — duplicating either in Ruby would mean maintaining a second,
16
+ # divergent source of truth for no gain.
17
+ class AuthorizationPolicy
18
+ MAX_IPS = 10
19
+
20
+ # Ordered as the schema sequences them; the caller's Hash order is irrelevant.
21
+ IP_ELEMENTS = { addresses: "Ip4Address", ranges: "Ip4Range", masks: "Ip4Mask" }.freeze
22
+
23
+ # The same three lists as JSON, for the KSeF-token flow's `authorizationPolicy`.
24
+ # Both authentication methods accept the policy; only their encodings differ, so the
25
+ # rules above are shared rather than restated. The OpenAPI contract caps each of
26
+ # these arrays at ten entries too, which independently corroborates {MAX_IPS}.
27
+ IP_FIELDS = { addresses: :ip4Addresses, ranges: :ip4Ranges, masks: :ip4Masks }.freeze
28
+
29
+ attr_reader :addresses, :ranges, :masks
30
+
31
+ # @return [AuthorizationPolicy, nil] passes `nil` and an existing policy through
32
+ # @raise [Ksef::ValidationError] on an unknown key
33
+ def self.coerce(value)
34
+ return value if value.nil? || value.is_a?(self)
35
+
36
+ unknown = value.keys - IP_ELEMENTS.keys
37
+ unless unknown.empty?
38
+ raise ValidationError,
39
+ "Unknown allowed_ips key(s) #{unknown.map(&:inspect).join(", ")}. " \
40
+ "Permitted: #{IP_ELEMENTS.keys.map(&:inspect).join(", ")}."
41
+ end
42
+
43
+ new(**value)
44
+ end
45
+
46
+ def initialize(addresses: [], ranges: [], masks: [])
47
+ @addresses = Array(addresses).freeze
48
+ @ranges = Array(ranges).freeze
49
+ @masks = Array(masks).freeze
50
+ validate!
51
+ freeze
52
+ end
53
+
54
+ # @return [Array<Array(String, String)>] `[element name, value]` pairs in schema order
55
+ def entries
56
+ IP_ELEMENTS.flat_map { |key, name| public_send(key).map { |value| [name, value] } }
57
+ end
58
+
59
+ # @return [Hash] the contract's `AllowedIps`, omitting the lists that are empty —
60
+ # all three are nullable, and sending `[]` says nothing the absence does not
61
+ def to_h
62
+ IP_FIELDS.each_with_object({}) do |(key, field), json|
63
+ values = public_send(key)
64
+ json[field] = values unless values.empty?
65
+ end
66
+ end
67
+
68
+ def empty? = addresses.empty? && ranges.empty? && masks.empty?
69
+
70
+ private
71
+
72
+ def validate!
73
+ # AllowedIps is mandatory inside AuthorizationPolicy, so an empty policy cannot be
74
+ # made schema-valid. Saying so beats emitting a document KSeF will reject.
75
+ raise ValidationError, "allowed_ips was given but lists no addresses" if empty?
76
+
77
+ IP_ELEMENTS.each_key { |key| check_count(key, public_send(key)) }
78
+ end
79
+
80
+ def check_count(key, list)
81
+ return if list.size <= MAX_IPS
82
+
83
+ raise ValidationError,
84
+ "allowed_ips[#{key.inspect}] has #{list.size} entries; the schema permits #{MAX_IPS}"
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ksef
4
+ module Auth
5
+ # The response to `POST /auth/challenge` — the prologue to both authentication methods.
6
+ #
7
+ # `timestamp_ms` is not decoration: the KSeF-token flow encrypts `{token}|{timestampMs}`
8
+ # and the timestamp acts as a nonce, so it must be the server's value rather than a
9
+ # locally generated one (docs/REFERENCE.md §4.5).
10
+ Challenge = Data.define(:challenge, :timestamp, :timestamp_ms, :client_ip)
11
+
12
+ # Reopened rather than using a `Data.define` block, matching {Ksef::FA3::Invoice}: a
13
+ # constant inside that block would be defined on `Object`, not on the class.
14
+ class Challenge
15
+ # Ten minutes from issue (§4).
16
+ LIFETIME = 600
17
+
18
+ # Length 36, `\d{8}-CR-[A-F0-9]{10}-[A-F0-9]{10}-[A-F0-9]{2}` (§4.1). Lives here
19
+ # rather than on the request documents because it describes a *challenge*, and both
20
+ # authentication methods consume the same one — the XAdES document and the KSeF-token
21
+ # JSON body would otherwise each carry their own copy.
22
+ FORMAT = /\A\d{8}-CR-[A-F0-9]{10}-[A-F0-9]{10}-[A-F0-9]{2}\z/
23
+
24
+ # Checked before a request is spent on the challenge, since a malformed or
25
+ # copy-pasted one is the cheapest failure to catch locally.
26
+ #
27
+ # @param value [String]
28
+ # @raise [Ksef::ValidationError]
29
+ def self.validate_format!(value)
30
+ return value if value.is_a?(String) && FORMAT.match?(value)
31
+
32
+ raise ValidationError,
33
+ "Challenge #{value.inspect} is malformed. Expected 36 characters as " \
34
+ "YYYYMMDD-CR-XXXXXXXXXX-XXXXXXXXXX-XX with uppercase hex, exactly as returned by " \
35
+ "POST /auth/challenge."
36
+ end
37
+
38
+ def self.from(payload)
39
+ new(
40
+ challenge: payload["challenge"],
41
+ timestamp: Ksef::Auth.time(payload["timestamp"]),
42
+ timestamp_ms: payload["timestampMs"],
43
+ client_ip: payload["clientIp"]
44
+ )
45
+ end
46
+
47
+ # Worth checking before spending a signature on a challenge that has already lapsed.
48
+ def expires_at = timestamp && (timestamp + LIFETIME)
49
+ def expired?(now = Time.now) = !expires_at.nil? && expires_at <= now
50
+ def to_s = challenge.to_s
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+
5
+ module Ksef
6
+ module Auth
7
+ # The six authentication endpoint calls: the five HTTP steps of §4.2's flow — its
8
+ # sixth step, signing, is offline — plus `POST /auth/ksef-token` for the other
9
+ # authentication method (docs/REFERENCE.md §4.2, §4.5).
10
+ #
11
+ # Deliberately thin: it maps requests and responses and does nothing else. Deciding
12
+ # *which* credential to present, and caching the result, belongs a layer up in
13
+ # {Ksef::Client}, which does exactly that.
14
+ #
15
+ # Namespaced `Ksef::Auth::Client` rather than folded into `Ksef::Client` so that each
16
+ # subsystem's endpoints stay together, matching how the official clients are organised.
17
+ #
18
+ # Three of these calls are **unauthenticated** — the challenge and both authentication
19
+ # submissions declare no `security` in the contract. Of the rest, {#status} and
20
+ # {#redeem} take the temporary *authentication* token and {#refresh} takes the refresh
21
+ # token, so the bearer is passed per call rather than held by the instance.
22
+ class Client
23
+ # Polling defaults. There is deliberately no timeout: on DEMO and PROD the operation
24
+ # legitimately stays "in progress" while the certificate's status is checked with its
25
+ # issuer over OCSP/CRL, and the docs say the duration depends on that issuer. A client
26
+ # that gives up after a fixed interval reports failure for authentications that were
27
+ # about to succeed (§4.2).
28
+ POLL_INTERVAL = 2
29
+
30
+ # @param connection [Faraday::Connection] usually from {Ksef::HTTP::Connection.build}
31
+ def initialize(connection)
32
+ @connection = connection
33
+ end
34
+
35
+ # @return [Challenge]
36
+ def challenge
37
+ Challenge.from(post("auth/challenge").body)
38
+ end
39
+
40
+ # Submits the XAdES-signed `AuthTokenRequest`. Returns `202 Accepted`; the operation
41
+ # is asynchronous from here.
42
+ #
43
+ # @param signed_xml [String]
44
+ # @param verify_certificate_chain [Boolean, nil] the contract's optional query flag
45
+ # @return [Initiation]
46
+ def submit_xades(signed_xml, verify_certificate_chain: nil)
47
+ response = @connection.post("auth/xades-signature") do |request|
48
+ request.params["verifyCertificateChain"] = verify_certificate_chain unless verify_certificate_chain.nil?
49
+ request.headers["Content-Type"] = "application/xml"
50
+ request.body = signed_xml
51
+ end
52
+ Initiation.from(response.body)
53
+ end
54
+
55
+ # Submits a KSeF-token authentication. Like {#submit_xades} this takes an
56
+ # already-built request — {Token#authentication_request} assembles it, because the
57
+ # encryption depends on which published key was selected and that is not this
58
+ # layer's decision.
59
+ #
60
+ # A `400` here carries `21111` for a bad challenge or `21470` for a stale key
61
+ # identifier; the latter is worth wrapping in
62
+ # {Ksef::Crypto::PublicKeys#with_key_rotation}.
63
+ #
64
+ # @param request [Hash] the contract's `InitTokenAuthenticationRequest`
65
+ # @return [Initiation]
66
+ def submit_ksef_token(request)
67
+ Initiation.from(post("auth/ksef-token", body: request).body)
68
+ end
69
+
70
+ # @param token [String, TokenInfo] the *authentication* token from {#submit_xades}
71
+ # @return [OperationStatus]
72
+ def status(reference_number, token:)
73
+ OperationStatus.from(get("auth/#{reference_number}", token: token).body)
74
+ end
75
+
76
+ # Exchanges a completed authentication for the token pair. **Single-use** — a second
77
+ # call with the same authentication token is a 400, so this is never auto-retried
78
+ # (it is a POST, which the retry policy already excludes).
79
+ #
80
+ # @return [Tokens]
81
+ def redeem(token:)
82
+ Tokens.from(post("auth/token/redeem", token: token).body)
83
+ end
84
+
85
+ # @return [TokenInfo] a fresh access token
86
+ def refresh(refresh_token:)
87
+ TokenInfo.from(post("auth/token/refresh", token: refresh_token).body["accessToken"])
88
+ end
89
+
90
+ # Polls until the operation stops being in progress.
91
+ #
92
+ # @param interval [Numeric] seconds between polls
93
+ # @param sleeper [#call] injected for tests; receives the interval
94
+ # @yieldparam status [OperationStatus] after each poll, for progress reporting
95
+ # @return [OperationStatus] the terminal status, successful or not
96
+ def wait_until_complete(reference_number, token:, interval: POLL_INTERVAL, sleeper: method(:sleep))
97
+ loop do
98
+ current = status(reference_number, token: token)
99
+ yield current if block_given?
100
+ return current if current.terminal?
101
+
102
+ sleeper.call(interval)
103
+ end
104
+ end
105
+
106
+ # As {#wait_until_complete}, but insists on success.
107
+ #
108
+ # @raise [Ksef::AuthenticationError] carrying the server's own wording and details
109
+ def authenticate!(reference_number, token:, **, &)
110
+ result = wait_until_complete(reference_number, token: token, **, &)
111
+ return result if result.success?
112
+
113
+ detail = result.details.empty? ? "" : " (#{result.details.join("; ")})"
114
+ raise AuthenticationError,
115
+ "Authentication #{reference_number} failed with status #{result.code}: #{result.explain}#{detail}"
116
+ end
117
+
118
+ private
119
+
120
+ def get(path, token: nil)
121
+ @connection.get(path) { |request| authorize(request, token) }
122
+ end
123
+
124
+ def post(path, token: nil, body: nil)
125
+ @connection.post(path) do |request|
126
+ authorize(request, token)
127
+ request.body = body unless body.nil?
128
+ end
129
+ end
130
+
131
+ # The bearer differs per call: the authentication token for status and redemption,
132
+ # the refresh token for refresh, and none at all for the first two. A {TokenInfo} or
133
+ # a bare String both work — but the value has to be pulled out explicitly, because
134
+ # `TokenInfo#to_s` redacts itself and interpolating one would send "[REDACTED]".
135
+ def authorize(request, token)
136
+ return if token.nil?
137
+
138
+ value = token.is_a?(TokenInfo) ? token.token : token.to_s
139
+ request.headers["Authorization"] = "Bearer #{value}"
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ksef
4
+ module Auth
5
+ # The `202 Accepted` from `POST /auth/xades-signature` — the contract's
6
+ # `AuthenticationInitResponse`.
7
+ #
8
+ # The token here is the short-lived *authentication* token, not an access token. It
9
+ # authorises exactly two follow-up calls: checking the operation's status, and redeeming
10
+ # it for the real pair. Confusing the two is the obvious mistake in this flow (docs/REFERENCE.md §4.2).
11
+ Initiation = Data.define(:reference_number, :authentication_token) do
12
+ def self.from(payload)
13
+ new(
14
+ reference_number: payload["referenceNumber"],
15
+ authentication_token: TokenInfo.from(payload["authenticationToken"])
16
+ )
17
+ end
18
+
19
+ def token = authentication_token&.token
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ksef
4
+ module Auth
5
+ # The response to `GET /auth/{referenceNumber}` — the contract's
6
+ # `AuthenticationOperationStatusResponse`.
7
+ OperationStatus = Data.define(
8
+ :code, :description, :details, :authentication_method, :started_at,
9
+ :token_redeemed, :last_refresh_at, :refresh_token_valid_until
10
+ ) do
11
+ def self.from(payload)
12
+ status = payload["status"] || {}
13
+ new(
14
+ code: status["code"],
15
+ description: status["description"],
16
+ details: Array(status["details"]).freeze,
17
+ authentication_method: payload["authenticationMethod"],
18
+ started_at: Ksef::Auth.time(payload["startDate"]),
19
+ token_redeemed: payload["isTokenRedeemed"],
20
+ last_refresh_at: Ksef::Auth.time(payload["lastTokenRefreshDate"]),
21
+ refresh_token_valid_until: Ksef::Auth.time(payload["refreshTokenValidUntil"])
22
+ )
23
+ end
24
+
25
+ def in_progress? = Status.in_progress?(code)
26
+ def success? = Status.success?(code)
27
+ def terminal? = Status.terminal?(code)
28
+ def retryable? = Status.retryable?(code)
29
+
30
+ # Prefers the server's own wording; falls back to our table when it sends none.
31
+ def explain = description.to_s.empty? ? Status.describe(code) : description
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,109 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <xsd:schema
3
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
4
+ xmlns:tns="http://ksef.mf.gov.pl/auth/token/2.0"
5
+ targetNamespace="http://ksef.mf.gov.pl/auth/token/2.0"
6
+ elementFormDefault="qualified" attributeFormDefault="unqualified">
7
+ <xsd:element name="AuthTokenRequest">
8
+ <xsd:complexType>
9
+ <xsd:sequence>
10
+ <xsd:element name="Challenge">
11
+ <xsd:simpleType>
12
+ <xsd:restriction base="xsd:token">
13
+ <xsd:length value="36"/>
14
+ <xsd:pattern value="\d{8}-CR-[A-F0-9]{10}-[A-F0-9]{10}-[A-F0-9]{2}"/>
15
+ </xsd:restriction>
16
+ </xsd:simpleType>
17
+ </xsd:element>
18
+ <xsd:element name="ContextIdentifier" type="tns:TContextIdentifier"/>
19
+ <xsd:element name="SubjectIdentifierType" type="tns:SubjectIdentifierTypeEnum"/>
20
+ <xsd:element name="AuthorizationPolicy" minOccurs="0">
21
+ <xsd:complexType>
22
+ <xsd:sequence>
23
+ <xsd:element name="AllowedIps">
24
+ <xsd:complexType>
25
+ <xsd:sequence minOccurs="1" maxOccurs="1">
26
+ <xsd:element name="Ip4Address" minOccurs="0" maxOccurs="10">
27
+ <xsd:simpleType>
28
+ <xsd:restriction base="xsd:token">
29
+ <xsd:pattern value="^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$"/>
30
+ </xsd:restriction>
31
+ </xsd:simpleType>
32
+ </xsd:element>
33
+ <xsd:element name="Ip4Range" minOccurs="0" maxOccurs="10">
34
+ <xsd:simpleType>
35
+ <xsd:restriction base="xsd:token">
36
+ <xsd:pattern value="^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}-((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$"/>
37
+ </xsd:restriction>
38
+ </xsd:simpleType>
39
+ </xsd:element>
40
+ <xsd:element name="Ip4Mask" minOccurs="0" maxOccurs="10">
41
+ <xsd:simpleType>
42
+ <xsd:restriction base="xsd:token">
43
+ <xsd:pattern value="^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}\/(0|[1-9]|1[0-9]|2[0-9]|3[0-2])$"/>
44
+ </xsd:restriction>
45
+ </xsd:simpleType>
46
+ </xsd:element>
47
+ </xsd:sequence>
48
+ </xsd:complexType>
49
+ </xsd:element>
50
+ </xsd:sequence>
51
+ </xsd:complexType>
52
+ </xsd:element>
53
+ </xsd:sequence>
54
+ </xsd:complexType>
55
+ </xsd:element>
56
+ <xsd:simpleType name="SubjectIdentifierTypeEnum">
57
+ <xsd:restriction base="xsd:token">
58
+ <xsd:enumeration value="certificateSubject"/>
59
+ <xsd:enumeration value="certificateFingerprint"/>
60
+ </xsd:restriction>
61
+ </xsd:simpleType>
62
+
63
+ <xsd:complexType name="TContextIdentifier">
64
+ <xsd:choice>
65
+ <xsd:element name="Nip" type="tns:TNIP">
66
+ <xsd:annotation>
67
+ <xsd:documentation>Numer Identyfikacyjny NIP</xsd:documentation>
68
+ </xsd:annotation>
69
+ </xsd:element>
70
+ <xsd:element name="InternalId" type="tns:TIID">
71
+ <xsd:annotation>
72
+ <xsd:documentation>Identyfikator wewnętrzny</xsd:documentation>
73
+ </xsd:annotation>
74
+ </xsd:element>
75
+ <xsd:element name="NipVatUe" type="tns:TNipVatUE">
76
+ <xsd:annotation>
77
+ <xsd:documentation>Kontekst złożony, będący połaczniem numeru NIP oraz numeru Vat UE</xsd:documentation>
78
+ </xsd:annotation>
79
+ </xsd:element>
80
+ <xsd:element name="PeppolId" type="tns:TPeppolId">
81
+ <xsd:annotation>
82
+ <xsd:documentation>Identyfikator dostawcy usług Peppol</xsd:documentation>
83
+ </xsd:annotation>
84
+ </xsd:element>
85
+ </xsd:choice>
86
+ </xsd:complexType>
87
+
88
+ <xsd:simpleType name="TIID">
89
+ <xsd:restriction base="xsd:string">
90
+ <xsd:pattern value="[1-9]((\d[1-9])|([1-9]\d))\d{7}-\d{5}"/>
91
+ </xsd:restriction>
92
+ </xsd:simpleType>
93
+ <xsd:simpleType name="TNIP">
94
+ <xsd:restriction base="xsd:string">
95
+ <xsd:pattern value="[1-9]((\d[1-9])|([1-9]\d))\d{7}"/>
96
+ </xsd:restriction>
97
+ </xsd:simpleType>
98
+ <xsd:simpleType name="TNipVatUE">
99
+ <xsd:restriction base="xsd:string">
100
+ <!-- based on: https://ec.europa.eu/taxation_customs/vies/#/faq Q11 -->
101
+ <xsd:pattern value="([1-9]((\d[1-9])|([1-9]\d))\d{7}-((AT)(U\d{8})|(BE)([01]{1}\d{9})|(BG)(\d{9,10})|(CY)(\d{8}[A-Z])|(CZ)(\d{8,10})|(DE)(\d{9})|(DK)(\d{8})|(EE)(\d{9})|(EL)(\d{9})|(ES)([A-Z]\d{8}|\d{8}[A-Z]|[A-Z]\d{7}[A-Z])|(FI)(\d{8})|(FR)[A-Z0-9]{2}\d{9}|(HR)(\d{11})|(HU)(\d{8})|(IE)(\d{7}[A-Z]{2}|\d[A-Z0-9+*]\d{5}[A-Z])|(IT)(\d{11})|(LT)(\d{9}|\d{12})|(LU)(\d{8})|(LV)(\d{11})|(MT)(\d{8})|(NL)([A-Z0-9+*]{12})|(PT)(\d{9})|(RO)(\d{2,10})|(SE)(\d{12})|(SI)(\d{8})|(SK)(\d{10})|(XI)((\d{9}|(\d{12}))|(GD|HA)(\d{3}))))$"/>
102
+ </xsd:restriction>
103
+ </xsd:simpleType>
104
+ <xsd:simpleType name="TPeppolId">
105
+ <xsd:restriction base="xsd:string">
106
+ <xsd:pattern value="^P[A-Z]{2}[0-9]{6}$"/>
107
+ </xsd:restriction>
108
+ </xsd:simpleType>
109
+ </xsd:schema>
@@ -0,0 +1,109 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <xsd:schema
3
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
4
+ xmlns:tns="http://ksef.mf.gov.pl/auth/token/2.1"
5
+ targetNamespace="http://ksef.mf.gov.pl/auth/token/2.1"
6
+ elementFormDefault="qualified" attributeFormDefault="unqualified">
7
+ <xsd:element name="AuthTokenRequest">
8
+ <xsd:complexType>
9
+ <xsd:sequence>
10
+ <xsd:element name="Challenge">
11
+ <xsd:simpleType>
12
+ <xsd:restriction base="xsd:token">
13
+ <xsd:length value="36"/>
14
+ <xsd:pattern value="\d{8}-CR-[A-F0-9]{10}-[A-F0-9]{10}-[A-F0-9]{2}"/>
15
+ </xsd:restriction>
16
+ </xsd:simpleType>
17
+ </xsd:element>
18
+ <xsd:element name="ContextIdentifier" type="tns:TContextIdentifier"/>
19
+ <xsd:element name="SubjectIdentifierType" type="tns:SubjectIdentifierTypeEnum"/>
20
+ <xsd:element name="AuthorizationPolicy" minOccurs="0">
21
+ <xsd:complexType>
22
+ <xsd:sequence>
23
+ <xsd:element name="AllowedIps">
24
+ <xsd:complexType>
25
+ <xsd:sequence minOccurs="1" maxOccurs="1">
26
+ <xsd:element name="Ip4Address" minOccurs="0" maxOccurs="10">
27
+ <xsd:simpleType>
28
+ <xsd:restriction base="xsd:token">
29
+ <xsd:pattern value="((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])"/>
30
+ </xsd:restriction>
31
+ </xsd:simpleType>
32
+ </xsd:element>
33
+ <xsd:element name="Ip4Range" minOccurs="0" maxOccurs="10">
34
+ <xsd:simpleType>
35
+ <xsd:restriction base="xsd:token">
36
+ <xsd:pattern value="((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])-((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])"/>
37
+ </xsd:restriction>
38
+ </xsd:simpleType>
39
+ </xsd:element>
40
+ <xsd:element name="Ip4Mask" minOccurs="0" maxOccurs="10">
41
+ <xsd:simpleType>
42
+ <xsd:restriction base="xsd:token">
43
+ <xsd:pattern value="((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])/(0|[1-9]|[12][0-9]|3[0-2])"/>
44
+ </xsd:restriction>
45
+ </xsd:simpleType>
46
+ </xsd:element>
47
+ </xsd:sequence>
48
+ </xsd:complexType>
49
+ </xsd:element>
50
+ </xsd:sequence>
51
+ </xsd:complexType>
52
+ </xsd:element>
53
+ </xsd:sequence>
54
+ </xsd:complexType>
55
+ </xsd:element>
56
+ <xsd:simpleType name="SubjectIdentifierTypeEnum">
57
+ <xsd:restriction base="xsd:token">
58
+ <xsd:enumeration value="certificateSubject"/>
59
+ <xsd:enumeration value="certificateFingerprint"/>
60
+ </xsd:restriction>
61
+ </xsd:simpleType>
62
+
63
+ <xsd:complexType name="TContextIdentifier">
64
+ <xsd:choice>
65
+ <xsd:element name="Nip" type="tns:TNIP">
66
+ <xsd:annotation>
67
+ <xsd:documentation>Numer Identyfikacyjny NIP</xsd:documentation>
68
+ </xsd:annotation>
69
+ </xsd:element>
70
+ <xsd:element name="InternalId" type="tns:TIID">
71
+ <xsd:annotation>
72
+ <xsd:documentation>Identyfikator wewnętrzny</xsd:documentation>
73
+ </xsd:annotation>
74
+ </xsd:element>
75
+ <xsd:element name="NipVatUe" type="tns:TNipVatUE">
76
+ <xsd:annotation>
77
+ <xsd:documentation>Kontekst złożony, będący połaczniem numeru NIP oraz numeru Vat UE</xsd:documentation>
78
+ </xsd:annotation>
79
+ </xsd:element>
80
+ <xsd:element name="PeppolId" type="tns:TPeppolId">
81
+ <xsd:annotation>
82
+ <xsd:documentation>Identyfikator dostawcy usług Peppol</xsd:documentation>
83
+ </xsd:annotation>
84
+ </xsd:element>
85
+ </xsd:choice>
86
+ </xsd:complexType>
87
+
88
+ <xsd:simpleType name="TIID">
89
+ <xsd:restriction base="xsd:string">
90
+ <xsd:pattern value="[1-9]((\d[1-9])|([1-9]\d))\d{7}-\d{5}"/>
91
+ </xsd:restriction>
92
+ </xsd:simpleType>
93
+ <xsd:simpleType name="TNIP">
94
+ <xsd:restriction base="xsd:string">
95
+ <xsd:pattern value="[1-9]((\d[1-9])|([1-9]\d))\d{7}"/>
96
+ </xsd:restriction>
97
+ </xsd:simpleType>
98
+ <xsd:simpleType name="TNipVatUE">
99
+ <xsd:restriction base="xsd:string">
100
+ <!-- based on: https://ec.europa.eu/taxation_customs/vies/#/faq Q11 -->
101
+ <xsd:pattern value="([1-9]((\d[1-9])|([1-9]\d))\d{7}-((AT)(U\d{8})|(BE)([01]{1}\d{9})|(BG)(\d{9,10})|(CY)(\d{8}[A-Z])|(CZ)(\d{8,10})|(DE)(\d{9})|(DK)(\d{8})|(EE)(\d{9})|(EL)(\d{9})|(ES)([A-Z]\d{8}|\d{8}[A-Z]|[A-Z]\d{7}[A-Z])|(FI)(\d{8})|(FR)[A-Z0-9]{2}\d{9}|(HR)(\d{11})|(HU)(\d{8})|(IE)(\d{7}[A-Z]{2}|\d[A-Z0-9+*]\d{5}[A-Z])|(IT)(\d{11})|(LT)(\d{9}|\d{12})|(LU)(\d{8})|(LV)(\d{11})|(MT)(\d{8})|(NL)([A-Z0-9+*]{12})|(PT)(\d{9})|(RO)(\d{2,10})|(SE)(\d{12})|(SI)(\d{8})|(SK)(\d{10})|(XI)((\d{9}|(\d{12}))|(GD|HA)(\d{3}))))$"/>
102
+ </xsd:restriction>
103
+ </xsd:simpleType>
104
+ <xsd:simpleType name="TPeppolId">
105
+ <xsd:restriction base="xsd:string">
106
+ <xsd:pattern value="^P[A-Z]{2}[0-9]{6}$"/>
107
+ </xsd:restriction>
108
+ </xsd:simpleType>
109
+ </xsd:schema>