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