pubnub 0.7.0

PubNub SDK for Rust
Documentation
use crate::common::{common_steps::PAMCurrentResourceType, PubNubWorld};
use cucumber::{given, then, when};
use pubnub::{access::permissions, core::PubNubError, parse_token, Token};
use std::collections::HashMap;
use std::ops::Not;

/// Add permission to specified `resource` in `resource` or `pattern`
/// permissions list.
fn add_permission(
    world: &mut PubNubWorld,
    name: Option<&String>,
    resource: Option<&String>,
    category: &String,
    permission: &String,
) {
    // Update current resource type if required.
    if let Some(resource) = resource {
        world.pam_state.resource_type = resource.into();
    }
    // Identify place, where permission should be stored.
    let resources = if category == "resource" {
        &mut world.pam_state.resource_permissions
    } else {
        &mut world.pam_state.pattern_permissions
    };

    match &world.pam_state.resource_type {
        PAMCurrentResourceType::Channel => {
            if let Some(name) = name {
                resources.channels.push(*permissions::channel(name));
            }

            if permission.is_empty() {
                return;
            }

            if let Some(perm) = resources.channels.pop() {
                match permission.as_str() {
                    "READ" => resources.channels.push(*perm.read()),
                    "WRITE" => resources.channels.push(*perm.write()),
                    "GET" => resources.channels.push(*perm.get()),
                    "MANAGE" => resources.channels.push(*perm.manage()),
                    "UPDATE" => resources.channels.push(*perm.update()),
                    "JOIN" => resources.channels.push(*perm.join()),
                    "DELETE" => resources.channels.push(*perm.delete()),
                    &_ => {}
                }
            }
        }
        PAMCurrentResourceType::ChannelGroup => {
            if let Some(name) = name {
                resources.groups.push(*permissions::channel_group(name));
            }

            if permission.is_empty() {
                return;
            }

            if let Some(perm) = resources.groups.pop() {
                match permission.as_str() {
                    "READ" => resources.groups.push(*perm.read()),
                    "MANAGE" => resources.groups.push(*perm.manage()),
                    &_ => {}
                }
            }
        }
        PAMCurrentResourceType::UserId => {
            if let Some(name) = name {
                resources.user_ids.push(*permissions::user_id(name));
            }

            if permission.is_empty() {
                return;
            }

            if let Some(perm) = resources.user_ids.pop() {
                match permission.as_str() {
                    "GET" => resources.user_ids.push(*perm.get()),
                    "UPDATE" => resources.user_ids.push(*perm.update()),
                    "DELETE" => resources.user_ids.push(*perm.delete()),
                    &_ => {}
                }
            }
        }
        _ => {}
    }
}

#[given("a token")]
fn given_token(world: &mut PubNubWorld) {
    given_token_string(world, "valid access token".to_string());
}

#[given(regex = r"^the token string '(.*)'$")]
fn given_token_string(world: &mut PubNubWorld, token: String) {
    world.pam_state.access_token = Some(token);
}

#[given("I have a known token containing an authorized UUID")]
#[given("I have a known token containing UUID resource permissions")]
#[given("I have a known token containing UUID pattern Permissions")]
#[given(regex = r"^a( valid|n expired) token with permissions to publish with channel .*$")]
#[given("The SDK is configured with an AuthKey representing an access Token")]
#[given("I have associated an access token with the SDK instance")]
fn given_token_with_authorized_uuid(world: &mut PubNubWorld) {
    given_token_string(world,
                       "qEF2AkF0GmEI03xDdHRsGDxDcmVzpURjaGFuoWljaGFubmVsLTEY70NncnChb2NoYW5uZWxfZ3JvdXAtMQVDdXNyoENzcGOgRHV1aWShZnV1aWQtMRhoQ3BhdKVEY2hhbqFtXmNoYW5uZWwtXFMqJBjvQ2dycKF0XjpjaGFubmVsX2dyb3VwLVxTKiQFQ3VzcqBDc3BjoER1dWlkoWpedXVpZC1cUyokGGhEbWV0YaBEdXVpZHR0ZXN0LWF1dGhvcml6ZWQtdXVpZENzaWdYIPpU-vCe9rkpYs87YUrFNWkyNq8CVvmKwEjVinnDrJJc".to_string());
}

