Add authn tests

next
Sayan Nandan 3 years ago
parent ade5b2a105
commit f67057dfb5
No known key found for this signature in database
GPG Key ID: 8BC07A0A4D41DD52

5
.gitignore vendored

@ -12,4 +12,7 @@ snapstore.partmap
.sky_pid
.devcontainer
*.deb
*.pem
*.pem
server1
server2
.skytestenv

@ -1,9 +1,8 @@
# although this is exported by cargo, we'll export it again to use it in the Makefile
SHELL := /bin/bash
export ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
SKYTEST_TCP_PORT := 2003
SKYTEST_TLS_PORT := 2004
SKYTEST_TCP_PORT2 := 2005
SKYTEST_TLS_PORT2 := 2006
SKYTEST_S1_FILE := $(ROOT_DIR)/ci/server1.toml
SKYTEST_S2_FILE := $(ROOT_DIR)/ci/server2.toml
SKYTEST_TLS_CERT := $(ROOT_DIR)/cert.pem
SKYTEST_TLS_KEY := $(ROOT_DIR)/key.pem
# no additional software note
@ -36,6 +35,8 @@ TARGET_FOLDER := $(addsuffix release/,${TARGET_FOLDER})
CBUILD := cargo build $(TARGET_ARG)
# cargo test
CTEST := cargo test $(TARGET_ARG)
# cargo run
CRUN := cargo run $(TARGET_ARG)
# binary file paths
BINARY_SKYSH := $(TARGET_FOLDER)skysh
@ -45,7 +46,7 @@ BINARY_SKYMIGRATE := $(TARGET_FOLDER)sky-migrate
# archive command
ARCHIVE :=
# start background server command
START_SERVER := cargo run $(TARGET_ARG) -p skyd -- --noart --sslchain ${SKYTEST_TLS_CERT} --sslkey ${SKYTEST_TLS_KEY} --port ${SKYTEST_TCP_PORT} --sslport ${SKYTEST_TLS_PORT}
START_SERVER := $(CRUN) -p skyd -- --withconfig $(SKYTEST_S1_FILE)
STOP_SERVER :=
ifeq ($(OS),Windows_NT)
@ -69,8 +70,7 @@ else
STOP_SERVER := pkill skyd
endif
START_SERVER2 := $(subst $(SKYTEST_TCP_PORT),$(SKYTEST_TCP_PORT2),$(START_SERVER))
START_SERVER2 := $(subst $(SKYTEST_TLS_PORT),$(SKYTEST_TLS_PORT2),$(START_SERVER2))
START_SERVER2 := $(subst $(SKYTEST_S1_FILE),$(SKYTEST_S2_FILE),$(START_SERVER))
# update the archive command if we have a version and artifact name
RENAME_ARTIFACT :=
@ -139,17 +139,20 @@ test: .pre
@${SEP}
@${SEP}
@echo "Running all tests ..."
@${TEST}
@chmod +x ci/pretest.sh && source ci/pretest.sh && ${TEST}
@echo "Waiting for server to shut down ..."
@${STOP_SERVER}
@sleep 10
@echo "Removing temporary files ..."
@rm -f .sky_pid cert.pem key.pem
@rm -rf .sky_pid cert.pem key.pem server1 server2 .skytestenv
@${SEP}
clean:
@${SEP}
@echo "Cleaning up target folder ..."
cargo clean
@${SEP}
testclean:
@rm -rf server1 server2
deb: release-bundle
@${SEP}
@echo "Making a debian package (release) ..."
@ -158,3 +161,6 @@ deb: release-bundle
@echo "Packaging ..."
@cargo deb $(TARGET_ARG) --no-build --manifest-path=server/Cargo.toml --output skytable-${VERSION}-${ARTIFACT}.deb
@${SEP}
checkcmd:
@echo $(START_SERVER)
@echo $(START_SERVER2)

