edc_web_components/components/
create_policy.rs1mod atomic_constraint_edit;
2mod list_of_rules;
3mod operator_selector;
4mod rule;
5
6pub use self::atomic_constraint_edit::AtomicConstraintEdit;
7pub use self::list_of_rules::ListOfRules;
8pub use self::operator_selector::OperatorSelector;
9pub use self::rule::Rule;
10use crate::components::create_policy::atomic_constraint_edit::ConstraintMode;
11use crate::components::simple_or_id_field::SimpleOrIdField;
12use crate::components::{ExtensiblePropertiesEdit, StringListEdit};
13use crate::contexts::use_edc_connector_context;
14use edc_connector_client::types::policy::{
15 Action, Constraint, NewPolicyDefinition, Obligation, Permission, Policy, PolicyKind, Prohibition,
16 Target,
17};
18use edc_connector_client::{EdcConnectorApiVersion, Error, ManagementApiErrorDetailKind};
19use patternfly_yew::prelude::*;
20use std::collections::HashMap;
21use uuid::Uuid;
22use yew::platform::spawn_local;
23use yew::prelude::*;
24
25#[derive(Clone, Debug, PartialEq, Properties)]
26pub struct CreatePolicyProps {
27 #[prop_or_default]
28 pub on_create: Callback<()>,
29}
30
31#[component]
32pub fn CreatePolicy(props: &CreatePolicyProps) -> Html {
33 let edc_connector_context = use_edc_connector_context();
34
35 let name = use_state(String::new);
36 let assignee = use_state(String::default);
37 let assigner = use_state(String::default);
38 let target = use_state(|| (true, String::default()));
39 let permissions = use_state(Vec::new);
40 let prohibitions = use_state(Vec::new);
41 let obligations = use_state(Vec::new);
42 let profiles = use_state(Vec::new);
43 let extensible_properties = use_state(HashMap::new);
44
45 let creation_errors = use_state(|| None);
46
47 let onsubmit = use_callback(
48 (
49 edc_connector_context,
50 name.clone(),
51 assignee.clone(),
52 assigner.clone(),
53 target.clone(),
54 permissions.clone(),
55 prohibitions.clone(),
56 obligations.clone(),
57 profiles.clone(),
58 extensible_properties.clone(),
59 props.on_create.clone(),
60 creation_errors.setter(),
61 ),
62 |event: SubmitEvent,
63 (
64 edc_connector_context,
65 name,
66 assignee,
67 assigner,
68 target,
69 permissions,
70 prohibitions,
71 obligations,
72 profiles,
73 extensible_properties,
74 on_create,
75 creation_errors_setter,
76 )| {
77 event.prevent_default();
78
79 let edc_connector_context = edc_connector_context.clone();
80 let name = (**name).clone();
81 let profiles = (**profiles).clone();
82 let extensible_properties = (**extensible_properties).clone();
83 let assignee = (**assignee).clone();
84 let assigner = (**assigner).clone();
85 let (is_simple_target, target) = (**target).clone();
86
87 let permissions = (**permissions)
88 .iter()
89 .map(
90 |(action, constraints): &(Action, Vec<(ConstraintMode, Constraint)>)| {
91 Permission::builder()
92 .action(action.clone())
93 .constraints(
94 constraints
95 .iter()
96 .map(|(_, constraint)| constraint.clone())
97 .collect(),
98 )
99 .build()
100 },
101 )
102 .collect();
103
104 let prohibitions = (**prohibitions)
105 .iter()
106 .map(
107 |(action, constraints): &(Action, Vec<(ConstraintMode, Constraint)>)| {
108 Prohibition::builder()
109 .action(action.clone())
110 .constraints(
111 constraints
112 .iter()
113 .map(|(_, constraint)| constraint.clone())
114 .collect(),
115 )
116 .build()
117 },
118 )
119 .collect();
120
121 let obligations = (**obligations)
122 .iter()
123 .map(
124 |(action, constraints): &(Action, Vec<(ConstraintMode, Constraint)>)| {
125 Obligation::builder()
126 .action(action.clone())
127 .constraints(
128 constraints
129 .iter()
130 .map(|(_, constraint)| constraint.clone())
131 .collect(),
132 )
133 .build()
134 },
135 )
136 .collect();
137
138 creation_errors_setter.set(None);
139
140 let on_create = on_create.clone();
141 let creation_errors_setter = creation_errors_setter.clone();
142
143 spawn_local(async move {
144 let kind = PolicyKind::Set;
145
146 let policy_builder = Policy::builder()
147 .kind(kind)
148 .permissions(permissions)
149 .prohibitions(prohibitions)
150 .obligations(obligations)
151 .profiles(profiles)
152 .extensible_properties(extensible_properties);
153
154 let policy_builder = if !assignee.is_empty() {
155 policy_builder.maybe_assignee(Some(assignee))
156 } else {
157 policy_builder.maybe_assignee(None::<String>)
158 };
159
160 let policy_builder = if !assigner.is_empty() {
161 policy_builder.maybe_assigner(Some(assigner))
162 } else {
163 policy_builder.maybe_assigner(None::<String>)
164 };
165
166 let policy_builder = if !target.is_empty() {
167 if is_simple_target {
168 policy_builder.target(Target::Simple(target))
169 } else {
170 policy_builder.target(Target::Id { id: target })
171 }
172 } else {
173 policy_builder.maybe_target(None::<Target>)
174 };
175
176 let policy = policy_builder.build();
177
178 let new_policy = NewPolicyDefinition::builder()
179 .id(Uuid::new_v4().to_string())
180 .policy(policy)
181 .private_property("name", name)
182 .build();
183
184 if let Some(client) = edc_connector_context.get_client() {
185 if let Err(error) = client
186 .policies(EdcConnectorApiVersion::V4)
187 .create(&new_policy)
188 .await
189 {
190 match error {
191 Error::ManagementApi(management_api_error) => {
192 let error_message = match management_api_error.error_detail {
193 ManagementApiErrorDetailKind::Raw(error) => html!(<div>{ error }</div>),
194 ManagementApiErrorDetailKind::Parsed(error_list) => error_list
195 .into_iter()
196 .map(|error| html!(<div>{ error.message }</div>))
197 .collect::<Html>(),
198 };
199
200 creation_errors_setter.set(Some(error_message));
201 }
202 _ => {
203 let error_message = format!("{error:?}");
204 creation_errors_setter.set(Some(html!(<div>{ error_message }</div>)));
205 }
206 }
207 } else {
208 on_create.emit(());
209 }
210 }
211 })
212 },
213 );
214
215 let onchange_name = use_callback(name.setter(), move |name, name_setter| {
216 name_setter.set(name);
217 });
218
219 let onchange_assignee = use_callback(
220 assignee.setter(),
221 move |assignee: String, assignee_setter| {
222 assignee_setter.set(assignee);
223 },
224 );
225
226 let onchange_assigner = use_callback(
227 assigner.setter(),
228 move |assigner: String, assigner_setter| {
229 assigner_setter.set(assigner);
230 },
231 );
232
233 let onchange_target = use_callback(
234 target.setter(),
235 move |target: (bool, String), target_setter| {
236 target_setter.set(target);
237 },
238 );
239
240 let onchange_permissions = use_callback(
241 permissions.setter(),
242 move |permissions, permissions_setter| {
243 permissions_setter.set(permissions);
244 },
245 );
246
247 let onchange_prohibitions = use_callback(
248 prohibitions.setter(),
249 move |prohibitions, prohibitions_setter| {
250 prohibitions_setter.set(prohibitions);
251 },
252 );
253
254 let onchange_obligations = use_callback(
255 obligations.setter(),
256 move |obligations, obligations_setter| {
257 obligations_setter.set(obligations);
258 },
259 );
260
261 let onchange_profiles = use_callback(profiles.setter(), move |profiles, profiles_setter| {
262 profiles_setter.set(profiles);
263 });
264
265 let onchange_extensible_properties = use_callback(
266 extensible_properties.setter(),
267 move |extensible_properties, extensible_properties_setter| {
268 extensible_properties_setter.set(extensible_properties);
269 },
270 );
271
272 let (target_is_simple, target_value) = (*target).clone();
273
274 let disabled = false;
275
276 let errors = if let Some(error_mesage) = (*creation_errors).clone() {
277 html!(
278 <Alert title="Unable to create the policy" r#type={AlertType::Danger}>{ error_mesage }</Alert>
279 )
280 } else {
281 html!()
282 };
283
284 html!(
285 <Form {onsubmit}>
286 { errors }
287 <FormGroup label="Name" required=true>
288 <TextInput required=true value={(*name).to_string()} onchange={onchange_name} />
289 </FormGroup>
290 <FormGroup label="Permissions">
291 <ListOfRules list={(*permissions).clone()} onchange={onchange_permissions} />
292 </FormGroup>
293 <FormGroup label="Prohibitions">
294 <ListOfRules list={(*prohibitions).clone()} onchange={onchange_prohibitions} />
295 </FormGroup>
296 <FormGroup label="Obligations">
297 <ListOfRules list={(*obligations).clone()} onchange={onchange_obligations} />
298 </FormGroup>
299 <FormGroup label="Profiles">
300 <StringListEdit values={(*profiles).clone()} onchange={onchange_profiles} />
301 </FormGroup>
302 <FormGroup label="Extensible Properties">
303 <ExtensiblePropertiesEdit
304 values={(*extensible_properties).clone()}
305 onchange={onchange_extensible_properties}
306 />
307 </FormGroup>
308 <FormGroup label="Assignee">
309 <TextInput value={(*assignee).clone()} onchange={onchange_assignee} />
310 </FormGroup>
311 <FormGroup label="Assigner">
312 <TextInput value={(*assigner).clone()} onchange={onchange_assigner} />
313 </FormGroup>
314 <FormGroup label="Target">
315 <SimpleOrIdField
316 onchange={onchange_target}
317 is_simple={target_is_simple}
318 value={target_value}
319 />
320 </FormGroup>
321 <ActionGroup>
322 <Button
323 variant={ButtonVariant::Primary}
324 label="Submit"
325 r#type={ButtonType::Submit}
326 {disabled}
327 />
328 <Button variant={ButtonVariant::Secondary} label="Reset" r#type={ButtonType::Reset} />
329 </ActionGroup>
330 </Form>
331 )
332}