ace-lab 0.5.2 → 0.7.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/CHANGELOG.md +19 -0
- data/lib/ace/lab/cli/commands/service.rb +147 -4
- data/lib/ace/lab/cli.rb +3 -1
- data/lib/ace/lab/molecules/forge_merge_config.rb +127 -0
- data/lib/ace/lab/molecules/forgejo_merge_adapter.rb +150 -0
- data/lib/ace/lab/molecules/grant_resolver.rb +15 -13
- data/lib/ace/lab/molecules/ordinary_admission_resolver.rb +112 -0
- data/lib/ace/lab/molecules/ordinary_ff_merge.rb +86 -0
- data/lib/ace/lab/molecules/ordinary_listener.rb +238 -0
- data/lib/ace/lab/molecules/ordinary_receiver_config.rb +86 -0
- data/lib/ace/lab/molecules/ordinary_socket.rb +129 -0
- data/lib/ace/lab/molecules/service_policy.rb +71 -0
- data/lib/ace/lab/organisms/ordinary_merge_receiver.rb +663 -0
- data/lib/ace/lab/organisms/ordinary_merge_request.rb +422 -0
- data/lib/ace/lab/version.rb +1 -1
- metadata +29 -6
|
@@ -15,6 +15,7 @@ module Ace
|
|
|
15
15
|
ID = /\A[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}\z/
|
|
16
16
|
|
|
17
17
|
def initialize(document, proposal_resolver = nil)
|
|
18
|
+
@document = document
|
|
18
19
|
@operations = document.fetch("operations", {})
|
|
19
20
|
@authorizations = document.fetch("authorizations", {})
|
|
20
21
|
@proposal_resolver = proposal_resolver
|
|
@@ -23,6 +24,10 @@ module Ace
|
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
|
|
27
|
+
# The trusted installation document as loaded; the ordinary producer
|
|
28
|
+
# binds its configuration digest to these exact bytes.
|
|
29
|
+
attr_reader :document
|
|
30
|
+
|
|
26
31
|
def operation!(name, project:, service_id:)
|
|
27
32
|
raise ArgumentError, "invalid operation name" unless name.to_s.match?(NAME)
|
|
28
33
|
operation = @operations[name]
|
|
@@ -101,6 +106,72 @@ module Ace
|
|
|
101
106
|
decision
|
|
102
107
|
end
|
|
103
108
|
|
|
109
|
+
# ---- ordinary fixed merge admission policy (spec 8x3.t.xz9.4) ------
|
|
110
|
+
#
|
|
111
|
+
# The installed ordinary section is the separately configured
|
|
112
|
+
# ordinary receiver mapping and admission policy. It supplies
|
|
113
|
+
# identity and placement only: the caller selects no endpoint,
|
|
114
|
+
# credential or receiver. An absent project section or a disabled
|
|
115
|
+
# admission flag is rollback: new admission is unavailable while
|
|
116
|
+
# canonical records and read-only status stay preserved.
|
|
117
|
+
ORDINARY_SECTION_KEYS = %w[projects].freeze
|
|
118
|
+
ORDINARY_PROJECT_KEYS = %w[receiver_id socket_path receiver_uid principal admission].freeze
|
|
119
|
+
ORDINARY_AUTHORIZATION_KEYS = %w[operation project_id assignment_id attempt_id queue_entry
|
|
120
|
+
input_digest forge pr_url head_repository head_ref candidate_head base_repository
|
|
121
|
+
base_ref expected_base_head expires_at].freeze
|
|
122
|
+
# expires_at bounds the decision itself; it is never exact-matched
|
|
123
|
+
# against the request binding, which carries no expiry.
|
|
124
|
+
ORDINARY_BINDING_KEYS = ORDINARY_AUTHORIZATION_KEYS - %w[expires_at].freeze
|
|
125
|
+
|
|
126
|
+
def ordinary_receiver!(project)
|
|
127
|
+
section = @document["ordinary"]
|
|
128
|
+
return nil unless section.is_a?(Hash)
|
|
129
|
+
validate_ordinary_section!(section)
|
|
130
|
+
entry = section.fetch("projects")[project.to_s]
|
|
131
|
+
return nil unless entry.is_a?(Hash)
|
|
132
|
+
entry
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# The ordinary admission decision: exact-match on every closed
|
|
136
|
+
# binding key, like authorize!, but over the ordinary request
|
|
137
|
+
# identity (forge/PR/repository/ref) instead of execution selectors.
|
|
138
|
+
def authorize_ordinary!(reference, binding)
|
|
139
|
+
raise ArgumentError, "invalid authorization reference" unless reference.to_s.match?(ID)
|
|
140
|
+
decision = @authorizations[reference]
|
|
141
|
+
raise SecurityError, "authorization reference is unresolved" unless decision.is_a?(Hash)
|
|
142
|
+
unless ORDINARY_AUTHORIZATION_KEYS.all? { |key| decision.key?(key) }
|
|
143
|
+
raise SecurityError, "authorization reference is not an ordinary merge decision"
|
|
144
|
+
end
|
|
145
|
+
unless ORDINARY_BINDING_KEYS.all? { |key| decision[key] == binding[key] }
|
|
146
|
+
raise SecurityError, "authorization does not match the exact ordinary merge request"
|
|
147
|
+
end
|
|
148
|
+
raise SecurityError, "authorization has expired" if parse_time(decision["expires_at"]) <= Time.now.utc
|
|
149
|
+
decision
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def validate_ordinary_section!(section)
|
|
153
|
+
unless (section.keys - ORDINARY_SECTION_KEYS).empty? && section["projects"].is_a?(Hash)
|
|
154
|
+
raise Ace::Lab::InvalidConfigurationError, "ordinary section must map projects"
|
|
155
|
+
end
|
|
156
|
+
section.fetch("projects").each do |project, entry|
|
|
157
|
+
unless project.is_a?(String) && project.match?(ID) && entry.is_a?(Hash) &&
|
|
158
|
+
(entry.keys - ORDINARY_PROJECT_KEYS).empty?
|
|
159
|
+
raise Ace::Lab::InvalidConfigurationError, "ordinary project section is invalid"
|
|
160
|
+
end
|
|
161
|
+
unless entry["receiver_id"].is_a?(String) && entry["receiver_id"].match?(ID) &&
|
|
162
|
+
entry["socket_path"].is_a?(String) && entry["socket_path"].start_with?("/") &&
|
|
163
|
+
entry["receiver_uid"].is_a?(Integer) && entry["receiver_uid"] >= 0 &&
|
|
164
|
+
entry["principal"].is_a?(String) && !entry["principal"].empty?
|
|
165
|
+
raise Ace::Lab::InvalidConfigurationError, "ordinary receiver mapping is incomplete"
|
|
166
|
+
end
|
|
167
|
+
admission = entry.fetch("admission", "enabled")
|
|
168
|
+
unless %w[enabled disabled].include?(admission)
|
|
169
|
+
raise Ace::Lab::InvalidConfigurationError, "ordinary admission flag is invalid"
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
true
|
|
173
|
+
end
|
|
174
|
+
|
|
104
175
|
# Inspection never renews effect permission or selects effect argv.
|
|
105
176
|
def inspection!(name, project:, service_id:, executor_uid:)
|
|
106
177
|
raise ArgumentError, "invalid inspection operation" unless name.is_a?(String) && name.match?(NAME)
|