diff --git a/.github/workflows/test-examples.yml b/.github/workflows/test-examples.yml index 87c6d4992..41f38ea03 100644 --- a/.github/workflows/test-examples.yml +++ b/.github/workflows/test-examples.yml @@ -192,9 +192,7 @@ jobs: "$MANAGEMENT_URL/v1/bootstrap" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ - -d '{ - "accept-terms-of-use": true - }' \ + -d '{}' \ || echo "Bootstrap may have already been completed" - name: Dump docker logs on failure diff --git a/crates/lakekeeper/src/api/management/v1/bootstrap.rs b/crates/lakekeeper/src/api/management/v1/bootstrap.rs index 4c21be4f7..52b64a6bc 100644 --- a/crates/lakekeeper/src/api/management/v1/bootstrap.rs +++ b/crates/lakekeeper/src/api/management/v1/bootstrap.rs @@ -25,9 +25,6 @@ pub enum AuthZBackend { #[derive(Debug, Deserialize, utoipa::ToSchema, TypedBuilder)] #[serde(rename_all = "kebab-case")] pub struct BootstrapRequest { - /// Set to true if you accept LAKEKEEPER terms of use. - #[builder(setter(strip_bool))] - pub accept_terms_of_use: bool, /// If set to true, the calling user is treated as an operator and obtain /// a corresponding role. If not specified, the user is treated as a human. #[serde(default)] @@ -89,19 +86,9 @@ pub(crate) trait Service { user_name, user_email, user_type, - accept_terms_of_use, is_operator, } = request; - if !accept_terms_of_use { - return Err(ErrorModel::builder() - .code(http::StatusCode::BAD_REQUEST.into()) - .message("You must accept the terms of use to bootstrap the catalog.".to_string()) - .r#type("TermsOfUseNotAccepted".to_string()) - .build() - .into()); - } - // ------------------- AUTHZ ------------------- // We check at two places if we can bootstrap: AuthZ and the catalog. // AuthZ just checks if the request metadata could be added as the servers @@ -111,7 +98,7 @@ pub(crate) trait Service { // ------------------- Business Logic ------------------- let mut t = C::Transaction::begin_write(state.v1_state.catalog.clone()).await?; - let success = C::bootstrap(request.accept_terms_of_use, t.transaction()).await?; + let success = C::bootstrap(t.transaction()).await?; if !success { return Err(ErrorModel::bad_request( "Catalog already bootstrapped", diff --git a/crates/lakekeeper/src/implementations/postgres/bootstrap.rs b/crates/lakekeeper/src/implementations/postgres/bootstrap.rs index 9e94c1be9..3a0174e5e 100644 --- a/crates/lakekeeper/src/implementations/postgres/bootstrap.rs +++ b/crates/lakekeeper/src/implementations/postgres/bootstrap.rs @@ -37,7 +37,6 @@ pub(super) async fn get_validation_data< if let Some(server) = server { Ok(ServerInfo::Bootstrapped { server_id: server.server_id, - terms_accepted: server.terms_accepted, }) } else { Ok(ServerInfo::NotBootstrapped) @@ -45,7 +44,6 @@ pub(super) async fn get_validation_data< } pub(super) async fn bootstrap<'e, 'c: 'e, E: sqlx::Executor<'c, Database = sqlx::Postgres>>( - terms_accepted: bool, connection: E, ) -> Result { let server_id = CONFIG.server_id; @@ -53,14 +51,13 @@ pub(super) async fn bootstrap<'e, 'c: 'e, E: sqlx::Executor<'c, Database = sqlx: let result = sqlx::query!( r#" INSERT INTO server (single_row, server_id, open_for_bootstrap, terms_accepted) - VALUES (true, $1, false, $2) + VALUES (true, $1, false, true) ON CONFLICT (single_row) - DO UPDATE SET terms_accepted = $2, open_for_bootstrap = false + DO UPDATE SET open_for_bootstrap = false, terms_accepted = true WHERE server.open_for_bootstrap = true returning server_id "#, server_id, - terms_accepted, ) .fetch_one(connection) .await; @@ -94,18 +91,17 @@ mod test { let data = get_validation_data(&state.read_pool()).await.unwrap(); assert_eq!(data, ServerInfo::NotBootstrapped); - let success = bootstrap(true, &state.read_write.write_pool).await.unwrap(); + let success = bootstrap(&state.read_write.write_pool).await.unwrap(); assert!(success); let data = get_validation_data(&state.read_pool()).await.unwrap(); assert_eq!( data, ServerInfo::Bootstrapped { server_id: CONFIG.server_id, - terms_accepted: true, } ); - let success = bootstrap(true, &state.read_write.write_pool).await.unwrap(); + let success = bootstrap(&state.read_write.write_pool).await.unwrap(); assert!(!success); } } diff --git a/crates/lakekeeper/src/implementations/postgres/catalog.rs b/crates/lakekeeper/src/implementations/postgres/catalog.rs index 380429139..1bd470d8a 100644 --- a/crates/lakekeeper/src/implementations/postgres/catalog.rs +++ b/crates/lakekeeper/src/implementations/postgres/catalog.rs @@ -86,10 +86,9 @@ impl Catalog for super::PostgresCatalog { // ---------------- Bootstrap ---------------- async fn bootstrap<'a>( - terms_accepted: bool, transaction: >::Transaction<'a>, ) -> Result { - bootstrap(terms_accepted, &mut **transaction).await + bootstrap(&mut **transaction).await } async fn get_warehouse_by_name( diff --git a/crates/lakekeeper/src/serve.rs b/crates/lakekeeper/src/serve.rs index 9a4837d5d..0e1b1feff 100644 --- a/crates/lakekeeper/src/serve.rs +++ b/crates/lakekeeper/src/serve.rs @@ -233,13 +233,8 @@ fn validate_server_info(server_info: &ServerInfo) -> anyhow::Result<()> { } ServerInfo::Bootstrapped { server_id, - terms_accepted, } => { - if !terms_accepted { - Err(anyhow!( - "The terms of service have not been accepted on bootstrap." - )) - } else if *server_id != CONFIG.server_id { + if *server_id != CONFIG.server_id { Err(anyhow!( "The server ID during bootstrap {} does not match the server ID in the configuration {}.", server_id, CONFIG.server_id diff --git a/crates/lakekeeper/src/service/catalog.rs b/crates/lakekeeper/src/service/catalog.rs index 133d5e630..d4bbaa6ad 100644 --- a/crates/lakekeeper/src/service/catalog.rs +++ b/crates/lakekeeper/src/service/catalog.rs @@ -203,8 +203,6 @@ pub enum ServerInfo { Bootstrapped { /// Server ID of the catalog at the time of bootstrapping server_id: uuid::Uuid, - /// Whether the terms have been accepted - terms_accepted: bool, }, } @@ -280,7 +278,6 @@ where /// If bootstrapped succeeded, return Ok(true). /// If the catalog is already bootstrapped, return Ok(false). async fn bootstrap<'a>( - terms_accepted: bool, transaction: >::Transaction<'a>, ) -> Result; diff --git a/crates/lakekeeper/src/tests/drop_warehouse.rs b/crates/lakekeeper/src/tests/drop_warehouse.rs index 8fb6b934a..e6cf6944a 100644 --- a/crates/lakekeeper/src/tests/drop_warehouse.rs +++ b/crates/lakekeeper/src/tests/drop_warehouse.rs @@ -34,7 +34,7 @@ async fn test_cannot_drop_warehouse_before_purge_tasks_completed(pool: PgPool) { ApiServer::bootstrap( api_context.clone(), random_request_metadata(), - BootstrapRequest::builder().accept_terms_of_use().build(), + BootstrapRequest::builder().build(), ) .await .unwrap(); diff --git a/crates/lakekeeper/src/tests/mod.rs b/crates/lakekeeper/src/tests/mod.rs index 47d63c7eb..6e2ba2f69 100644 --- a/crates/lakekeeper/src/tests/mod.rs +++ b/crates/lakekeeper/src/tests/mod.rs @@ -214,7 +214,6 @@ pub(crate) async fn setup( api_context.clone(), metadata.clone(), BootstrapRequest { - accept_terms_of_use: true, is_operator: true, user_name: None, user_email: None, diff --git a/docs/docs/api/management-open-api.yaml b/docs/docs/api/management-open-api.yaml index 45fe1ebc6..1b71b7972 100644 --- a/docs/docs/api/management-open-api.yaml +++ b/docs/docs/api/management-open-api.yaml @@ -2454,12 +2454,7 @@ components: - azure-system-identity BootstrapRequest: type: object - required: - - accept-terms-of-use properties: - accept-terms-of-use: - type: boolean - description: Set to true if you accept LAKEKEEPER terms of use. is-operator: type: boolean description: |- diff --git a/docs/docs/bootstrap.md b/docs/docs/bootstrap.md index 00c6d20db..5e6c6db9f 100644 --- a/docs/docs/bootstrap.md +++ b/docs/docs/bootstrap.md @@ -5,9 +5,7 @@ After the initial deployment, Lakekeeper needs to be bootstrapped. This can be d curl --location 'https:///management/v1/bootstrap' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ ---data '{ - "accept-terms-of-use": true -}' +--data '{}' ``` `` is obtained by logging into the IdP before bootstrapping Lakekeeper. If authentication is disabled, no token is required. Lakekeeper can only be bootstrapped once. @@ -16,7 +14,6 @@ During bootstrapping, Lakekeeper performs the following actions: * Grants the server's `admin` role to the user performing the POST request. The user is identified by their token. If authentication is disabled, the `Authorization` header is not required, and no `admin` is set, as permissions are disabled in this case. * Stores the current [Server ID](./concepts.md#server) to prevent unwanted future changes of the Server ID that would break permissions. -* Accepts terms of use as defined by our [License](../../about/license.md). * If `LAKEKEEPER__ENABLE_DEFAULT_PROJECT` is enabled (default), a default project with the NIL Project ID ("00000000-0000-0000-0000-000000000000") is created. If the initial user is a technical user (e.g., a Kubernetes Operator) managing the Lakekeeper deployment, the `admin` role might not be sufficient as it limits access to projects until the `admin` grants themselves permission. For technical users, the `operator` role grants full access to all APIs and can be obtained by adding `"is-operator": true` to the JSON body of the bootstrap request. diff --git a/examples/access-control-advanced/notebooks/01-Bootstrap.ipynb b/examples/access-control-advanced/notebooks/01-Bootstrap.ipynb index 2f230d30f..4874a94ca 100644 --- a/examples/access-control-advanced/notebooks/01-Bootstrap.ipynb +++ b/examples/access-control-advanced/notebooks/01-Bootstrap.ipynb @@ -102,18 +102,7 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "response = requests.post(\n", - " url=f\"{MANAGEMENT_URL}/v1/bootstrap\",\n", - " headers={\n", - " \"Authorization\": f\"Bearer {access_token}\"\n", - " },\n", - " json={\n", - " \"accept-terms-of-use\": True,\n", - " },\n", - ")\n", - "response.raise_for_status()" - ] + "source": "response = requests.post(\n url=f\"{MANAGEMENT_URL}/v1/bootstrap\",\n headers={\n \"Authorization\": f\"Bearer {access_token}\"\n },\n json={},\n)\nresponse.raise_for_status()" }, { "cell_type": "markdown", @@ -287,4 +276,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file diff --git a/examples/access-control-simple/notebooks/01-Bootstrap.ipynb b/examples/access-control-simple/notebooks/01-Bootstrap.ipynb index e89137e29..94e1e382a 100644 --- a/examples/access-control-simple/notebooks/01-Bootstrap.ipynb +++ b/examples/access-control-simple/notebooks/01-Bootstrap.ipynb @@ -107,22 +107,7 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "response = requests.post(\n", - " url=f\"{MANAGEMENT_URL}/v1/bootstrap\",\n", - " headers={\n", - " \"Authorization\": f\"Bearer {access_token}\"\n", - " },\n", - " json={\n", - " \"accept-terms-of-use\": True,\n", - " # Optionally, we can override the name / type of the user:\n", - " # \"user-email\": \"user@example.com\",\n", - " # \"user-name\": \"Roald Amundsen\",\n", - " # \"user-type\": \"human\"\n", - " },\n", - ")\n", - "response.raise_for_status()" - ] + "source": "response = requests.post(\n url=f\"{MANAGEMENT_URL}/v1/bootstrap\",\n headers={\n \"Authorization\": f\"Bearer {access_token}\"\n },\n json={\n # Optionally, we can override the name / type of the user:\n # \"user-email\": \"user@example.com\",\n # \"user-name\": \"Roald Amundsen\",\n # \"user-type\": \"human\"\n },\n)\nresponse.raise_for_status()" }, { "cell_type": "markdown", @@ -395,4 +380,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file diff --git a/examples/minimal/docker-compose.yaml b/examples/minimal/docker-compose.yaml index 5c10e90ad..73921a9b1 100644 --- a/examples/minimal/docker-compose.yaml +++ b/examples/minimal/docker-compose.yaml @@ -77,7 +77,7 @@ services: - "-H" - "Content-Type: application/json" - "--data" - - '{"accept-terms-of-use": true}' + - '{}' - "-o" - "/dev/null" # - "--fail-with-body" diff --git a/tests/kube-auth/bootstrap.sh b/tests/kube-auth/bootstrap.sh index 462d156f9..3f9c0bb39 100755 --- a/tests/kube-auth/bootstrap.sh +++ b/tests/kube-auth/bootstrap.sh @@ -2,4 +2,4 @@ set -eu -curl -f -H "Content-Type: application/json" -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" my-lakekeeper:8181/management/v1/bootstrap -d '{"accept-terms-of-use": true}' +curl -f -H "Content-Type: application/json" -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" my-lakekeeper:8181/management/v1/bootstrap -d '{}' diff --git a/tests/migrations/docker-compose.yaml b/tests/migrations/docker-compose.yaml index 4f4a9a24c..e1ada75d1 100644 --- a/tests/migrations/docker-compose.yaml +++ b/tests/migrations/docker-compose.yaml @@ -122,7 +122,7 @@ services: - "-H" - "Content-Type: application/json" - "--data" - - '{"accept-terms-of-use": true}' + - '{}' - "-o" - "/dev/null" # - "--fail-with-body" diff --git a/tests/python/tests/conftest.py b/tests/python/tests/conftest.py index 570a08f20..f0407ffb6 100644 --- a/tests/python/tests/conftest.py +++ b/tests/python/tests/conftest.py @@ -471,7 +471,7 @@ def server(access_token) -> Server: response = requests.post( management_url + "v1/bootstrap", headers={"Authorization": f"Bearer {access_token}"}, - json={"accept-terms-of-use": True}, + json={}, ) response.raise_for_status()