@ -0,0 +1,19 @@
#!/bin/bash
export ORIGIN_KEY=4527387f92a381cbe804593f33991d327d456a97
function ensure_eq() {
local q1=`${RUNSKYSH} "${1}" | tr -d '[:space:]' | sed -r 's/\x1B\[(;?[0-9]{1,3})+[mGK]//g'`
local q1e=${2}
if [[ "$q1" != "$q1e" ]]; then
echo "Expected '${q1e}', but got '${q1}' instead"
exit 1
fi
}
OKAY="(Okay)"
RUNSKYSH="cargo run ${TARGET} -p skysh -- --port 2005 -e"
export ROOTUSER_TOKEN=`${RUNSKYSH} "auth claim ${ORIGIN_KEY}" | tr -d '[:space:]' | tr -d "\""`
# login as root
export TESTUSER_TOKEN=`${RUNSKYSH} "auth login root ${ROOTUSER_TOKEN}" -e "auth adduser testuser"\
| head -n 2 | tail -n 1 | tr -d '[:space:]' | tr -d "\"" | sed -r 's/\x1B\[(;?[0-9]{1,3})+[mGK]//g'`
echo "TESTUSER_TOKEN=${TESTUSER_TOKEN}" >> .skytestenv
echo "ROOTUSER_TOKEN=${ROOTUSER_TOKEN}" >> .skytestenv

@ -0,0 +1,9 @@
[server]
host = "127.0.0.1"
port = 2003
noart = true
[ssl]
key="../key.pem"
chain="../cert.pem"
port = 2004

@ -0,0 +1,12 @@
[server]
host = "127.0.0.1"
port = 2005
noart = true
[auth]
origin_key="4527387f92a381cbe804593f33991d327d456a97"
[ssl]
key="../key.pem"
chain="../cert.pem"
port = 2006

