Skip to content
This repository has been archived by the owner on Dec 18, 2024. It is now read-only.

Add authorization integration test #708

Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions kuksa_databroker/databroker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ name = "databroker"
path = "src/lib.rs"

[dependencies]
common = { path = "../lib"}
kuksa = { path = "../lib/kuksa"}
databroker-proto = { workspace = true }
tonic = { workspace = true, features = ["transport", "channel", "prost"] }
prost = { workspace = true }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,75 @@
Feature: Reading and writing values of a VSS Data Entry

Rule: Access with right permissions succeeds and fails with wrong/no permissions

Background:
Given a running Databroker server with authorization true with the following Data Entries registered
| path | data type | change type | type |
| Vehicle.Speed | float | Static | Attribute |

Scenario: Write the current value of an unset Data Entry without authenticating fails
lukasmittag marked this conversation as resolved.
Show resolved Hide resolved
When a client sets the current value of Vehicle.Width of type float to 13.4 with token ""
Then the current value for Vehicle.Width can not be accessed because we are unauthorized

Scenario: Read the current value of an unset Data Entry without authenticating fails
When a client gets the current value of Vehicle.Width with token ""
Then the current value for Vehicle.Width can not be accessed because we are unauthorized

Scenario: Write the current value of a Data Entry without right permissions fails
"""
The token comes from kuksa.val/jwt/read-vehicle-speed.token. Expire date is Thursday, 1 January 2026
"""
When a client sets the current value of Vehicle.Speed of type float to 13.4 with token eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJsb2NhbCBkZXYiLCJpc3MiOiJjcmVhdGVUb2tlbi5weSIsImF1ZCI6WyJrdWtzYS52YWwiXSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3NjcyMjU1OTksInNjb3BlIjoicmVhZDpWZWhpY2xlLlNwZWVkIn0.QyaKO-vjjzeimAAyUMjlM_jAvhlmYVguzXp76jAnW9UUiWowrXeYTa_ERVzZqxz21txq1QQmee8WtCO978w95irSRugTmlydWNjS6xPGAOCan6TEXBryrmR5FSmm6fPNrgLPhFEfcqFIQm07B286JTU98s-s2yeb6bJRpm7gOzhH5klCLxBPFYNncwdqqaT6aQD31xOcq4evooPOoV5KZFKI9WKkzOclDvto6TCLYIgnxu9Oey_IqyRAkDHybeFB7LR-1XexweRjWKleXGijpNweu6ATsbPakeVIfJ6k3IN-oKNvP6nawtBg7GeSEFYNksUUCVrIg8b0_tZi3c3E114MvdiDe7iWfRd5SDjO1f8nKiqDYy9P-hwejHntsaXLZbWSs_GNbWmi6J6pwgdM4XI3x4vx-0Otsb4zZ0tOmXjIL_tRsKA5dIlSBYEpIZq6jSXgQLN2_Ame0i--gGt4jTg1sYJHqE56BKc74HqkcvrQAZrZcoeqiKkwKcy6ofRpIQ57ghooZLkJ8BLWEV_zJgSffHBX140EEnMg1z8GAhrsbGvJ29TmXGmYyLrAhyTj_L6aNCZ1XEkbK0Yy5pco9sFGRm9nKTeFcNICogPuzbHg4xsGX_SZgUmla8Acw21AAXwuFY60_aFDUlCKZh93Z2SAruz1gfNIL43I0L8-wfw
lukasmittag marked this conversation as resolved.
Show resolved Hide resolved
Then setting the value for Vehicle.Speed fails with error code 403