#[given(regex = r#"^the authorized UUID "(.*)"$"#)]
fn given_authorized_uuid(world: &mut PubNubWorld, authorized_uuid: String) {
    world.pam_state.authorized_uuid = authorized_uuid.is_empty().not().then_some(authorized_uuid);
}

#[given(regex = r"^the TTL (\d+)$")]
fn given_token_ttl(world: &mut PubNubWorld, ttl: usize) {
    world.pam_state.ttl = Some(ttl);
}

#[given(regex = r"^the '(.*)' (CHANNEL|CHANNEL_GROUP|UUID) (resource|pattern) access permissions$")]
fn given_grant_token_resource(
    world: &mut PubNubWorld,
    name: String,
    resource: String,
    category: String,
) {
    add_permission(
        world,
        Some(&name),
        Some(&resource),
        &category,
        &String::new(),
    );
}

#[given(
    regex = r"^grant (resource|pattern) permission (READ|WRITE|MANAGE|DELETE|GET|UPDATE|JOIN)$"
)]
fn given_resource_permission(world: &mut PubNubWorld, category: String, permission: String) {
    add_permission(world, None, None, &category, &permission);
}

#[when(regex = r"^I (attempt to )?grant a token specifying those permissions")]
async fn grant_given_permissions(world: &mut PubNubWorld, _unused: String) {
    let resource_permissions = world.pam_state.resource_permissions.permissions();
    let pattern_permissions = world.pam_state.pattern_permissions.permissions();
    let client = world.get_pubnub(world.keyset.to_owned());
    let mut builder = client
        .grant_token(world.pam_state.ttl.unwrap())
        .resources(&resource_permissions)
        .patterns(&pattern_permissions);

    // Adding authorized user identifier if provided.
    if let Some(authorised_uuid) = &world.pam_state.authorized_uuid {
        builder = builder.authorized_user_id(authorised_uuid);
    }

    world.pam_state.grant_token_result = builder
        .execute()
        .await
        .map_err(|err| {
            if let PubNubError::API { .. } = err {
                world.api_error = Some(err.clone());
            }
            err
        })
        .map(|response| {
            world.pam_state.access_token = Some(response.token.clone());
            response
        });

    world.is_succeed = world.pam_state.grant_token_result.is_ok();
}

#[when("I revoke a token")]
async fn i_revoke_a_token(world: &mut PubNubWorld) {
    // Intentional `unwrap` to panic if for some reason step with token
    // specification not called.
    let token = world.pam_state.access_token.clone().unwrap();
    world.pam_state.revoke_token_result = world
        .get_pubnub(world.keyset.to_owned())
        .revoke_token(token)
        .execute()
        .await
        .map_err(|err| {
            if let PubNubError::API { .. } = err {
                world.api_error = Some(err.clone());
            }
            err
        });
    world.is_succeed = world.pam_state.revoke_token_result.is_ok();
}

#[then("I get confirmation that token has been revoked")]
fn i_receive_token_revoke_confirmation(world: &mut PubNubWorld) {
    assert!(world.is_succeed, "Expected successful response");
}

#[then(regex = r#"^the token contains the authorized UUID "(.*)"$"#)]
#[then(regex = r#"^the parsed token output contains the authorized UUID "(.*)"$"#)]
fn token_contains_authorization_uuid(world: &mut PubNubWorld, uuid: String) {
    let token_string = world.pam_state.access_token.clone().unwrap();
    let mut matched = false;
    if let Ok(Token::V2(token)) = parse_token(token_string.as_str()) {
        assert!(token.authorized_user_id.is_some());
        assert_eq!(token.authorized_user_id.unwrap(), uuid);
        matched = true;
    }
    assert!(matched);
}

