Skip to content

Commit dc9f305

Browse files
fix(services): permission queries (#1080)
* Filter out hidden providers only in root collection * Adjust view of user permitted resources; Ensure unique provider permissions * Update column name * Test priority filter * Test dataset deletion with owner and read permission
1 parent 1b81258 commit dc9f305

File tree

9 files changed

+411
-29
lines changed

9 files changed

+411
-29
lines changed

services/src/api/handlers/datasets.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1383,7 +1383,7 @@ mod tests {
13831383
use crate::ge_context;
13841384
use crate::projects::{PointSymbology, RasterSymbology, Symbology};
13851385
use crate::test_data;
1386-
use crate::users::UserAuth;
1386+
use crate::users::{RoleDb, UserAuth};
13871387
use crate::util::tests::admin_login;
13881388
use crate::util::tests::{
13891389
MockQueryContext, SetMultipartBody, TestDataUploads, add_file_definition_to_datasets,
@@ -3059,6 +3059,60 @@ mod tests {
30593059
Ok(())
30603060
}
30613061

3062+
#[ge_context::test]
3063+
async fn it_deletes_dataset_with_additional_read_permission(
3064+
app_ctx: PostgresContext<NoTls>,
3065+
) -> Result<()> {
3066+
let mut test_data = TestDataUploads::default(); // remember created folder and remove them on drop
3067+
3068+
let session = app_ctx.create_anonymous_session().await.unwrap();
3069+
let session_id = session.id();
3070+
let ctx = app_ctx.session_context(session);
3071+
3072+
let upload_id = upload_ne_10m_ports_files(app_ctx.clone(), session_id).await?;
3073+
test_data.uploads.push(upload_id);
3074+
3075+
let dataset_name =
3076+
construct_dataset_from_upload(app_ctx.clone(), upload_id, session_id).await;
3077+
3078+
let db = ctx.db();
3079+
let dataset_id = db
3080+
.resolve_dataset_name_to_id(&dataset_name)
3081+
.await
3082+
.unwrap()
3083+
.unwrap();
3084+
3085+
assert!(db.load_dataset(&dataset_id).await.is_ok());
3086+
3087+
let admin_session = admin_login(&app_ctx).await;
3088+
let admin_ctx = app_ctx.session_context(admin_session);
3089+
let admin_db = admin_ctx.db();
3090+
3091+
let role_id = admin_db.add_role("test_role").await.unwrap();
3092+
admin_db
3093+
.assign_role(&role_id, &ctx.session().user.id)
3094+
.await
3095+
.unwrap();
3096+
3097+
db.add_permission(role_id, dataset_id, Permission::Read)
3098+
.await
3099+
.unwrap();
3100+
3101+
let req = actix_web::test::TestRequest::delete()
3102+
.uri(&format!("/dataset/{dataset_name}"))
3103+
.append_header((header::CONTENT_LENGTH, 0))
3104+
.append_header((header::AUTHORIZATION, Bearer::new(session_id.to_string())))
3105+
.append_header((header::CONTENT_TYPE, "application/json"));
3106+
3107+
let res = send_test_request(req, app_ctx.clone()).await;
3108+
3109+
assert_eq!(res.status(), 200, "response: {res:?}");
3110+
3111+
assert!(db.load_dataset(&dataset_id).await.is_err());
3112+
3113+
Ok(())
3114+
}
3115+
30623116
#[ge_context::test]
30633117
async fn it_deletes_volume_dataset(app_ctx: PostgresContext<NoTls>) -> Result<()> {
30643118
let volume = VolumeName("test_data".to_string());

services/src/api/handlers/layers.rs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2072,6 +2072,175 @@ mod tests {
20722072
}
20732073
}
20742074

2075+
#[ge_context::test]
2076+
async fn test_list_providers(app_ctx: PostgresContext<NoTls>) {
2077+
let session = admin_login(&app_ctx).await;
2078+
let ctx = app_ctx.session_context(session.clone());
2079+
2080+
let session_id = session.id();
2081+
2082+
let dataset_listing_provider = default_dataset_layer_listing_provider_definition();
2083+
2084+
ctx.db()
2085+
.add_layer_provider(
2086+
crate::layers::external::TypedDataProviderDefinition::DatasetLayerListingProviderDefinition(
2087+
dataset_listing_provider.clone(),
2088+
),
2089+
)
2090+
.await.unwrap();
2091+
2092+
let req = test::TestRequest::get()
2093+
.uri("/layerDb/providers?limit=5&offset=0")
2094+
.append_header((header::AUTHORIZATION, Bearer::new(session_id.to_string())));
2095+
let response = send_test_request(req, app_ctx.clone()).await;
2096+
2097+
assert!(response.status().is_success(), "{response:?}");
2098+
2099+
let response_provider =
2100+
serde_json::from_str::<Vec<LayerProviderListing>>(&read_body_string(response).await)
2101+
.unwrap();
2102+
2103+
let listing = LayerProviderListing {
2104+
id: DataProviderId::from_u128(0xcbb2_1ee3_d15d_45c5_a175_6696_4adf_4e85).into(),
2105+
name: "User Data Listing".to_string(),
2106+
priority: 0,
2107+
};
2108+
2109+
assert_eq!(response_provider, vec![listing.clone()]);
2110+
2111+
let dataset_listing_provider = DatasetLayerListingProviderDefinition {
2112+
priority: Some(-1000),
2113+
..dataset_listing_provider
2114+
};
2115+
2116+
ctx.db()
2117+
.update_layer_provider_definition(
2118+
DataProviderId::from_u128(0xcbb2_1ee3_d15d_45c5_a175_6696_4adf_4e85),
2119+
crate::layers::external::TypedDataProviderDefinition::DatasetLayerListingProviderDefinition(
2120+
dataset_listing_provider,
2121+
),
2122+
)
2123+
.await.unwrap();
2124+
2125+
let req = test::TestRequest::get()
2126+
.uri("/layerDb/providers?limit=5&offset=0")
2127+
.append_header((header::AUTHORIZATION, Bearer::new(session_id.to_string())));
2128+
let response = send_test_request(req, app_ctx.clone()).await;
2129+
2130+
assert!(response.status().is_success(), "{response:?}");
2131+
2132+
let response_provider =
2133+
serde_json::from_str::<Vec<LayerProviderListing>>(&read_body_string(response).await)
2134+
.unwrap();
2135+
2136+
let listing = LayerProviderListing {
2137+
priority: -1000,
2138+
..listing
2139+
};
2140+
2141+
// No filtering for priority is applied here
2142+
assert_eq!(response_provider, vec![listing]);
2143+
}
2144+
2145+
#[ge_context::test]
2146+
async fn test_list_root_collections(app_ctx: PostgresContext<NoTls>) {
2147+
let session = admin_login(&app_ctx).await;
2148+
let ctx = app_ctx.session_context(session.clone());
2149+
2150+
let session_id = session.id();
2151+
2152+
let dataset_listing_provider = default_dataset_layer_listing_provider_definition();
2153+
2154+
ctx.db()
2155+
.add_layer_provider(
2156+
crate::layers::external::TypedDataProviderDefinition::DatasetLayerListingProviderDefinition(
2157+
dataset_listing_provider.clone(),
2158+
),
2159+
)
2160+
.await.unwrap();
2161+
2162+
let req = test::TestRequest::get()
2163+
.uri("/layers/collections?limit=5&offset=0")
2164+
.append_header((header::AUTHORIZATION, Bearer::new(session_id.to_string())));
2165+
let response = send_test_request(req, app_ctx.clone()).await;
2166+
2167+
assert!(response.status().is_success(), "{response:?}");
2168+
2169+
let response_collection =
2170+
serde_json::from_str::<LayerCollection>(&read_body_string(response).await).unwrap();
2171+
2172+
let data_catalog_item = CollectionItem::Collection(LayerCollectionListing {
2173+
r#type: Default::default(),
2174+
id: ProviderLayerCollectionId {
2175+
provider_id: INTERNAL_PROVIDER_ID,
2176+
collection_id: LayerCollectionId(
2177+
crate::layers::storage::INTERNAL_LAYER_DB_ROOT_COLLECTION_ID.to_string(),
2178+
),
2179+
},
2180+
name: "Data Catalog".to_string(),
2181+
description: "Catalog of data and workflows".to_string(),
2182+
properties: vec![],
2183+
});
2184+
2185+
let collection = LayerCollection {
2186+
id: ProviderLayerCollectionId {
2187+
provider_id: ROOT_PROVIDER_ID,
2188+
collection_id: LayerCollectionId(ROOT_COLLECTION_ID.to_string()),
2189+
},
2190+
name: "Layer Providers".to_string(),
2191+
description: "All available Geo Engine layer providers".to_string(),
2192+
items: vec![
2193+
data_catalog_item.clone(),
2194+
CollectionItem::Collection(LayerCollectionListing {
2195+
r#type: Default::default(),
2196+
id: ProviderLayerCollectionId {
2197+
provider_id: dataset_listing_provider.id,
2198+
collection_id: LayerCollectionId("root".to_string()),
2199+
},
2200+
name: dataset_listing_provider.name.clone(),
2201+
description: dataset_listing_provider.description.clone(),
2202+
properties: Default::default(),
2203+
}),
2204+
],
2205+
entry_label: None,
2206+
properties: vec![],
2207+
};
2208+
2209+
assert_eq!(response_collection, collection.clone());
2210+
2211+
let dataset_listing_provider = DatasetLayerListingProviderDefinition {
2212+
priority: Some(-1000),
2213+
..dataset_listing_provider
2214+
};
2215+
2216+
ctx.db()
2217+
.update_layer_provider_definition(
2218+
DataProviderId::from_u128(0xcbb2_1ee3_d15d_45c5_a175_6696_4adf_4e85),
2219+
crate::layers::external::TypedDataProviderDefinition::DatasetLayerListingProviderDefinition(
2220+
dataset_listing_provider,
2221+
),
2222+
)
2223+
.await.unwrap();
2224+
2225+
let req = test::TestRequest::get()
2226+
.uri("/layers/collections?limit=5&offset=0")
2227+
.append_header((header::AUTHORIZATION, Bearer::new(session_id.to_string())));
2228+
let response = send_test_request(req, app_ctx.clone()).await;
2229+
2230+
assert!(response.status().is_success(), "{response:?}");
2231+
2232+
let response_collection =
2233+
serde_json::from_str::<LayerCollection>(&read_body_string(response).await).unwrap();
2234+
2235+
let collection = LayerCollection {
2236+
items: vec![data_catalog_item],
2237+
..collection
2238+
};
2239+
2240+
// Provider with priority -1000 is filtered out
2241+
assert_eq!(response_collection, collection);
2242+
}
2243+
20752244
#[ge_context::test]
20762245
async fn test_get_provider_definition(app_ctx: PostgresContext<NoTls>) {
20772246
let session = admin_login(&app_ctx).await;

services/src/api/model/services.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ pub struct Volume {
218218
pub path: Option<String>,
219219
}
220220

221-
#[derive(Debug, Clone, PartialEq, Eq, ToSchema, Serialize)]
221+
#[derive(Debug, Clone, PartialEq, Eq, ToSchema, Serialize, Deserialize)]
222222
pub struct LayerProviderListing {
223223
pub id: DataProviderId,
224224
pub name: String,

services/src/contexts/migrations/current_schema.sql

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,60 +1168,76 @@ CREATE UNIQUE INDEX ON permissions (
11681168
ml_model_id
11691169
);
11701170

1171+
CREATE UNIQUE INDEX ON permissions (
1172+
role_id,
1173+
permission,
1174+
provider_id
1175+
);
1176+
11711177
CREATE VIEW user_permitted_datasets
11721178
AS
11731179
SELECT
11741180
r.user_id,
11751181
p.dataset_id,
1176-
p.permission
1182+
MAX(p.permission) AS max_permission
11771183
FROM user_roles AS r
1178-
INNER JOIN permissions AS p ON (
1179-
r.role_id = p.role_id AND p.dataset_id IS NOT NULL
1180-
);
1184+
INNER JOIN permissions AS p
1185+
ON (
1186+
r.role_id = p.role_id AND p.dataset_id IS NOT NULL
1187+
)
1188+
GROUP BY r.user_id, p.dataset_id;
11811189

11821190
CREATE VIEW user_permitted_projects
11831191
AS
11841192
SELECT
11851193
r.user_id,
11861194
p.project_id,
1187-
p.permission
1195+
MAX(p.permission) AS max_permission
11881196
FROM user_roles AS r
1189-
INNER JOIN permissions AS p ON (
1190-
r.role_id = p.role_id AND p.project_id IS NOT NULL
1191-
);
1197+
INNER JOIN permissions AS p
1198+
ON (
1199+
r.role_id = p.role_id AND p.project_id IS NOT NULL
1200+
)
1201+
GROUP BY r.user_id, p.project_id;
11921202

11931203
CREATE VIEW user_permitted_layer_collections
11941204
AS
11951205
SELECT
11961206
r.user_id,
11971207
p.layer_collection_id,
1198-
p.permission
1208+
MAX(p.permission) AS max_permission
11991209
FROM user_roles AS r
1200-
INNER JOIN permissions AS p ON (
1201-
r.role_id = p.role_id AND p.layer_collection_id IS NOT NULL
1202-
);
1210+
INNER JOIN permissions AS p
1211+
ON (
1212+
r.role_id = p.role_id AND p.layer_collection_id IS NOT NULL
1213+
)
1214+
GROUP BY r.user_id, p.layer_collection_id;
12031215

12041216
CREATE VIEW user_permitted_layers
12051217
AS
12061218
SELECT
12071219
r.user_id,
12081220
p.layer_id,
1209-
p.permission
1221+
MAX(p.permission) AS max_permission
12101222
FROM user_roles AS r
1211-
INNER JOIN permissions AS p ON (
1212-
r.role_id = p.role_id AND p.layer_id IS NOT NULL
1213-
);
1223+
INNER JOIN permissions AS p
1224+
ON (
1225+
r.role_id = p.role_id AND p.layer_id IS NOT NULL
1226+
)
1227+
GROUP BY r.user_id, p.layer_id;
12141228

12151229
CREATE VIEW user_permitted_providers
12161230
AS
12171231
SELECT
12181232
r.user_id,
12191233
p.provider_id,
1220-
p.permission
1234+
MAX(p.permission) AS max_permission
12211235
FROM user_roles AS r
1222-
INNER JOIN permissions AS p ON (
1223-
r.role_id = p.role_id AND p.provider_id IS NOT NULL
1224-
);
1236+
INNER JOIN permissions AS p
1237+
ON (
1238+
r.role_id = p.role_id AND p.provider_id IS NOT NULL
1239+
)
1240+
GROUP BY r.user_id, p.provider_id;
12251241

12261242
CREATE TABLE oidc_session_tokens (
12271243
session_id uuid PRIMARY KEY REFERENCES sessions (
@@ -1239,11 +1255,13 @@ AS
12391255
SELECT
12401256
r.user_id,
12411257
p.ml_model_id,
1242-
p.permission
1258+
MAX(p.permission) AS max_permission
12431259
FROM user_roles AS r
1244-
INNER JOIN permissions AS p ON (
1245-
r.role_id = p.role_id AND p.ml_model_id IS NOT NULL
1246-
);
1260+
INNER JOIN permissions AS p
1261+
ON (
1262+
r.role_id = p.role_id AND p.ml_model_id IS NOT NULL
1263+
)
1264+
GROUP BY r.user_id, p.ml_model_id;
12471265

12481266
CREATE TABLE quota_log (
12491267
timestamp timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use super::database_migration::{DatabaseVersion, Migration};
2+
use crate::contexts::migrations::migration_0021_default_permissions_for_existing_providers::Migration0021DefaultPermissionsForExistingProviders;
3+
use crate::error::Result;
4+
use async_trait::async_trait;
5+
use tokio_postgres::Transaction;
6+
7+
/// This migration adds the provider permissions
8+
pub struct Migration0022PermissionQueries;
9+
10+
#[async_trait]
11+
impl Migration for Migration0022PermissionQueries {
12+
fn prev_version(&self) -> Option<DatabaseVersion> {
13+
Some(Migration0021DefaultPermissionsForExistingProviders.version())
14+
}
15+
16+
fn version(&self) -> DatabaseVersion {
17+
"0022_permission_queries".into()
18+
}
19+
20+
async fn migrate(&self, tx: &Transaction<'_>) -> Result<()> {
21+
tx.batch_execute(include_str!("migration_0022_permission_queries.sql"))
22+
.await?;
23+
24+
Ok(())
25+
}
26+
}

0 commit comments

Comments
 (0)