Scenario: Write the current value of a Data Entry with right permissions succeeds
"""
The token comes from kuksa.val/jwt/provide-vehicle-speed.token. Expire date is Thursday, 1 January 2026
"""
When a client sets the current value of Vehicle.Speed of type float to 13.4 with token eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJsb2NhbCBkZXYiLCJpc3MiOiJjcmVhdGVUb2tlbi5weSIsImF1ZCI6WyJrdWtzYS52YWwiXSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3NjcyMjU1OTksInNjb3BlIjoicHJvdmlkZTpWZWhpY2xlLlNwZWVkIn0.WlHkTSverOeprozFHG5Oo14c_Qr0NL9jv3ObAK4S10ddbqFRjWttkY9C0ehLqM6vXNUyI9uimbrM5FSPpw058mWGbOaEc8l1ImjS-DBKkDXyFkSlMoCPuWfhbamfFWTfY-K_K21kTs0hvr-FGRREC1znnZx0TFEi9HQO2YJvsSfJ7-6yo1Wfplvhf3NCa-sC5PrZEEbvYLkTB56C--0waqxkLZGx_SAo_XoRCijJ3s_LnrEbp61kT9CVYmNk017--mA9EEcjpHceOOtj1_UVjHpLKHOxitjpF-7LQNdq2kCY-Y2qv9vf8H6nAFVG8QKAUAaFb0CmYpDIdK8XSLRD7yLd6JnoRswBqmveFCUpmdrMYsSgut1JH4oCn5EnJ-c5UfZ4IRDgc7iBE5cqH9ao7j5PItsE9tYQJDAfygel3sYnIzuAd-DMYyPs1Jj9BzrAWEmI9s0PelA0KAEspmNufn9e-mjeC050e5NhhzJ4Vj_ffbOBzgx1vgLAaoMj5dOb4j3OpNC0XoUgGfR-YbTLi48h6uXEnxsXNGblOlSqTBmy2iZhYpfLBIsdvQTzKf2iYkw_TLo5LE5p9m4aUKFywcyGPMxzVcA8JIJ2g2Xp30RnIAxUlDTXcuYDGYRgKiGJb0rq1yQVl3RCnKaxTVHg8qqHkts_B-cbItlZP8bJA5M
Then the set operation succeeds without an error

Rule: Accessing unregistered Data Entries fails

Background:
Given a running Databroker server
Given a running Databroker server with authorization false

Scenario: Setting the current value of an unregistered Data Entry fails
When a client sets the current value of No.Such.Path of type float to 13.4
When a client sets the current value of No.Such.Path of type float to 13.4 with token ""
lukasmittag marked this conversation as resolved.
Show resolved Hide resolved
Then setting the value for No.Such.Path fails with error code 404

Scenario: Reading the current value of an unregistered Data Entry fails
When a client gets the current value of No.Such.Path
When a client gets the current value of No.Such.Path with token ""
Then the current value is not found

Scenario: Setting the target value of an unregistered Data Entry fails
When a client sets the target value of No.Such.Path of type float to 13.4
When a client sets the target value of No.Such.Path of type float to 13.4 with token ""
Then setting the value for No.Such.Path fails with error code 404

Scenario: Reading the target value of an unregistered Data Entry fails
When a client gets the target value of No.Such.Path
When a client gets the target value of No.Such.Path with token ""
Then the target value is not found

Rule: Target values can only be set on Actuators

Background:
Given a running Databroker server with the following Data Entries registered
Given a running Databroker server with authorization false with the following Data Entries registered
| path | data type | change type | type |
| Vehicle.Powertrain.Range | uint32 | Continuous | Sensor |
| Vehicle.Width | uint16 | Static | Attribute |

Scenario: Setting the target value of an Attribute fails
When a client sets the target value of Vehicle.Width of type uint16 to 13
When a client sets the target value of Vehicle.Width of type uint16 to 13 with token ""
Then the operation fails with status code 3

Scenario: Setting the target value of a Sensor fails
When a client sets the target value of Vehicle.Powertrain.Range of type uint32 to 13455
When a client sets the target value of Vehicle.Powertrain.Range of type uint32 to 13455 with token ""
Then the operation fails with status code 3

Rule: Accessing registered Data Entries works

Background:
Given a running Databroker server with the following Data Entries registered
Given a running Databroker server with authorization false with the following Data Entries registered
| path | data type | change type | type |
| Vehicle.Cabin.Lights.AmbientLight | uint8 | OnChange | Actuator |
| Vehicle.Cabin.Sunroof.Position | int8 | OnChange | Actuator |
Expand All @@ -54,12 +83,12 @@ Feature: Reading and writing values of a VSS Data Entry
| Vehicle.Width | uint16 | Static | Attribute |