#[then("the token does not contain an authorized uuid")]
fn token_not_contains_authorization_uuid(world: &mut PubNubWorld) {
    let token_string = world.pam_state.access_token.clone().unwrap();
    let mut matched = false;
    if let Ok(Token::V2(token)) = parse_token(token_string.as_str()) {
        assert!(token.authorized_user_id.is_none());
        matched = true;
    }
    assert!(matched);
}

#[then(regex = r"^the token contains the TTL (\d+)$")]
fn token_contains_ttl(world: &mut PubNubWorld, ttl: u32) {
    let token_string = world.pam_state.access_token.clone().unwrap();
    let mut matched = false;
    if let Ok(Token::V2(token)) = parse_token(token_string.as_str()) {
        assert!(token.ttl.gt(&0));
        assert_eq!(token.ttl, ttl);
        matched = true;
    }
    assert!(matched);
}

#[then(
    regex = r"^the token has '(.*)' (CHANNEL|CHANNEL_GROUP|UUID) (resource|pattern) access permissions$"
)]
fn token_contains_resource(
    world: &mut PubNubWorld,
    name: String,
    resource: String,
    category: String,
) {
    let token_string = world.pam_state.access_token.clone().unwrap();
    let mut matched = false;

    if let Ok(Token::V2(token)) = parse_token(token_string.as_str()) {
        let resources = if category == "resource" {
            token.resources
        } else {
            token.patterns
        };

        if resource == "CHANNEL" && resources.channels.contains_key(name.as_str()) {
            world.pam_state.resource_type = PAMCurrentResourceType::Channel;
            world.pam_state.resource_name = Some(name);
            matched = true;
        } else if resource == "CHANNEL_GROUP" && resources.groups.contains_key(name.as_str()) {
            world.pam_state.resource_type = PAMCurrentResourceType::ChannelGroup;
            world.pam_state.resource_name = Some(name);
            matched = true;
        } else if resource == "UUID" && resources.users.contains_key(name.as_str()) {
            world.pam_state.resource_type = PAMCurrentResourceType::UserId;
            world.pam_state.resource_name = Some(name);
            matched = true;
        }
    }
    assert!(matched);
}

#[given(
    regex = r"^token (resource|pattern) permission (READ|WRITE|MANAGE|DELETE|GET|UPDATE|JOIN)$"
)]
fn token_resource_has_permission(world: &mut PubNubWorld, category: String, permission: String) {
    let resource_name = world.pam_state.resource_name.clone().unwrap();
    let token_string = world.pam_state.access_token.clone().unwrap();
    let mut matched = false;

    if let Ok(parse_token::Token::V2(token)) = parse_token(token_string.as_str()) {
        let resources = if category == "resource" {
            token.resources
        } else {
            token.patterns
        };
        let resource_permission = match world.pam_state.resource_type {
            PAMCurrentResourceType::Channel => resources.channels,
            PAMCurrentResourceType::ChannelGroup => resources.groups,
            PAMCurrentResourceType::UserId => resources.users,
            PAMCurrentResourceType::None => HashMap::new(),
        };
        let token_permission = resource_permission.get(resource_name.as_str()).unwrap();
        matched = true;

        match permission.as_str() {
            "READ" => assert!(token_permission.read),
            "WRITE" => assert!(token_permission.write),
            "GET" => assert!(token_permission.get),
            "MANAGE" => assert!(token_permission.manage),
            "UPDATE" => assert!(token_permission.update),
            "JOIN" => assert!(token_permission.join),
            "DELETE" => assert!(token_permission.delete),
            &_ => matched = false,
        }
    }
    assert!(matched);
}

#[given(regex = r"^deny resource permission (READ|WRITE|MANAGE|DELETE|GET|UPDATE|JOIN)$")]
fn deny_token_permission(_world: &mut PubNubWorld, _permission: String) {
    // Don't add any permissions.
}

#[when("I parse the token")]
fn i_parse_token(_world: &mut PubNubWorld) {
    // Do nothing, actual parsing happens in step.
}