Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: 1 addition & 1 deletion .github/workflows/benchmark_fork_run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:

- name: Track base branch benchmarks with Bencher
run: |
cargo bench > benchmark_results.log
cargo bench --features bench_internals > benchmark_results.log

- name: Upload Benchmark Results
uses: actions/upload-artifact@v5
Expand Down
4 changes: 2 additions & 2 deletions benches/fernet_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use std::io::Write;
use tempfile::tempdir;

use openstack_keystone::config::Config;
use openstack_keystone::token::fernet::FernetTokenProvider;
use openstack_keystone::token::fernet::bench_get_fernet_timestamp;
use openstack_keystone::token::backend::fernet::FernetTokenProvider;
use openstack_keystone::token::backend::fernet::bench_get_fernet_timestamp;
//use openstack_keystone::token::types::TokenBackend;

fn decode(backend: &FernetTokenProvider, token: &str) {
Expand Down
36 changes: 36 additions & 0 deletions src/token/backend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//! Token provider backends.

use dyn_clone::DynClone;

use crate::config::Config;
use crate::token::{TokenProviderError, types::Token};

pub mod fernet;
pub use fernet::*;

/// Token Provider backend interface.
pub trait TokenBackend: DynClone + Send + Sync + std::fmt::Debug {
/// Set config.
fn set_config(&mut self, g: Config);

/// Extract the token from string.
fn decode(&self, credential: &str) -> Result<Token, TokenProviderError>;

/// Extract the token from string.
fn encode(&self, token: &Token) -> Result<String, TokenProviderError>;
}

dyn_clone::clone_trait_object!(TokenBackend);
20 changes: 14 additions & 6 deletions src/token/fernet.rs → src/token/backend/fernet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,22 @@ use std::io::{Cursor, Write};
use tracing::trace;

use crate::config::Config;
use crate::token::backend::TokenBackend;
use crate::token::{
TokenProviderError, application_credential::ApplicationCredentialPayload,
domain_scoped::DomainScopePayload, federation_domain_scoped::FederationDomainScopePayload,
federation_project_scoped::FederationProjectScopePayload,
federation_unscoped::FederationUnscopedPayload, fernet_utils::FernetUtils,
project_scoped::ProjectScopePayload, restricted::RestrictedPayload, types::*,
unscoped::UnscopedPayload,
TokenProviderError,
types::{
application_credential::ApplicationCredentialPayload, domain_scoped::DomainScopePayload,
federation_domain_scoped::FederationDomainScopePayload,
federation_project_scoped::FederationProjectScopePayload,
federation_unscoped::FederationUnscopedPayload, project_scoped::ProjectScopePayload,
restricted::RestrictedPayload, unscoped::UnscopedPayload, *,
},
};
use utils::FernetUtils;

mod application_credential;
mod restricted;
pub mod utils;

#[derive(Clone)]
pub struct FernetTokenProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,74 +12,15 @@
//
// SPDX-License-Identifier: Apache-2.0

use chrono::{DateTime, Utc};
use derive_builder::Builder;
use rmp::{decode::read_pfix, encode::write_pfix};
use serde::Serialize;
use std::io::Write;

use crate::assignment::types::Role;
use crate::identity::types::UserResponse;
use crate::resource::types::Project;
use crate::token::{
backend::fernet::{FernetTokenProvider, MsgPackToken, utils},
error::TokenProviderError,
fernet::{FernetTokenProvider, MsgPackToken},
fernet_utils,
types::Token,
types::ApplicationCredentialPayload,
};

#[derive(Builder, Clone, Debug, Default, PartialEq, Serialize)]
#[builder(setter(into))]
pub struct ApplicationCredentialPayload {
pub user_id: String,
#[builder(default, setter(name = _methods))]
pub methods: Vec<String>,
#[builder(default, setter(name = _audit_ids))]
pub audit_ids: Vec<String>,
pub expires_at: DateTime<Utc>,
pub project_id: String,
pub application_credential_id: String,

#[builder(default)]
pub issued_at: DateTime<Utc>,
#[builder(default)]
pub user: Option<UserResponse>,
#[builder(default)]
pub roles: Vec<Role>,
#[builder(default)]
pub project: Option<Project>,
}

impl ApplicationCredentialPayloadBuilder {
pub fn methods<I, V>(&mut self, iter: I) -> &mut Self
where
I: Iterator<Item = V>,
V: Into<String>,
{
self.methods
.get_or_insert_with(Vec::new)
.extend(iter.map(Into::into));
self
}

pub fn audit_ids<I, V>(&mut self, iter: I) -> &mut Self
where
I: Iterator<Item = V>,
V: Into<String>,
{
self.audit_ids
.get_or_insert_with(Vec::new)
.extend(iter.map(Into::into));
self
}
}

impl From<ApplicationCredentialPayload> for Token {
fn from(value: ApplicationCredentialPayload) -> Self {
Self::ApplicationCredential(value)
}
}

impl MsgPackToken for ApplicationCredentialPayload {
type Token = Self;

Expand All @@ -88,16 +29,16 @@ impl MsgPackToken for ApplicationCredentialPayload {
wd: &mut W,
fernet_provider: &FernetTokenProvider,
) -> Result<(), TokenProviderError> {
fernet_utils::write_uuid(wd, &self.user_id)?;
utils::write_uuid(wd, &self.user_id)?;
write_pfix(
wd,
fernet_provider.encode_auth_methods(self.methods.clone())?,
)
.map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?;
fernet_utils::write_uuid(wd, &self.project_id)?;
fernet_utils::write_time(wd, self.expires_at)?;
fernet_utils::write_audit_ids(wd, self.audit_ids.clone())?;
fernet_utils::write_uuid(wd, &self.application_credential_id)?;
utils::write_uuid(wd, &self.project_id)?;
utils::write_time(wd, self.expires_at)?;
utils::write_audit_ids(wd, self.audit_ids.clone())?;
utils::write_uuid(wd, &self.application_credential_id)?;

Ok(())
}
Expand All @@ -107,15 +48,15 @@ impl MsgPackToken for ApplicationCredentialPayload {
fernet_provider: &FernetTokenProvider,
) -> Result<Self::Token, TokenProviderError> {
// Order of reading is important
let user_id = fernet_utils::read_uuid(rd)?;
let user_id = utils::read_uuid(rd)?;
let methods: Vec<String> = fernet_provider
.decode_auth_methods(read_pfix(rd)?)?
.into_iter()
.collect();
let project_id = fernet_utils::read_uuid(rd)?;
let expires_at = fernet_utils::read_time(rd)?;
let audit_ids: Vec<String> = fernet_utils::read_audit_ids(rd)?.into_iter().collect();
let application_credential_id = fernet_utils::read_uuid(rd)?;
let project_id = utils::read_uuid(rd)?;
let expires_at = utils::read_time(rd)?;
let audit_ids: Vec<String> = utils::read_audit_ids(rd)?.into_iter().collect();
let application_credential_id = utils::read_uuid(rd)?;

Ok(Self {
user_id,
Expand All @@ -134,8 +75,8 @@ mod tests {
use chrono::{Local, SubsecRound};
use uuid::Uuid;

use super::super::tests::setup_config;
use super::*;
use crate::token::tests::setup_config;

#[test]
fn test_roundtrip() {
Expand Down
111 changes: 111 additions & 0 deletions src/token/backend/fernet/restricted.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//! Restricted token Fernet implementation.

use rmp::{decode::read_pfix, encode::write_pfix};
use std::io::Write;

use crate::token::{
backend::fernet::{FernetTokenProvider, MsgPackToken, utils},
error::TokenProviderError,
types::RestrictedPayload,
};

impl MsgPackToken for RestrictedPayload {
type Token = Self;

fn assemble<W: Write>(
&self,
wd: &mut W,
fernet_provider: &FernetTokenProvider,
) -> Result<(), TokenProviderError> {
utils::write_uuid(wd, &self.user_id)?;
write_pfix(
wd,
fernet_provider.encode_auth_methods(self.methods.clone())?,
)
.map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?;
utils::write_uuid(wd, &self.token_restriction_id)?;
utils::write_time(wd, self.expires_at)?;
utils::write_uuid(wd, &self.project_id)?;
utils::write_bool(wd, self.allow_renew)?;
utils::write_bool(wd, self.allow_rescope)?;
utils::write_audit_ids(wd, self.audit_ids.clone())?;

Ok(())
}

fn disassemble(
rd: &mut &[u8],
fernet_provider: &FernetTokenProvider,
) -> Result<Self::Token, TokenProviderError> {
// Order of reading is important
let user_id = utils::read_uuid(rd)?;
let methods: Vec<String> = fernet_provider
.decode_auth_methods(read_pfix(rd)?)?
.into_iter()
.collect();
let token_restriction_id = utils::read_uuid(rd)?;
let expires_at = utils::read_time(rd)?;
let project_id = utils::read_uuid(rd)?;
let allow_renew = utils::read_bool(rd)?;
let allow_rescope = utils::read_bool(rd)?;
let audit_ids: Vec<String> = utils::read_audit_ids(rd)?.into_iter().collect();
Ok(Self {
user_id,
methods,
expires_at,
audit_ids,
token_restriction_id,
project_id,
allow_renew,
allow_rescope,

..Default::default()
})
}
}

#[cfg(test)]
mod tests {
use chrono::{Local, SubsecRound};
use uuid::Uuid;

use super::*;
use crate::token::tests::setup_config;

#[test]
fn test_roundtrip() {
let token = RestrictedPayload {
user_id: Uuid::new_v4().simple().to_string(),
methods: vec!["openid".into()],
audit_ids: vec!["Zm9vCg".into()],
expires_at: Local::now().trunc_subsecs(0).into(),
token_restriction_id: "trid".into(),
project_id: "pid".into(),
allow_renew: true,
allow_rescope: true,
..Default::default()
};

let provider = FernetTokenProvider::new(setup_config());

let mut buf = vec![];
token.assemble(&mut buf, &provider).unwrap();
let encoded_buf = buf.clone();
let decoded =
RestrictedPayload::disassemble(&mut encoded_buf.as_slice(), &provider).unwrap();
assert_eq!(token, decoded);
}
}
File renamed without changes.
15 changes: 8 additions & 7 deletions src/token/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//! Token provider errors.

use sea_orm::SqlErr;
use std::num::TryFromIntError;
Expand Down Expand Up @@ -119,49 +120,49 @@ pub enum TokenProviderError {
UnscopedBuilder {
/// The source of the error.
#[from]
source: crate::token::unscoped::UnscopedPayloadBuilderError,
source: crate::token::types::unscoped::UnscopedPayloadBuilderError,
},

#[error(transparent)]
ProjectScopeBuilder {
/// The source of the error.
#[from]
source: crate::token::project_scoped::ProjectScopePayloadBuilderError,
source: crate::token::types::project_scoped::ProjectScopePayloadBuilderError,
},

#[error(transparent)]
DomainScopeBuilder {
/// The source of the error.
#[from]
source: crate::token::domain_scoped::DomainScopePayloadBuilderError,
source: crate::token::types::domain_scoped::DomainScopePayloadBuilderError,
},

#[error(transparent)]
FederationUnscopedBuilder {
/// The source of the error.
#[from]
source: crate::token::federation_unscoped::FederationUnscopedPayloadBuilderError,
source: crate::token::types::federation_unscoped::FederationUnscopedPayloadBuilderError,
},

#[error(transparent)]
FederationProjectScopeBuilder {
/// The source of the error.
#[from]
source: crate::token::federation_project_scoped::FederationProjectScopePayloadBuilderError,
source: crate::token::types::federation_project_scoped::FederationProjectScopePayloadBuilderError,
},

#[error(transparent)]
FederationDomainScopeBuilder {
/// The source of the error.
#[from]
source: crate::token::federation_domain_scoped::FederationDomainScopePayloadBuilderError,
source: crate::token::types::federation_domain_scoped::FederationDomainScopePayloadBuilderError,
},

#[error(transparent)]
RestrictedBuilder {
/// The source of the error.
#[from]
source: crate::token::restricted::RestrictedPayloadBuilderError,
source: crate::token::types::restricted::RestrictedPayloadBuilderError,
},

#[error(transparent)]
Expand Down
Loading
Loading