Scenario: Reading the current value of an unset Data Entry yields an empty result
When a client gets the current value of Vehicle.Width
When a client gets the current value of Vehicle.Width with token ""
Then the current value for Vehicle.Width is not specified

Scenario Outline: Reading the current value works
Given a Data Entry <path> of type <type> having current value <value>
When a client gets the current value of <path>
When a client gets the current value of <path> with token ""
Then the current value for <path> is <value> having type <type>

Examples:
Expand All @@ -76,7 +105,7 @@ Feature: Reading and writing values of a VSS Data Entry
| Vehicle.IsMoving | bool | true |

Scenario Outline: Setting a Data Entry's current value using a wrong type fails
When a client sets the current value of Vehicle.Speed of type <type> to <value>
When a client sets the current value of Vehicle.Speed of type <type> to <value> with token ""
Then setting the value for Vehicle.Speed fails with error code 400

Examples:
Expand All @@ -93,12 +122,12 @@ Feature: Reading and writing values of a VSS Data Entry
| int64 | -255256525864925 |

Scenario: Reading the target value of an unset Data Entry yields an empty result
When a client gets the target value of Vehicle.Cabin.Sunroof.Position
When a client gets the target value of Vehicle.Cabin.Sunroof.Position with token ""
Then the target value for Vehicle.Cabin.Sunroof.Position is not specified

Scenario Outline: Reading the target value of Actuators works
Given a Data Entry <path> of type <type> having target value <value>
When a client gets the target value of <path>
When a client gets the target value of <path> with token ""
Then the target value for <path> is <value> having type <type>

Examples:
Expand All @@ -107,7 +136,7 @@ Feature: Reading and writing values of a VSS Data Entry
| Vehicle.Cabin.Sunroof.Position | int8 | -128 |

Scenario Outline: Setting an Actuator's target value using a wrong type fails
When a client sets the target value of Vehicle.Cabin.Sunroof.Position of type <type> to <value>
When a client sets the target value of Vehicle.Cabin.Sunroof.Position of type <type> to <value> with token ""
Then setting the value for Vehicle.Cabin.Sunroof.Position fails with error code 400

Examples:
Expand Down
168 changes: 95 additions & 73 deletions kuksa_databroker/databroker/tests/read_write_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@
********************************************************************************/

use core::panic;
use std::{future, time::SystemTime, vec};
use std::{collections::HashMap, future, time::SystemTime, vec};

use common::ClientError;
use cucumber::{cli, gherkin::Step, given, then, when, writer, World as _};
use databroker::broker;
use databroker_proto::kuksa::val::v1::{
datapoint::Value, DataEntry, DataType, Datapoint, EntryRequest, EntryUpdate, Field, GetRequest,
SetRequest, View,
};
use databroker_proto::kuksa::val::v1::{datapoint::Value, DataType, Datapoint};
use tonic::Code;
use tracing::debug;
use world::{DataBrokerWorld, ValueType};

Expand Down Expand Up @@ -75,9 +74,10 @@ fn get_data_entries_from_table(
data_entries
}

