ocpi 0.3.5

Unofficial, in progress, OCPI implementation
Documentation
//! # 7. Credentials module
//! Module Identifier: `credentials`
//! Type: Configuration Module
//! The credentials module is used to exchange the credentials token that has to
//! be used by parties for authorization of requests.
//! Every OCPI request is required to contain a credentials token in the
//! HTTP Authorization header.
use crate::{types, CommandsHandler, Context, Cpo, Party, PartyStore, Result, VersionsStore};
use async_trait::async_trait;

#[async_trait]
pub trait CredentialsModule {
    type PartyModel: Party;
    type RegistrationModel;

    /// # 7.2.1. GET Method
    /// Retrieves the credentials object to access the server’s platform.
    /// The request body is empty, the response contains the credentials
    /// object to access the server’s platform.
    /// This credentials object also contains extra information about the server
    /// such as its business details.
    async fn credentials_get(&self, ctx: Context) -> Result<types::Credential>;

    /// # 7.2.2. POST Method
    /// Provides the server with credentials to access the client’s system.
    /// This credentials object also contains extra information about the
    /// client such as its business details.
    /// A POST initiates the registration process for this endpoint’s version.
    /// The server must also fetch the client’s endpoints for this version.
    async fn credentials_post(
        &self,
        ctx: Context,
        param: types::Credential,
    ) -> Result<types::Credential>;

    /// # 7.2.3. PUT Method
    /// Provides the server with updated credentials to access the client’s system.
    /// This credentials object also contains extra information about the
    /// client such as its business details.
    /// A PUT will switch to the version that contains this credentials endpoint if
    /// it’s different from the current version.
    /// The server must fetch the client’s endpoints again, even if the version has not changed.
    /// If successful, the server must generate a new credentials token for the client and
    /// respond with the client’s updated credentials to access the server’s system.
    /// The credentials object in the response also contains extra information about the server
    /// such as its business details.
    /// This method MUST return a
    /// `HTTP status code 405: method not allowed`
    /// if the client has not been registered yet.
    async fn credentials_put(
        &self,
        ctx: Context,
        param: types::Credential,
    ) -> Result<types::Credential>;

    /// # 7.2.4. DELETE Method
    /// Informs the server that its credentials to access the client’s system are now
    /// invalid and can no longer be used.
    /// Both parties must end any automated communication.
    /// This is the unregistration process.
    /// This method __MUST__ return a HTTP status code
    /// 405: method not allowed if the client has not been registered before.
    async fn credentials_delete(&self, ctx: Context) -> Result<()>;
}

#[async_trait]
impl<DB, CH, M, Rm> CredentialsModule for Cpo<DB, CH>
where
    M: Party + Send + 'static,
    Rm: Send + 'static,
    CH: CommandsHandler,
    DB: PartyStore<PartyModel = M, RegistrationModel = Rm> + VersionsStore + Sync,
{
    type PartyModel = M;
    type RegistrationModel = Rm;

    async fn credentials_get(&self, ctx: Context) -> Result<types::Credential> {
        let party = self
            .db
            .get_authorized(ctx.credentials_token)
            .await?
            .party()?;

        let roles = self.db.get_our_roles().await?;

        Ok(types::Credential {
            token: party.token_they_use(),
            url: self.db.base_url(),
            roles,
        })
    }

    /// Provides credentials for the Server to access the Clients system.
    /// This is done using the Temporary model.
    /// If this the authentication of the provided Credential is succesful
    ///
    /// The provided Credentials will be used to create a new Model.
    /// The Credentials for this new Model is what is returned.
    async fn credentials_post(
        &self,
        ctx: Context,
        param: types::Credential,
    ) -> Result<types::Credential> {
        let reg = self
            .db
            .get_authorized(ctx.credentials_token.clone())
            .await?
            .registration()?;

        let version_details = self
            .client
            .get_endpoints_for_version(
                ctx.extend(&param.token),
                param.url.clone(),
                types::VersionNumber::V2_2,
            )
            .await?;

        let party = self.db.save_new_party(reg, param, version_details).await?;
        let roles = self.db.get_our_roles().await?;

        Ok(types::Credential {
            token: party.token_they_use(),
            url: self.db.base_url(),
            roles,
        })
    }

    async fn credentials_put(
        &self,
        ctx: Context,
        param: types::Credential,
    ) -> Result<types::Credential> {
        let party = self
            .db
            .get_authorized(ctx.credentials_token.clone())
            .await?
            .party()?;

        let version_details = self
            .client
            .get_endpoints_for_version(
                ctx.extend(&param.token),
                param.url.clone(),
                types::VersionNumber::V2_2,
            )
            .await?;

        let updated_party = self.db.update_party(party, param, version_details).await?;
        let roles = self.db.get_our_roles().await?;

        Ok(types::Credential {
            token: updated_party.token_they_use(),
            url: self.db.base_url(),
            // @TODO: Add a Store component that can retreive our own roles.
            // For now, dummy hardcode this stuff.
            roles,
        })
    }

    async fn credentials_delete(&self, ctx: Context) -> Result<()> {
        let party = self
            .db
            .get_authorized(ctx.credentials_token)
            .await?
            .party()?;

        let id = party.id();
        self.db.delete_party(id).await?;
        Ok(())
    }
}