1use super::{OrganizationPlugin, handlers, types};
3use crate::plugins::endpoint::{definition, error_response, validation};
4use alibi_core::endpoint::{
5 EndpointCall, EndpointDefinition, EndpointInput, EndpointResponse, ServerEndpoint,
6};
7use alibi_core::entity::{AuthOrganization, AuthUser};
8use alibi_core::session::SessionRequest;
9use alibi_core::types::{Team, TeamMember};
10use alibi_core::utils::json::JsValue;
11use alibi_core::wire::InvitationView;
12use alibi_core::{AuthContext, AuthError, AuthResponse, AuthResult, AuthSchema, HttpMethod};
13
14pub(super) fn definitions() -> Vec<EndpointDefinition> {
15 vec![
16 definition(
17 "listOrganizations",
18 "listOrganizations",
19 Some("/organization/list"),
20 HttpMethod::Get,
21 ),
22 definition(
23 "updateOrganization",
24 "updateOrganization",
25 Some("/organization/update"),
26 HttpMethod::Post,
27 ),
28 definition(
29 "getOrganization",
30 "getOrganization",
31 Some("/organization/get-organization"),
32 HttpMethod::Get,
33 ),
34 definition(
35 "getFullOrganization",
36 "getFullOrganization",
37 Some("/organization/get-full-organization"),
38 HttpMethod::Get,
39 ),
40 definition(
41 "checkOrganizationSlug",
42 "checkOrganizationSlug",
43 Some("/organization/check-slug"),
44 HttpMethod::Post,
45 ),
46 definition(
47 "setActiveOrganization",
48 "setActiveOrganization",
49 Some("/organization/set-active"),
50 HttpMethod::Post,
51 ),
52 definition(
53 "leaveOrganization",
54 "leaveOrganization",
55 Some("/organization/leave"),
56 HttpMethod::Post,
57 ),
58 definition(
59 "inviteMember",
60 "inviteMember",
61 Some("/organization/invite-member"),
62 HttpMethod::Post,
63 ),
64 definition(
65 "getInvitation",
66 "getInvitation",
67 Some("/organization/get-invitation"),
68 HttpMethod::Get,
69 ),
70 definition(
71 "listInvitations",
72 "listInvitations",
73 Some("/organization/list-invitations"),
74 HttpMethod::Get,
75 ),
76 definition(
77 "listUserInvitations",
78 "listUserInvitations",
79 Some("/organization/list-user-invitations"),
80 HttpMethod::Get,
81 ),
82 definition(
83 "acceptInvitation",
84 "acceptInvitation",
85 Some("/organization/accept-invitation"),
86 HttpMethod::Post,
87 ),
88 definition(
89 "rejectInvitation",
90 "rejectInvitation",
91 Some("/organization/reject-invitation"),
92 HttpMethod::Post,
93 ),
94 definition(
95 "cancelInvitation",
96 "cancelInvitation",
97 Some("/organization/cancel-invitation"),
98 HttpMethod::Post,
99 ),
100 definition(
101 "getActiveMember",
102 "getActiveMember",
103 Some("/organization/get-active-member"),
104 HttpMethod::Get,
105 ),
106 definition(
107 "getActiveMemberRole",
108 "getActiveMemberRole",
109 Some("/organization/get-active-member-role"),
110 HttpMethod::Get,
111 ),
112 definition(
113 "listMembers",
114 "listMembers",
115 Some("/organization/list-members"),
116 HttpMethod::Get,
117 ),
118 definition(
119 "updateMemberRole",
120 "updateMemberRole",
121 Some("/organization/update-member-role"),
122 HttpMethod::Post,
123 ),
124 definition(
125 "hasPermission",
126 "hasPermission",
127 Some("/organization/has-permission"),
128 HttpMethod::Post,
129 ),
130 definition(
131 "createTeam",
132 "createTeam",
133 Some("/organization/create-team"),
134 HttpMethod::Post,
135 ),
136 definition(
137 "updateTeam",
138 "updateTeam",
139 Some("/organization/update-team"),
140 HttpMethod::Post,
141 ),
142 definition(
143 "removeTeam",
144 "removeTeam",
145 Some("/organization/remove-team"),
146 HttpMethod::Post,
147 ),
148 definition(
149 "setActiveTeam",
150 "setActiveTeam",
151 Some("/organization/set-active-team"),
152 HttpMethod::Post,
153 ),
154 definition(
155 "addTeamMember",
156 "addTeamMember",
157 Some("/organization/add-team-member"),
158 HttpMethod::Post,
159 ),
160 definition(
161 "removeTeamMember",
162 "removeTeamMember",
163 Some("/organization/remove-team-member"),
164 HttpMethod::Post,
165 ),
166 definition(
167 "listTeams",
168 "listTeams",
169 Some("/organization/list-teams"),
170 HttpMethod::Get,
171 ),
172 definition(
173 "listUserTeams",
174 "listUserTeams",
175 Some("/organization/list-user-teams"),
176 HttpMethod::Get,
177 ),
178 definition(
179 "listTeamMembers",
180 "listTeamMembers",
181 Some("/organization/list-team-members"),
182 HttpMethod::Get,
183 ),
184 definition(
185 "createOrganizationRole",
186 "createOrganizationRole",
187 Some("/organization/create-role"),
188 HttpMethod::Post,
189 ),
190 definition(
191 "updateOrganizationRole",
192 "updateOrganizationRole",
193 Some("/organization/update-role"),
194 HttpMethod::Post,
195 ),
196 definition(
197 "deleteOrganizationRole",
198 "deleteOrganizationRole",
199 Some("/organization/delete-role"),
200 HttpMethod::Post,
201 ),
202 definition(
203 "getOrganizationRole",
204 "getOrganizationRole",
205 Some("/organization/get-role"),
206 HttpMethod::Get,
207 ),
208 definition(
209 "listOrganizationRoles",
210 "listOrganizationRoles",
211 Some("/organization/list-roles"),
212 HttpMethod::Get,
213 ),
214 ]
215}
216
217pub(super) fn validate(call: &EndpointCall) -> AuthResult<EndpointInput> {
218 match call.operation_id() {
220 "updateOrganization" => {
221 drop(handlers::org_input::update_value(call.body().cloned()).map_err(error_response)?);
222 }
223 "getOrganization" => {
224 let _: types::GetOrganizationQuery = call
225 .query_as()
226 .map_err(|error| validation(error.to_string()))?;
227 }
228 "getFullOrganization" => {
229 let _: types::GetFullOrganizationQuery = call
230 .query_as()
231 .map_err(|error| validation(error.to_string()))?;
232 }
233 "checkOrganizationSlug" => {
234 let _: types::CheckSlugRequest = call
235 .body_as()
236 .map_err(|error| validation(error.to_string()))?;
237 }
238 "setActiveOrganization" => {
239 drop(
240 handlers::org_input::set_active_value(call.body().cloned())
241 .map_err(error_response)?,
242 );
243 }
244 "leaveOrganization" => {
245 let _: types::LeaveOrganizationRequest = call
246 .body_as()
247 .map_err(|error| validation(error.to_string()))?;
248 }
249 "inviteMember" => {
250 drop(
251 handlers::org_input::invitation_create_value(call.body().cloned())
252 .map_err(error_response)?,
253 );
254 }
255 "getInvitation" => {
256 let _: types::GetInvitationQuery = call
257 .query_as()
258 .map_err(|error| validation(error.to_string()))?;
259 }
260 "listInvitations" => {
261 let _: types::ListInvitationsQuery = call
262 .query_as()
263 .map_err(|error| validation(error.to_string()))?;
264 }
265 "listUserInvitations" => {
266 let _: types::ListUserInvitationsQuery = call
267 .query_as()
268 .map_err(|error| validation(error.to_string()))?;
269 }
270 "acceptInvitation" => {
271 let _: types::AcceptInvitationRequest = call
272 .body_as()
273 .map_err(|error| validation(error.to_string()))?;
274 }
275 "rejectInvitation" => {
276 let _: types::RejectInvitationRequest = call
277 .body_as()
278 .map_err(|error| validation(error.to_string()))?;
279 }
280 "cancelInvitation" => {
281 let _: types::CancelInvitationRequest = call
282 .body_as()
283 .map_err(|error| validation(error.to_string()))?;
284 }
285 "getActiveMemberRole" => {
286 let _: types::GetActiveMemberRoleQuery = call
287 .query_as()
288 .map_err(|error| validation(error.to_string()))?;
289 }
290 "listMembers" => {
291 let _: types::ListMembersQuery = call
292 .query_as()
293 .map_err(|error| validation(error.to_string()))?;
294 }
295 "updateMemberRole" => {
296 drop(
297 handlers::org_input::member_role_update_value(call.body().cloned())
298 .map_err(error_response)?,
299 );
300 }
301 "hasPermission" => {
302 let _: types::HasPermissionRequest = call
303 .body_as()
304 .map_err(|error| validation(error.to_string()))?;
305 }
306 "createTeam" => {
307 let _: types::CreateTeamRequest = call
308 .body_as()
309 .map_err(|error| validation(error.to_string()))?;
310 }
311 "updateTeam" => {
312 let _: types::UpdateTeamRequest = call
313 .body_as()
314 .map_err(|error| validation(error.to_string()))?;
315 }
316 "removeTeam" => {
317 let _: types::RemoveTeamRequest = call
318 .body_as()
319 .map_err(|error| validation(error.to_string()))?;
320 }
321 "setActiveTeam" => {
322 let _: types::SetActiveTeamRequest = call
323 .body_as()
324 .map_err(|error| validation(error.to_string()))?;
325 }
326 "addTeamMember" => {
327 let _: types::TeamMemberRequest = call
328 .body_as()
329 .map_err(|error| validation(error.to_string()))?;
330 }
331 "removeTeamMember" => {
332 let _: types::TeamMemberRequest = call
333 .body_as()
334 .map_err(|error| validation(error.to_string()))?;
335 }
336 "listTeams" => {
337 let _: types::TeamQuery = call
338 .query_as()
339 .map_err(|error| validation(error.to_string()))?;
340 }
341 "listUserTeams" => {
342 let _: types::TeamQuery = call
343 .query_as()
344 .map_err(|error| validation(error.to_string()))?;
345 }
346 "listTeamMembers" => {
347 let _: types::TeamQuery = call
348 .query_as()
349 .map_err(|error| validation(error.to_string()))?;
350 }
351 "createOrganizationRole" => {
352 let _: types::CreateRoleRequest = call
353 .body_as()
354 .map_err(|error| validation(error.to_string()))?;
355 }
356 "updateOrganizationRole" => {
357 let _: types::UpdateRoleRequest = call
358 .body_as()
359 .map_err(|error| validation(error.to_string()))?;
360 }
361 "deleteOrganizationRole" => {
362 let _: types::RoleQuery = call
363 .body_as()
364 .map_err(|error| validation(error.to_string()))?;
365 }
366 "getOrganizationRole" => {
367 let _: types::RoleQuery = call
368 .query_as()
369 .map_err(|error| validation(error.to_string()))?;
370 }
371 "listOrganizationRoles" => {
372 let _: types::RoleQuery = call
373 .query_as()
374 .map_err(|error| validation(error.to_string()))?;
375 }
376 _ => {}
377 }
378 Ok(EndpointInput {
379 body: call.body().cloned(),
380 query: call.query().cloned(),
381 })
382}
383
384impl OrganizationPlugin {
385 #[must_use]
389 pub fn list_organizations_endpoint() -> ServerEndpoint<Vec<types::OrganizationResponse>> {
390 ServerEndpoint::new("organization", "listOrganizations")
391 }
392 pub fn update_endpoint(
396 input: &types::UpdateOrganizationRequest,
397 ) -> AuthResult<ServerEndpoint<Option<types::CreatedOrganizationResponse>>> {
398 ServerEndpoint::new("organization", "updateOrganization").with_body(input)
399 }
400 pub fn get_organization_endpoint(
404 input: &types::GetOrganizationQuery,
405 ) -> AuthResult<ServerEndpoint<Option<types::OrganizationResponse>>> {
406 ServerEndpoint::new("organization", "getOrganization").with_query(input)
407 }
408 pub fn get_full_organization_endpoint(
412 input: &types::GetFullOrganizationQuery,
413 ) -> AuthResult<
414 ServerEndpoint<
415 Option<types::FullOrganizationResponse<types::OrganizationResponse, InvitationView>>,
416 >,
417 > {
418 ServerEndpoint::new("organization", "getFullOrganization").with_query(input)
419 }
420 pub fn check_slug_endpoint(
424 input: &types::CheckSlugRequest,
425 ) -> AuthResult<ServerEndpoint<types::CheckSlugResponse>> {
426 ServerEndpoint::new("organization", "checkOrganizationSlug").with_body(input)
427 }
428 pub fn set_active_endpoint(
432 input: &types::SetActiveOrganizationRequest,
433 ) -> AuthResult<ServerEndpoint<Option<types::OrganizationResponse>>> {
434 ServerEndpoint::new("organization", "setActiveOrganization").with_body(input)
435 }
436 pub fn leave_endpoint(
440 input: &types::LeaveOrganizationRequest,
441 ) -> AuthResult<ServerEndpoint<types::SuccessResponse>> {
442 ServerEndpoint::new("organization", "leaveOrganization").with_body(input)
443 }
444 pub fn invite_member_endpoint(
448 input: &types::InviteMemberRequest,
449 ) -> AuthResult<ServerEndpoint<InvitationView>> {
450 ServerEndpoint::new("organization", "inviteMember").with_body(input)
451 }
452 pub fn get_invitation_endpoint(
456 input: &types::GetInvitationQuery,
457 ) -> AuthResult<ServerEndpoint<types::GetInvitationResponse<InvitationView>>> {
458 ServerEndpoint::new("organization", "getInvitation").with_query(input)
459 }
460 pub fn list_invitations_endpoint(
464 input: &types::ListInvitationsQuery,
465 ) -> AuthResult<ServerEndpoint<Vec<InvitationView>>> {
466 ServerEndpoint::new("organization", "listInvitations").with_query(input)
467 }
468 pub fn list_user_invitations_endpoint(
472 input: &types::ListUserInvitationsQuery,
473 ) -> AuthResult<ServerEndpoint<Vec<types::UserInvitationResponse<InvitationView>>>> {
474 ServerEndpoint::new("organization", "listUserInvitations").with_query(input)
475 }
476 pub fn accept_invitation_endpoint(
480 input: &types::AcceptInvitationRequest,
481 ) -> AuthResult<
482 ServerEndpoint<types::AcceptInvitationResponse<InvitationView, types::BasicMemberResponse>>,
483 > {
484 ServerEndpoint::new("organization", "acceptInvitation").with_body(input)
485 }
486 pub fn reject_invitation_endpoint(
490 input: &types::RejectInvitationRequest,
491 ) -> AuthResult<
492 ServerEndpoint<
493 types::AcceptInvitationResponse<InvitationView, Option<types::BasicMemberResponse>>,
494 >,
495 > {
496 ServerEndpoint::new("organization", "rejectInvitation").with_body(input)
497 }
498 pub fn cancel_invitation_endpoint(
502 input: &types::CancelInvitationRequest,
503 ) -> AuthResult<ServerEndpoint<InvitationView>> {
504 ServerEndpoint::new("organization", "cancelInvitation").with_body(input)
505 }
506 #[must_use]
510 pub fn get_active_member_endpoint() -> ServerEndpoint<types::MemberResponse> {
511 ServerEndpoint::new("organization", "getActiveMember")
512 }
513 pub fn get_active_member_role_endpoint(
517 input: &types::GetActiveMemberRoleQuery,
518 ) -> AuthResult<ServerEndpoint<types::GetActiveMemberRoleResponse>> {
519 ServerEndpoint::new("organization", "getActiveMemberRole").with_query(input)
520 }
521 pub fn list_members_endpoint(
525 input: &types::ListMembersQuery,
526 ) -> AuthResult<ServerEndpoint<types::ListMembersResponse>> {
527 ServerEndpoint::new("organization", "listMembers").with_query(input)
528 }
529 pub fn update_member_role_endpoint(
533 input: &types::UpdateMemberRoleRequest,
534 ) -> AuthResult<ServerEndpoint<types::BasicMemberResponse>> {
535 ServerEndpoint::new("organization", "updateMemberRole").with_body(input)
536 }
537 pub fn has_permission_endpoint(
541 input: &types::HasPermissionRequest,
542 ) -> AuthResult<ServerEndpoint<types::HasPermissionResponse>> {
543 ServerEndpoint::new("organization", "hasPermission").with_body(input)
544 }
545 pub fn create_team_endpoint(
549 input: &types::CreateTeamRequest,
550 ) -> AuthResult<ServerEndpoint<Team>> {
551 ServerEndpoint::new("organization", "createTeam").with_body(input)
552 }
553 pub fn update_team_endpoint(
557 input: &types::UpdateTeamRequest,
558 ) -> AuthResult<ServerEndpoint<Team>> {
559 ServerEndpoint::new("organization", "updateTeam").with_body(input)
560 }
561 pub fn remove_team_endpoint(
565 input: &types::RemoveTeamRequest,
566 ) -> AuthResult<ServerEndpoint<types::MessageResponse>> {
567 ServerEndpoint::new("organization", "removeTeam").with_body(input)
568 }
569 pub fn set_active_team_endpoint(
573 input: &types::SetActiveTeamRequest,
574 ) -> AuthResult<ServerEndpoint<Option<Team>>> {
575 ServerEndpoint::new("organization", "setActiveTeam").with_body(input)
576 }
577 pub fn add_team_member_endpoint(
581 input: &types::TeamMemberRequest,
582 ) -> AuthResult<ServerEndpoint<TeamMember>> {
583 ServerEndpoint::new("organization", "addTeamMember").with_body(input)
584 }
585 pub fn remove_team_member_endpoint(
589 input: &types::TeamMemberRequest,
590 ) -> AuthResult<ServerEndpoint<types::MessageResponse>> {
591 ServerEndpoint::new("organization", "removeTeamMember").with_body(input)
592 }
593 pub fn list_teams_endpoint(input: &types::TeamQuery) -> AuthResult<ServerEndpoint<Vec<Team>>> {
597 ServerEndpoint::new("organization", "listTeams").with_query(input)
598 }
599 pub fn list_user_teams_endpoint(
603 input: &types::TeamQuery,
604 ) -> AuthResult<ServerEndpoint<Vec<Team>>> {
605 ServerEndpoint::new("organization", "listUserTeams").with_query(input)
606 }
607 pub fn list_team_members_endpoint(
611 input: &types::TeamQuery,
612 ) -> AuthResult<ServerEndpoint<Vec<TeamMember>>> {
613 ServerEndpoint::new("organization", "listTeamMembers").with_query(input)
614 }
615 pub fn create_role_endpoint(
619 input: &types::CreateRoleRequest,
620 ) -> AuthResult<ServerEndpoint<types::CreateRoleResponse>> {
621 ServerEndpoint::new("organization", "createOrganizationRole").with_body(input)
622 }
623 pub fn update_role_endpoint(
627 input: &types::UpdateRoleRequest,
628 ) -> AuthResult<ServerEndpoint<types::UpdateRoleResponse>> {
629 ServerEndpoint::new("organization", "updateOrganizationRole").with_body(input)
630 }
631 pub fn delete_role_endpoint(
635 input: &types::RoleQuery,
636 ) -> AuthResult<ServerEndpoint<types::SuccessResponse>> {
637 ServerEndpoint::new("organization", "deleteOrganizationRole").with_body(input)
638 }
639 pub fn get_role_endpoint(
643 input: &types::RoleQuery,
644 ) -> AuthResult<ServerEndpoint<types::RoleResponse>> {
645 ServerEndpoint::new("organization", "getOrganizationRole").with_query(input)
646 }
647 pub fn list_roles_endpoint(
651 input: &types::RoleQuery,
652 ) -> AuthResult<ServerEndpoint<Vec<types::RoleResponse>>> {
653 ServerEndpoint::new("organization", "listOrganizationRoles").with_query(input)
654 }
655}
656
657fn response(output: AuthResponse) -> AuthResult<EndpointResponse> {
658 let value = if output.body.is_empty() {
659 JsValue::Null
660 } else {
661 alibi_core::utils::json::from_slice::<JsValue>(&output.body)?
662 };
663 let mut response = if output.status >= 400 {
664 EndpointResponse::error(error_response(output.clone())).with_error_body(value)
665 } else {
666 EndpointResponse::value(value)
667 };
668 response.set_status(Some(output.status));
669 response.merge_headers(output.headers);
670 Ok(response)
671}
672
673fn query_pairs(call: &EndpointCall) -> AuthResult<std::collections::HashMap<String, String>> {
674 call.query()
675 .and_then(JsValue::as_object)
676 .map(|fields| {
677 fields
678 .iter()
679 .map(|(key, value)| {
680 value
681 .coerce_string()
682 .map(|value| (key.clone(), value))
683 .map_err(validation)
684 })
685 .collect()
686 })
687 .unwrap_or_else(|| Ok(Default::default()))
688}
689
690pub(super) async fn execute<S: AuthSchema>(
691 plugin: &OrganizationPlugin,
692 call: &EndpointCall,
693 ctx: &AuthContext<S>,
694) -> AuthResult<EndpointResponse> {
695 let config = &plugin.config;
696 if call.operation_id() == "listUserInvitations" {
697 let query: types::ListUserInvitationsQuery = call.query_as()?;
698 if let Some(email) = query.email.as_deref().filter(|email| !email.is_empty()) {
699 if call.request().is_some() {
700 return Err(AuthError::bad_request(
701 "User email cannot be passed for client side API calls.",
702 ));
703 }
704 return EndpointResponse::json(
705 &handlers::invitation::list_user_invitations_for_email_core(email, ctx).await?,
706 );
707 }
708 }
709 let (user, session) = ctx.require_cached_session(call).await.map_err(|error| {
710 if matches!(
711 error,
712 AuthError::Unauthenticated | AuthError::SessionNotFound
713 ) {
714 handlers::extension_common::org_error(401, "UNAUTHORIZED")
715 } else {
716 error
717 }
718 })?;
719 match call.operation_id() {
720 "listOrganizations" => {
721 EndpointResponse::json(&handlers::org::list_organizations_core(&user, ctx).await?)
722 }
723 "updateOrganization" => {
724 let (body, metadata) =
725 handlers::org_input::update_value(call.body().cloned()).map_err(error_response)?;
726 EndpointResponse::json(
727 &handlers::org::update_organization_core(
728 &body, metadata, &user, &session, config, ctx,
729 )
730 .await?,
731 )
732 }
733 "getOrganization" => {
734 let query: types::GetOrganizationQuery = call.query_as()?;
735 let organization =
736 if let Some(slug) = query.organization_slug.as_deref().filter(|s| !s.is_empty()) {
737 ctx.database.get_organization_by_slug(slug).await?
738 } else if let Some(id) = query
739 .organization_id
740 .as_deref()
741 .filter(|s| !s.is_empty())
742 .or(session.active_organization_id.as_deref())
743 {
744 ctx.database.get_organization_by_id(id).await?
745 } else {
746 return EndpointResponse::json(&Option::<types::OrganizationResponse>::None);
747 }
748 .ok_or_else(|| AuthError::bad_request("Organization not found"))?;
749 if ctx
750 .database
751 .get_member(&organization.id(), &user.id())
752 .await?
753 .is_none()
754 {
755 drop(
756 ctx.database
757 .update_session_active_organization_record(&session.token, None)
758 .await?,
759 );
760 return Err(AuthError::forbidden(
761 "User is not a member of the organization",
762 ));
763 }
764 EndpointResponse::json(&types::OrganizationResponse::from_stored_organization(
765 &organization,
766 )?)
767 }
768 "getFullOrganization" => EndpointResponse::json(
769 &handlers::org::get_full_organization_core(
770 &call.query_as()?,
771 &user,
772 &session,
773 config,
774 ctx,
775 )
776 .await
777 .map_err(AuthError::from)?,
778 ),
779 "checkOrganizationSlug" => {
780 EndpointResponse::json(&handlers::org::check_slug_core(&call.body_as()?, ctx).await?)
781 }
782 "setActiveOrganization" => {
783 let body = handlers::org_input::set_active_value(call.body().cloned())
784 .map_err(error_response)?;
785 let organization =
786 handlers::org::set_active_organization_core(&body, &user, &session, ctx).await?;
787 if organization.is_some()
788 || session
789 .active_organization_id
790 .as_deref()
791 .is_some_and(|id| !id.is_empty())
792 {
793 use alibi_core::utils::cookie_utils::{
794 create_session_cookie_with_max_age, create_session_like_cookie,
795 related_cookie_name, sign_cookie_value, verify_cookie_value,
796 };
797 let preference = related_cookie_name(&ctx.config, "dont_remember");
798 let dont_remember = call.session_headers().get("cookie").is_some_and(|header| {
799 cookie::Cookie::split_parse(header)
800 .flatten()
801 .find(|cookie| cookie.name() == preference)
802 .and_then(|cookie| {
803 verify_cookie_value(cookie.value(), ctx.config.current_secret())
804 })
805 .is_some_and(|value| !value.is_empty())
806 });
807 call.queue_response_header(
808 "set-cookie",
809 create_session_cookie_with_max_age(
810 Some(&session.token),
811 (!dont_remember).then(|| ctx.config.session.expires_in.num_seconds()),
812 &ctx.config,
813 )?,
814 );
815 if dont_remember {
816 call.queue_response_header(
817 "set-cookie",
818 create_session_like_cookie(
819 &preference,
820 &sign_cookie_value("true", ctx.config.current_secret()),
821 None,
822 &ctx.config,
823 )?,
824 );
825 }
826 }
827 EndpointResponse::json(&organization)
828 }
829 "leaveOrganization" => EndpointResponse::json(
830 &handlers::org::leave_organization_core(&call.body_as()?, &user, &session, config, ctx)
831 .await?,
832 ),
833 "inviteMember" => {
834 let body = handlers::org_input::invitation_create_value(call.body().cloned())
835 .map_err(error_response)?;
836 EndpointResponse::json(
837 &handlers::invitation::invite_member_core(&body, &user, &session, config, ctx)
838 .await?,
839 )
840 }
841 "getInvitation" => EndpointResponse::json(
842 &handlers::invitation::get_invitation_core(&call.query_as()?, &user, config, ctx)
843 .await?,
844 ),
845 "listInvitations" => EndpointResponse::json(
846 &handlers::invitation::list_invitations_core(&call.query_as()?, &user, &session, ctx)
847 .await?,
848 ),
849 "listUserInvitations" => EndpointResponse::json(
850 &handlers::invitation::list_user_invitations_core(&user, ctx).await?,
851 ),
852 "acceptInvitation" => {
853 let transport = handlers::invitation_acceptance::AcceptanceTransport::native(call);
854 let result = handlers::invitation_acceptance::accept(
855 &call.body_as()?,
856 &user,
857 &session,
858 config,
859 ctx,
860 &transport,
861 )
862 .await;
863 if matches!(result, Err(AuthError::Internal(_) | AuthError::Database(_))) {
864 transport.discard_cookies();
865 }
866 EndpointResponse::json(&result?)
867 }
868 "rejectInvitation" => EndpointResponse::json(
869 &handlers::invitation::reject_invitation_core(&call.body_as()?, &user, config, ctx)
870 .await?,
871 ),
872 "cancelInvitation" => EndpointResponse::json(
873 &handlers::invitation::cancel_invitation_core(&call.body_as()?, &user, config, ctx)
874 .await?,
875 ),
876 "getActiveMember" => EndpointResponse::json(
877 &handlers::member::get_active_member_core(&user, &session, ctx).await?,
878 ),
879 "getActiveMemberRole" => EndpointResponse::json(
880 &handlers::member::get_active_member_role_core(&call.query_as()?, &user, &session, ctx)
881 .await?,
882 ),
883 "listMembers" => EndpointResponse::json(
884 &handlers::member::list_members_core(&call.query_as()?, &user, &session, config, ctx)
885 .await
886 .map_err(AuthError::from)?,
887 ),
888 "updateMemberRole" => {
889 let body = handlers::org_input::member_role_update_value(call.body().cloned())
890 .map_err(error_response)?;
891 response(
892 handlers::member::update_member_role_response(&body, &user, &session, config, ctx)
893 .await?,
894 )
895 }
896 "hasPermission" => EndpointResponse::json(
897 &handlers::has_permission_core(&call.body_as()?, &user, &session, config, ctx).await?,
898 ),
899 "createTeam" | "updateTeam" | "removeTeam" | "setActiveTeam" | "addTeamMember"
900 | "removeTeamMember" | "listTeams" | "listUserTeams" | "listTeamMembers" => {
901 let path = match call.operation_id() {
902 "createTeam" => "/organization/create-team",
903 "updateTeam" => "/organization/update-team",
904 "removeTeam" => "/organization/remove-team",
905 "setActiveTeam" => "/organization/set-active-team",
906 "addTeamMember" => "/organization/add-team-member",
907 "removeTeamMember" => "/organization/remove-team-member",
908 "listTeams" => "/organization/list-teams",
909 "listUserTeams" => "/organization/list-user-teams",
910 "listTeamMembers" => "/organization/list-team-members",
911 _ => return Err(AuthError::not_found("Unknown operation")),
912 };
913 let method = call.default_method();
914 let query = query_pairs(call)?;
915 let body = call
916 .body()
917 .map(JsValue::to_json_value)
918 .transpose()
919 .map_err(|error| validation(error.to_string()))?;
920 let result = handlers::team::team_core(
921 method,
922 path,
923 body,
924 &query,
925 user,
926 session,
927 call.request(),
928 false,
929 ctx,
930 config,
931 )
932 .await?;
933 response(result.ok_or_else(|| AuthError::not_found("Unknown operation"))?)
934 }
935 "createOrganizationRole"
936 | "updateOrganizationRole"
937 | "deleteOrganizationRole"
938 | "getOrganizationRole"
939 | "listOrganizationRoles" => {
940 let path = match call.operation_id() {
941 "createOrganizationRole" => "/organization/create-role",
942 "updateOrganizationRole" => "/organization/update-role",
943 "deleteOrganizationRole" => "/organization/delete-role",
944 "getOrganizationRole" => "/organization/get-role",
945 "listOrganizationRoles" => "/organization/list-roles",
946 _ => return Err(AuthError::not_found("Unknown operation")),
947 };
948 let method = call.default_method();
949 let query = query_pairs(call)?;
950 let body = call
951 .body()
952 .map(JsValue::to_json_value)
953 .transpose()
954 .map_err(|error| validation(error.to_string()))?
955 .and_then(|value| value.as_object().cloned())
956 .unwrap_or_default();
957 let result =
958 handlers::role::role_core(method, path, body, &query, call, ctx, config).await?;
959 response(result.ok_or_else(|| AuthError::not_found("Unknown operation"))?)
960 }
961 _ => Err(AuthError::not_found("Unregistered organization operation")),
962 }
963}