#[given(regex = "^a running Databroker server.*$")]
async fn start_databroker_server(w: &mut DataBrokerWorld, step: &Step) {
w.start_databroker(get_data_entries_from_table(step)).await;
#[given(regex = "^a running Databroker server with authorization (true|false).*$")]
lukasmittag marked this conversation as resolved.
Show resolved Hide resolved
async fn start_databroker_server(w: &mut DataBrokerWorld, auth: bool, step: &Step) {
w.start_databroker(get_data_entries_from_table(step), auth)
.await;
assert!(w.broker_client.is_some())
}

Expand All @@ -89,90 +89,100 @@ async fn a_known_data_entry_has_value(
value_type: ValueType,
value: String,
) {
set_value(w, value_type, path, data_type, value).await;
w.assert_set_response_has_succeeded()
// comes from jwt/actuate-provide-all.token and is expiring 31st of December 2025
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJsb2NhbCBkZXYiLCJpc3MiOiJjcmVhdGVUb2tlbi5weSIsImF1ZCI6WyJrdWtzYS52YWwiXSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3NjcyMjU1OTksInNjb3BlIjoiYWN0dWF0ZSBwcm92aWRlIn0.x-bUZwDCC663wGYrWCYjQZwQWhN1CMuKgxuIN5dUF_izwMutiqF6Xc-tnXgZa93BbT3I74WOMk4awKHBUSTWekGs3-qF6gajorbat6n5180TOqvNu4CXuIPZN5zpngf4id3smMkKOT699tPnSEbmlkj4vk-mIjeOAU-FcYA-VbkKBTsjvfFgKa2OdB5h9uZARBg5Rx7uBN3JsH1I6j9zoLid184Ewa6bhU2qniFt5iPsGJniNsKsRrrndN1KzthO13My44s56yvwSHIOrgDGbXdja_eLuOVOq9pHCjCtorPScgEuUUE4aldIuML-_j397taNP9Y3VZYVvofEK7AuiePTbzwxrZ1RAjK74h1-4ued3A2gUTjr5BsRlc9b7eLZzxLJkrqdfGAzBh_rtrB7p32TbvpjeFP30NW6bB9JS43XACUUm_S_RcyI7BLuUdnFyQDQr6l6sRz9XayYXceilHdCxbAVN0HVnBeui5Bb0mUZYIRZeY8k6zcssmokANTD8ZviDMpKlOU3t5AlXJ0nLkgyMhV9IUTwPUv6F8BTPc-CquJCUNbTyo4ywTSoODWbm3PmQ3Y46gWF06xqnB4wehLscBdVk3iAihQp3tckGhMnx5PI_Oy7utIncr4pRCMos63TnBkfrl7d43cHQTuK0kO76EWtv4ODEHgLvEAv4HA";
set_value(w, value_type, path, data_type, value, token.to_string()).await;
w.assert_set_succeeded()
}

#[when(expr = "a client sets the {word} value of {word} of type {word} to {word}")]
#[when(
expr = "a client sets the {word} value of {word} of type {word} to {word} with token {word}"
)]
async fn set_value(
w: &mut DataBrokerWorld,
value_type: ValueType,
path: String,
data_type: DataType,
value: String,
token: String,
) {
let client = w
.broker_client
.as_mut()
.and_then(|client| match client.basic_client.set_access_token(token) {
Ok(()) => Some(client),
Err(e) => {
println!("Error: {e}");
None
}
})
.expect("no Databroker client available, broker not started?");
let value = Value::new(data_type, value.as_str()).expect("cannot parse value into given type");
let datapoint = Datapoint {
timestamp: Some(SystemTime::now().into()),
value: Some(value),
};

let data_entry = match value_type {
ValueType::Current => DataEntry {
path: path.clone(),
value: Some(datapoint),
actuator_target: None,
metadata: None,
},
ValueType::Target => DataEntry {
path: path.clone(),
value: None,
actuator_target: Some(datapoint),
metadata: None,
},
};
let req = SetRequest {
updates: vec![EntryUpdate {
entry: Some(data_entry),
fields: vec![
Field::ActuatorTarget.into(),
Field::Value.into(),
Field::Path.into(),
],
}],
};
match client.set(req).await {
Ok(res) => {
let set_response = res.into_inner();
debug!(
"response from Databroker [global error: {:?}, Data Entry errors: {:?}]",
set_response.error, set_response.errors
);
w.current_set_response = Some(set_response);
match value_type {
ValueType::Target => {
match client
.set_target_values(HashMap::from([(path.clone(), datapoint.clone())]))
.await
{
Ok(_) => {
w.current_client_error = None;
}
Err(e) => {
debug!("failed to invoke Databroker's set operation: {:?}", e);
w.current_client_error = Some(e);
}
}
}
Err(e) => {
debug!("failed to invoke Databroker's set operation: {:?}", e);
w.current_status = Some(e);
ValueType::Current => {
match client
.set_current_values(HashMap::from([(path.clone(), datapoint.clone())]))
.await
{
Ok(_) => {
w.current_client_error = None;
}
Err(e) => {
debug!("failed to invoke Databroker's set operation: {:?}", e);
w.current_client_error = Some(e);
}
}
}
}
}

#[when(expr = "a client gets the {word} value of {word}")]
async fn get_value(w: &mut DataBrokerWorld, value_type: ValueType, path: String) {
#[when(expr = "a client gets the {word} value of {word} with token {word}")]
async fn get_value(w: &mut DataBrokerWorld, value_type: ValueType, path: String, token: String) {
let client = w
.broker_client
.as_mut()
.and_then(|client| match client.basic_client.set_access_token(token) {
Ok(()) => Some(client),
Err(e) => {
println!("Error: {e}");
None
}
})
.expect("no Databroker client available, broker not started?");
let get_request = GetRequest {
entries: vec![EntryRequest {
path: path.to_string(),
view: match value_type {
ValueType::Current => View::CurrentValue.into(),
ValueType::Target => View::TargetValue.into(),
},
fields: vec![Field::Value.into(), Field::Metadata.into()],
}],
};
match client.get(get_request).await {
Ok(res) => w.current_get_response = Some(res.into_inner()),
Err(e) => {
debug!("failed to invoke Databroker's get operation: {:?}", e);
w.current_status = Some(e);
}
match value_type {
ValueType::Target => match client.get_target_values(vec![&path]).await {
Ok(res) => w.current_data_entries = Some(res),
Err(e) => {
debug!("failed to invoke Databroker's get operation: {:?}", e);
w.current_client_error = Some(e);
}
},
ValueType::Current => match client.get_current_values(vec![path]).await {
Ok(res) => w.current_data_entries = Some(res),
Err(e) => {
debug!("failed to invoke Databroker's get operation: {:?}", e);
w.current_client_error = Some(e);
}
},
}
}

Expand Down Expand Up @@ -224,18 +234,12 @@ fn assert_value_is_unspecified(w: &mut DataBrokerWorld, value_type: ValueType, p

#[then(regex = r"^the (current|target) value is not found$")]
fn assert_value_not_found(w: &mut DataBrokerWorld) {
let error_code = w
.current_get_response
.clone()
.and_then(|res| res.error)
.map(|error| error.code);

assert_eq!(error_code, Some(404));
w.assert_response_has_error_code(vec![404]);
}

#[then(expr = "setting the value for {word} fails with error code {int}")]
fn assert_set_request_failure(w: &mut DataBrokerWorld, path: String, expected_error_code: u32) {
w.assert_set_response_has_error_code(path, expected_error_code)
fn assert_set_request_failure(w: &mut DataBrokerWorld, _path: String, expected_error_code: u32) {
w.assert_response_has_error_code(vec![expected_error_code])
}

/// https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc
Expand All @@ -244,6 +248,24 @@ fn assert_request_failure(w: &mut DataBrokerWorld, expected_status_code: i32) {
w.assert_status_has_code(expected_status_code)
}

#[then(expr = "the current value for {word} can not be accessed because we are unauthorized")]
lukasmittag marked this conversation as resolved.
Show resolved Hide resolved
fn assert_current_value_unauthenticated(w: &mut DataBrokerWorld) {
if let Some(error) = w.current_client_error.clone() {
match error {
ClientError::Connection(e) => {
panic!("No connection error {:?} should occcur", e)
}
ClientError::Function(e) => panic!("No function error {:?} should occur", e),
ClientError::Status(status) => assert_eq!(status.code(), Code::Unauthenticated),
}
}
}

#[then(expr = "the set operation succeeds without an error")]
lukasmittag marked this conversation as resolved.
Show resolved Hide resolved
fn assert_set_succeeds(w: &mut DataBrokerWorld) {
w.assert_set_succeeded()
}

#[tokio::main]
async fn main() {
// databroker::init_logging();
Expand Down
Loading