@ -97,7 +97,7 @@ pub async fn start_repl() {
($editor:expr) => {
match $editor.readline(&skysh_blank) {
Ok(l) => l,
Err(ReadlineError::Interrupted) => return,
Err(ReadlineError::Interrupted | ReadlineError::Eof) => return,
Err(err) => fatal!("ERROR: Failed to read line with error: {}", err),
}
};
@ -143,9 +143,11 @@ pub async fn start_repl() {
};
}
if let Some(eval_expr) = matches.value_of("eval") {
if !eval_expr.is_empty() {
runner.run_query(eval_expr).await;
if let Some(eval_expr) = matches.values_of("eval") {
for eval_expr in eval_expr {
if !eval_expr.is_empty() {
runner.run_query(eval_expr).await;
}
}
process::exit(0x00);
}

@ -42,6 +42,7 @@ args:
short: e
required: false
long: eval
multiple: true
value_name: expression
help: Run an expression without REPL
takes_value: true

@ -27,3 +27,9 @@
#[sky_macros::dbtest_func(username = "abcd", password = "1234")]
#[should_panic]
async fn test_auth_fail() {}
#[sky_macros::dbtest_func(port = 2005, auth_testuser = true)]
async fn test_auth_testuser() {}
#[sky_macros::dbtest_func(port = 2005, auth_rootuser = true)]
async fn test_auth_rootuser() {}

@ -29,13 +29,16 @@ use proc_macro::TokenStream;
use quote::quote;
use syn::AttributeArgs;
type OptString = Option<String>;
pub struct DBTestFunctionConfig {
table_decl: String,
port: u16,
host: String,
tls_cert: Option<String>,
username: Option<String>,
password: Option<String>,
tls_cert: OptString,
login: (OptString, OptString),
testuser: bool,
rootuser: bool,
}
impl DBTestFunctionConfig {
@ -45,8 +48,9 @@ impl DBTestFunctionConfig {
port: 2003,
host: "127.0.0.1".to_owned(),
tls_cert: None,
username: None,
password: None,
login: (None, None),
testuser: false,
rootuser: false,
}
}
pub fn get_connection_tokens(&self) -> impl quote::ToTokens {
@ -86,19 +90,49 @@ impl DBTestFunctionConfig {
}
pub fn get_login_tokens(&self) -> Option<impl quote::ToTokens> {
let Self {
username, password, ..
login,
testuser,
rootuser,
..
} = self;
match (username, password) {
(Some(username), Some(password)) => Some(quote! {
let query = ::skytable::query!("auth", "login", #username, #password);
assert_eq!(
con.run_simple_query(&query).await.unwrap(),
::skytable::Element::RespCode(::skytable::RespCode::Okay)
);
}),
(None, None) => None,
_ => panic!("Expected both `username` and `password`"),
let conflict = (*rootuser && *testuser)
|| ((*rootuser || *testuser) && (login.0.is_some() || login.1.is_some()));
if conflict {
panic!("Expected either of `username` and `password`, or `auth_rootuser`, or `auth_testuser`");
}
let ret;
if *testuser {
ret = quote! {
let __username__ = "testuser";
let __password__ = ::std::env::var("TESTUSER_TOKEN").expect("TESTUSER_TOKEN unset");
};
} else if *rootuser {
ret = quote! {
let __username__ = "root";
let __password__ = ::std::env::var("ROOTUSER_TOKEN").expect("ROOTUSER_TOKEN unset");
};
} else {
let (username, password) = login;
match (username, password) {
(Some(username), Some(password)) => {
ret = quote! {
let __username__ = #username;
let __password__ = #password;
}
}
(None, None) => return None,
_ => panic!("Expected both `username` and `password`"),
}
}
Some(quote! {
#ret
let __loginquery__ = ::skytable::query!("auth", "login", __username__, __password__.clone());
assert_eq!(
con.run_simple_query(&__loginquery__).await.unwrap(),
::skytable::Element::RespCode(::skytable::RespCode::Okay),
"Failed to login"
);
})
}
}
@ -125,13 +159,19 @@ pub fn parse_dbtest_func_args(
fcfg.tls_cert = Some(util::parse_string(lit, span, "host").expect("Expected a string"));
}
"username" => {
fcfg.username =
fcfg.login.0 =
Some(util::parse_string(lit, span, "username").expect("Expected a string"))
}
"password" => {
fcfg.password =
fcfg.login.1 =
Some(util::parse_string(lit, span, "password").expect("Expected a string"))
}
"auth_testuser" => {
fcfg.testuser = util::parse_bool(lit, span, "auth_testuser").expect("Expected a bool")
}
"auth_rootuser" => {
fcfg.rootuser = util::parse_bool(lit, span, "auth_testuser").expect("Expected a bool")
}
x => panic!("unknown attribute {x} specified"),
}
}

@ -62,6 +62,17 @@ pub fn dbtest_module(args: TokenStream, item: TokenStream) -> TokenStream {
///
/// All tests will clean up all values once a single test is over. **These tests should not
/// be run in multi-threaded environments because they often use the same keys**
///
/// ## Arguments
/// - `table -> str`: Custom table declaration
/// - `port -> u16`: Custom port
/// - `host -> str`: Custom host
/// - `tls_cert -> str`: TLS cert (makes the connection a TLS one)
/// - `username -> str`: Username for authn
/// - `password -> str`: Password for authn
/// - `auth_testuser -> bool`: Login as the test user
/// - `auth_rootuser -> bool`: Login as the root user
///
/// ## _Ghost_ values
/// This macro gives:
/// - a `skytable::AsyncConnection` accessible by the `con` variable

@ -53,7 +53,7 @@ pub fn parse_string(int: &Lit, span: Span, field: &str) -> Result<String, syn::E
}
pub fn parse_number<T: FromStr<Err = E>, E: Display>(
int: &syn::Lit,
int: &Lit,
span: Span,
field: &str,
) -> Result<T, syn::Error> {
@ -61,7 +61,17 @@ pub fn parse_number<T: FromStr<Err = E>, E: Display>(
syn::Lit::Int(int) => int.base10_parse::<T>(),
_ => Err(syn::Error::new(
span,
format!("Failed to parse {} into a string.", field),
format!("Failed to parse {} into an int.", field),
)),
}
}
pub fn parse_bool(boolean: &Lit, span: Span, field: &str) -> Result<bool, syn::Error> {
match boolean {
Lit::Bool(boolean) => Ok(boolean.value),
_ => Err(syn::Error::new(
span,
format!("Failed to parse {} into a boolean.", field),
)),
}
}

Loading…
Cancel
Save