Bug: fix(admin): jwt added for organization update and retrieve



Previously, updating and retrieving organization details required authentication using the admin API key. With the recent change, the dashboard JWT can now be used to update the org_name directly through the dashboard.
diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs
index e5a18fba536..78238d3af07 100644
--- a/crates/router/src/routes/admin.rs
+++ b/crates/router/src/routes/admin.rs
@@ -39,14 +39,24 @@ pub async fn organization_update(
 ) -> HttpResponse {
     let flow = Flow::OrganizationUpdate;
     let organization_id = org_id.into_inner();
-    let org_id = admin::OrganizationId { organization_id };
+    let org_id = admin::OrganizationId {
+        organization_id: organization_id.clone(),
+    };
     Box::pin(api::server_wrap(
         flow,
         state,
         &req,
         json_payload.into_inner(),
         |state, _, req, _| update_organization(state, org_id.clone(), req),
-        &auth::AdminApiAuth,
+        auth::auth_type(
+            &auth::AdminApiAuth,
+            &auth::JWTAuthOrganizationFromRoute {
+                organization_id,
+                required_permission: Permission::MerchantAccountWrite,
+                minimum_entity_level: EntityType::Organization,
+            },
+            req.headers(),
+        ),
         api_locking::LockAction::NotApplicable,
     ))
     .await
@@ -61,14 +71,25 @@ pub async fn organization_retrieve(
 ) -> HttpResponse {
     let flow = Flow::OrganizationRetrieve;
     let organization_id = org_id.into_inner();
-    let payload = admin::OrganizationId { organization_id };
+    let payload = admin::OrganizationId {
+        organization_id: organization_id.clone(),
+    };
+
     Box::pin(api::server_wrap(
         flow,
         state,
         &req,
         payload,
         |state, _, req, _| get_organization(state, req),
-        &auth::AdminApiAuth,
+        auth::auth_type(
+            &auth::AdminApiAuth,
+            &auth::JWTAuthOrganizationFromRoute {
+                organization_id,
+                required_permission: Permission::MerchantAccountRead,
+                minimum_entity_level: EntityType::Organization,
+            },
+            req.headers(),
+        ),
         api_locking::LockAction::NotApplicable,
     ))
     .await
diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs
index d8318ce9517..225d6a783fc 100644
--- a/crates/router/src/services/authentication.rs
+++ b/crates/router/src/services/authentication.rs
@@ -88,6 +88,10 @@ pub enum AuthenticationType {
     AdminApiAuthWithMerchantId {
         merchant_id: id_type::MerchantId,
     },
+    OrganizationJwt {
+        org_id: id_type::OrganizationId,
+        user_id: String,
+    },
     MerchantJwt {
         merchant_id: id_type::MerchantId,
         user_id: Option<String>,
@@ -149,6 +153,7 @@ impl AuthenticationType {
             | Self::MerchantJwtWithProfileId { merchant_id, .. }
             | Self::WebhookAuth { merchant_id } => Some(merchant_id),
             Self::AdminApiKey
+            | Self::OrganizationJwt { .. }
             | Self::UserJwt { .. }
             | Self::SinglePurposeJwt { .. }
             | Self::SinglePurposeOrLoginJwt { .. }
@@ -1134,6 +1139,45 @@ where
     }
 }
 
+pub struct JWTAuthOrganizationFromRoute {
+    pub organization_id: id_type::OrganizationId,
+    pub required_permission: Permission,
+    pub minimum_entity_level: EntityType,
+}
+
+#[async_trait]
+impl<A> AuthenticateAndFetch<(), A> for JWTAuthOrganizationFromRoute
+where
+    A: SessionStateInfo + Sync,
+{
+    async fn authenticate_and_fetch(
+        &self,
+        request_headers: &HeaderMap,
+        state: &A,
+    ) -> RouterResult<((), AuthenticationType)> {
+        let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?;
+        if payload.check_in_blacklist(state).await? {
+            return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
+        }
+
+        let role_info = authorization::get_role_info(state, &payload).await?;
+        authorization::check_permission(&self.required_permission, &role_info)?;
+        authorization::check_entity(self.minimum_entity_level, &role_info)?;
+
+        // Check if token has access to Organization that has been requested in the route
+        if payload.org_id != self.organization_id {
+            return Err(report!(errors::ApiErrorResponse::InvalidJwtToken));
+        }
+        Ok((
+            (),
+            AuthenticationType::OrganizationJwt {
+                org_id: payload.org_id,
+                user_id: payload.user_id,
+            },
+        ))
+    }
+}
+
 pub struct JWTAuthMerchantFromRoute {
     pub merchant_id: id_type::MerchantId,
     pub required_permission: Permission,

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [X] Bugfix
- [ ] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
Previously, updating and retrieving organization details required authentication using the admin API key. With the recent change, the dashboard JWT can now be used to update the org_name directly through the dashboard.

### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->
Closes #6141


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
Request : 
```
curl --location --request PUT 'http://localhost:8080/organization/${org_id}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer JWT' \
--data '{
    "organization_name":"RiddhiTest3"
}'
```

Response:
```
{
    "organization_id": "org_id",
    "organization_name": "RiddhiTest3",
    "organization_details": null,
    "metadata": null,
    "modified_at": "2024-09-27 07:13:13.074956",
    "created_at": "2024-09-19 12:46:25.990045"
}
```


Request :
```
curl --location 'http://localhost:8080/organization/${org_id}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer JWT' \
--data ''
```

Response:
```
{
    "organization_id": "org_id",
    "organization_name": "RiddhiTest3",
    "organization_details": null,
    "metadata": null,
    "modified_at": "2024-09-27 07:13:13.074956",
    "created_at": "2024-09-19 12:46:25.990045"
}
```
## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [ ] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
