mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
Open tickets for most todos, fix more.
This commit is contained in:
parent
ed99da58d0
commit
0fbd181f9f
|
@ -32,6 +32,7 @@ log = "0.4"
|
||||||
env_logger = "0.6"
|
env_logger = "0.6"
|
||||||
reqwest = "0.9"
|
reqwest = "0.9"
|
||||||
# reqwest = { path = "../reqwest" }
|
# reqwest = { path = "../reqwest" }
|
||||||
|
rand = "0.6"
|
||||||
|
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
cookie = "0.11"
|
cookie = "0.11"
|
||||||
|
@ -52,6 +53,7 @@ r2d2 = "0.8"
|
||||||
r2d2_sqlite = "0.7"
|
r2d2_sqlite = "0.7"
|
||||||
|
|
||||||
structopt = { version = "0.2", default-features = false }
|
structopt = { version = "0.2", default-features = false }
|
||||||
|
time = "0.1"
|
||||||
|
|
||||||
concread = "0.1"
|
concread = "0.1"
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@ use crate::event::{CreateEvent, DeleteEvent, EventOrigin, ModifyEvent, SearchEve
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AccessControlSearch {
|
pub struct AccessControlSearch {
|
||||||
acp: AccessControlProfile,
|
acp: AccessControlProfile,
|
||||||
// TODO: Make this a BTreeSet?
|
|
||||||
attrs: Vec<String>,
|
attrs: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,9 +131,7 @@ impl AccessControlDelete {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AccessControlCreate {
|
pub struct AccessControlCreate {
|
||||||
acp: AccessControlProfile,
|
acp: AccessControlProfile,
|
||||||
// TODO: Make this a BTreeSet?
|
|
||||||
classes: Vec<String>,
|
classes: Vec<String>,
|
||||||
// TODO: Make this a BTreeSet?
|
|
||||||
attrs: Vec<String>,
|
attrs: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,11 +190,8 @@ impl AccessControlCreate {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AccessControlModify {
|
pub struct AccessControlModify {
|
||||||
acp: AccessControlProfile,
|
acp: AccessControlProfile,
|
||||||
// TODO: Make this a BTreeSet?
|
|
||||||
classes: Vec<String>,
|
classes: Vec<String>,
|
||||||
// TODO: Make this a BTreeSet?
|
|
||||||
presattrs: Vec<String>,
|
presattrs: Vec<String>,
|
||||||
// TODO: Make this a BTreeSet?
|
|
||||||
remattrs: Vec<String>,
|
remattrs: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,7 +508,6 @@ pub trait AccessControlsTransaction {
|
||||||
&self,
|
&self,
|
||||||
audit: &mut AuditScope,
|
audit: &mut AuditScope,
|
||||||
se: &SearchEvent,
|
se: &SearchEvent,
|
||||||
// TODO: This could actually take a &mut Vec and modify in place!
|
|
||||||
entries: Vec<Entry<EntryValid, EntryCommitted>>,
|
entries: Vec<Entry<EntryValid, EntryCommitted>>,
|
||||||
) -> Result<Vec<Entry<EntryReduced, EntryCommitted>>, OperationError> {
|
) -> Result<Vec<Entry<EntryReduced, EntryCommitted>>, OperationError> {
|
||||||
/*
|
/*
|
||||||
|
@ -568,7 +561,8 @@ pub trait AccessControlsTransaction {
|
||||||
|
|
||||||
audit_log!(audit, "Related acs -> {:?}", related_acp);
|
audit_log!(audit, "Related acs -> {:?}", related_acp);
|
||||||
|
|
||||||
// Get the set of attributes requested by the caller - TODO: This currently
|
// Get the set of attributes requested by the caller
|
||||||
|
// TODO #69: This currently
|
||||||
// is ALL ATTRIBUTES, so we actually work here to just remove things we
|
// is ALL ATTRIBUTES, so we actually work here to just remove things we
|
||||||
// CAN'T see instead.
|
// CAN'T see instead.
|
||||||
|
|
||||||
|
@ -754,7 +748,7 @@ pub trait AccessControlsTransaction {
|
||||||
let scoped_acp: Vec<&AccessControlModify> = related_acp
|
let scoped_acp: Vec<&AccessControlModify> = related_acp
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|acm: &&AccessControlModify| {
|
.filter_map(|acm: &&AccessControlModify| {
|
||||||
// TODO: We are continually compiling and using these
|
// We are continually compiling and using these
|
||||||
// in a tight loop, so this is a possible oppurtunity
|
// in a tight loop, so this is a possible oppurtunity
|
||||||
// to cache or handle these filters better - filter compiler
|
// to cache or handle these filters better - filter compiler
|
||||||
// cache maybe?
|
// cache maybe?
|
||||||
|
|
|
@ -23,7 +23,7 @@ mod sqlite_be;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct IdEntry {
|
struct IdEntry {
|
||||||
// TODO: for now this is i64 to make sqlite work, but entry is u64 for indexing reasons!
|
// TODO #20: for now this is i64 to make sqlite work, but entry is u64 for indexing reasons!
|
||||||
id: i64,
|
id: i64,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
@ -53,8 +53,8 @@ pub trait BackendTransaction {
|
||||||
) -> Result<Vec<Entry<EntryValid, EntryCommitted>>, OperationError> {
|
) -> Result<Vec<Entry<EntryValid, EntryCommitted>>, OperationError> {
|
||||||
// Do things
|
// Do things
|
||||||
// Alloc a vec for the entries.
|
// Alloc a vec for the entries.
|
||||||
// TODO: Make this actually a good size for the result set ...
|
// TODO #8: Make this actually a good size for the result set ...
|
||||||
// TODO: Actually compute indexes here.
|
// TODO #8: Actually compute indexes here.
|
||||||
// So to make this use indexes, we can use the filter type and
|
// So to make this use indexes, we can use the filter type and
|
||||||
// destructure it to work out what we need to actually search (if
|
// destructure it to work out what we need to actually search (if
|
||||||
// possible) to create the candidate set.
|
// possible) to create the candidate set.
|
||||||
|
@ -357,6 +357,8 @@ impl BackendWriteTransaction {
|
||||||
// write them all
|
// write them all
|
||||||
for ser_entry in ser_entries {
|
for ser_entry in ser_entries {
|
||||||
// TODO: Prepared statement.
|
// TODO: Prepared statement.
|
||||||
|
// Actually, I'm not sure we can - prepared stmt are per-conn, and we don't
|
||||||
|
// hold conns for that long? Maybe we should just rely on the stmt cache in sqlite?
|
||||||
try_audit!(
|
try_audit!(
|
||||||
au,
|
au,
|
||||||
stmt.execute_named(&[
|
stmt.execute_named(&[
|
||||||
|
@ -396,7 +398,7 @@ impl BackendWriteTransaction {
|
||||||
|
|
||||||
self.internal_create(au, &dbentries)
|
self.internal_create(au, &dbentries)
|
||||||
|
|
||||||
// TODO: update indexes (as needed)
|
// TODO #8: update indexes (as needed)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,7 +435,7 @@ impl BackendWriteTransaction {
|
||||||
let data = serde_cbor::to_vec(&db_e).map_err(|_| OperationError::SerdeCborError)?;
|
let data = serde_cbor::to_vec(&db_e).map_err(|_| OperationError::SerdeCborError)?;
|
||||||
|
|
||||||
Ok(IdEntry {
|
Ok(IdEntry {
|
||||||
// TODO: Instead of getting these from the server entry struct , we could lookup
|
// TODO #8: Instead of getting these from the server entry struct , we could lookup
|
||||||
// uuid -> id in the index.
|
// uuid -> id in the index.
|
||||||
//
|
//
|
||||||
// relies on the uuid -> id index being correct (and implemented)
|
// relies on the uuid -> id index being correct (and implemented)
|
||||||
|
@ -582,7 +584,7 @@ impl BackendWriteTransaction {
|
||||||
} else {
|
} else {
|
||||||
Err(OperationError::ConsistencyError(vr))
|
Err(OperationError::ConsistencyError(vr))
|
||||||
}
|
}
|
||||||
// TODO: run re-index after db is restored
|
// TODO #8: run re-index after db is restored
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn commit(mut self) -> Result<(), OperationError> {
|
pub fn commit(mut self) -> Result<(), OperationError> {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use rand::prelude::*;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
@ -5,15 +6,16 @@ pub struct Configuration {
|
||||||
pub address: String,
|
pub address: String,
|
||||||
pub domain: String,
|
pub domain: String,
|
||||||
pub threads: usize,
|
pub threads: usize,
|
||||||
|
// db type later
|
||||||
pub db_path: String,
|
pub db_path: String,
|
||||||
pub maximum_request: usize,
|
pub maximum_request: usize,
|
||||||
// db type later
|
|
||||||
pub secure_cookies: bool,
|
pub secure_cookies: bool,
|
||||||
|
pub cookie_key: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Configuration {
|
impl Configuration {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Configuration {
|
let mut c = Configuration {
|
||||||
address: String::from("127.0.0.1:8080"),
|
address: String::from("127.0.0.1:8080"),
|
||||||
domain: String::from("127.0.0.1"),
|
domain: String::from("127.0.0.1"),
|
||||||
threads: 8,
|
threads: 8,
|
||||||
|
@ -21,9 +23,13 @@ impl Configuration {
|
||||||
maximum_request: 262144, // 256k
|
maximum_request: 262144, // 256k
|
||||||
// log type
|
// log type
|
||||||
// log path
|
// log path
|
||||||
// TODO: default true in prd
|
// TODO #63: default true in prd
|
||||||
secure_cookies: false,
|
secure_cookies: false,
|
||||||
}
|
cookie_key: [0; 32],
|
||||||
|
};
|
||||||
|
let mut rng = StdRng::from_entropy();
|
||||||
|
rng.fill(&mut c.cookie_key);
|
||||||
|
c
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_db_path(&mut self, p: &PathBuf) {
|
pub fn update_db_path(&mut self, p: &PathBuf) {
|
||||||
|
|
|
@ -7,6 +7,7 @@ use actix_web::{
|
||||||
|
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use futures::{future, Future, Stream};
|
use futures::{future, Future, Stream};
|
||||||
|
use time::Duration;
|
||||||
|
|
||||||
use crate::config::Configuration;
|
use crate::config::Configuration;
|
||||||
|
|
||||||
|
@ -349,6 +350,7 @@ pub fn create_server_core(config: Configuration) {
|
||||||
let max_size = config.maximum_request;
|
let max_size = config.maximum_request;
|
||||||
let secure_cookies = config.secure_cookies;
|
let secure_cookies = config.secure_cookies;
|
||||||
let domain = config.domain.clone();
|
let domain = config.domain.clone();
|
||||||
|
let cookie_key: [u8; 32] = config.cookie_key.clone();
|
||||||
|
|
||||||
// start the web server
|
// start the web server
|
||||||
actix_web::server::new(move || {
|
actix_web::server::new(move || {
|
||||||
|
@ -359,12 +361,18 @@ pub fn create_server_core(config: Configuration) {
|
||||||
// Connect all our end points here.
|
// Connect all our end points here.
|
||||||
.middleware(middleware::Logger::default())
|
.middleware(middleware::Logger::default())
|
||||||
.middleware(session::SessionStorage::new(
|
.middleware(session::SessionStorage::new(
|
||||||
// TODO: Signed prevents tampering. this 32 byte key MUST
|
// Signed prevents tampering. this 32 byte key MUST
|
||||||
// be generated (probably stored in DB for cross-host access)
|
// be generated (probably a cli option, and it's up to the
|
||||||
session::CookieSessionBackend::signed(&[0; 32])
|
// server process to coordinate these on hosts). IE an RODC
|
||||||
|
// could have a different key than our write servers to prevent
|
||||||
|
// disclosure of a writeable token in case of compromise. It does
|
||||||
|
// mean that you can't load balance between the rodc and the write
|
||||||
|
// though, but that's tottaly reasonable.
|
||||||
|
session::CookieSessionBackend::signed(&cookie_key)
|
||||||
// Limit to path?
|
// Limit to path?
|
||||||
// .path("/")
|
// .path("/")
|
||||||
//.max_age() duration of the token life TODO make this proper!
|
// TODO #63: make this configurable!
|
||||||
|
.max_age(Duration::hours(1))
|
||||||
// .domain(domain.as_str())
|
// .domain(domain.as_str())
|
||||||
// .same_site(cookie::SameSite::Strict) // constrain to the domain
|
// .same_site(cookie::SameSite::Strict) // constrain to the domain
|
||||||
// Disallow from js and ...?
|
// Disallow from js and ...?
|
||||||
|
|
|
@ -947,11 +947,9 @@ impl<VALID, STATE> Entry<VALID, STATE> {
|
||||||
|
|
||||||
pub fn attribute_substring(&self, attr: &str, subvalue: &str) -> bool {
|
pub fn attribute_substring(&self, attr: &str, subvalue: &str) -> bool {
|
||||||
match self.attrs.get(attr) {
|
match self.attrs.get(attr) {
|
||||||
Some(v_list) => {
|
Some(v_list) => v_list
|
||||||
v_list
|
.iter()
|
||||||
.iter()
|
.fold(false, |acc, v| if acc { acc } else { v.contains(subvalue) }),
|
||||||
.fold(false, |acc, v| if acc { acc } else { v.contains(subvalue) })
|
|
||||||
}
|
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,12 +29,6 @@ use crate::proto::v1::SearchRecycledRequest;
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
// Should the event Result have the log items?
|
|
||||||
// TODO: Remove seralising here - each type should
|
|
||||||
// have it's own result type!
|
|
||||||
|
|
||||||
// TODO: Every event should have a uuid for logging analysis
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct OpResult {}
|
pub struct OpResult {}
|
||||||
|
|
||||||
|
@ -119,7 +113,7 @@ impl Event {
|
||||||
let uat = uat.ok_or(OperationError::NotAuthenticated)?;
|
let uat = uat.ok_or(OperationError::NotAuthenticated)?;
|
||||||
|
|
||||||
let e = try_audit!(audit, qs.internal_search_uuid(audit, uat.uuid.as_str()));
|
let e = try_audit!(audit, qs.internal_search_uuid(audit, uat.uuid.as_str()));
|
||||||
// TODO: Now apply claims from the uat into the Entry
|
// TODO #64: Now apply claims from the uat into the Entry
|
||||||
// to allow filtering.
|
// to allow filtering.
|
||||||
|
|
||||||
Ok(Event {
|
Ok(Event {
|
||||||
|
@ -164,7 +158,7 @@ impl Event {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_impersonate(event: &Self) -> Self {
|
pub fn from_impersonate(event: &Self) -> Self {
|
||||||
// TODO: In the future, we could change some of this data
|
// TODO #64 ?: In the future, we could change some of this data
|
||||||
// to reflect the fact we are infact impersonating the action
|
// to reflect the fact we are infact impersonating the action
|
||||||
// rather than the user explicitly requesting it. Could matter
|
// rather than the user explicitly requesting it. Could matter
|
||||||
// to audits and logs to determine what happened.
|
// to audits and logs to determine what happened.
|
||||||
|
@ -342,7 +336,6 @@ pub struct CreateEvent {
|
||||||
// This may affect which plugins are run ...
|
// This may affect which plugins are run ...
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Should this actually be in createEvent handler?
|
|
||||||
impl CreateEvent {
|
impl CreateEvent {
|
||||||
pub fn from_request(
|
pub fn from_request(
|
||||||
audit: &mut AuditScope,
|
audit: &mut AuditScope,
|
||||||
|
@ -634,7 +627,6 @@ pub struct AuthEvent {
|
||||||
impl AuthEvent {
|
impl AuthEvent {
|
||||||
pub fn from_message(msg: AuthMessage) -> Result<Self, OperationError> {
|
pub fn from_message(msg: AuthMessage) -> Result<Self, OperationError> {
|
||||||
Ok(AuthEvent {
|
Ok(AuthEvent {
|
||||||
// TODO: Change to AuthMessage, and fill in uat?
|
|
||||||
event: None,
|
event: None,
|
||||||
step: AuthEventStep::from_authstep(msg.req.step, msg.sessionid)?,
|
step: AuthEventStep::from_authstep(msg.req.step, msg.sessionid)?,
|
||||||
})
|
})
|
||||||
|
@ -661,7 +653,6 @@ impl AuthEvent {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AuthResult {
|
pub struct AuthResult {
|
||||||
pub sessionid: Uuid,
|
pub sessionid: Uuid,
|
||||||
// TODO: Make this an event specific authstate type?
|
|
||||||
pub state: AuthState,
|
pub state: AuthState,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -692,8 +683,6 @@ impl WhoamiResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Are these part of the proto?
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PurgeTombstoneEvent {
|
pub struct PurgeTombstoneEvent {
|
||||||
pub event: Event,
|
pub event: Event,
|
||||||
|
|
|
@ -266,7 +266,7 @@ impl Filter<FilterInvalid> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This has to have two versions to account for ro/rw traits, because RS can't
|
// This has to have two versions to account for ro/rw traits, because RS can't
|
||||||
// monomorphise on the trait to call clone_value. An option is to make a fn that
|
// monomorphise on the trait to call clone_value. An option is to make a fn that
|
||||||
// takes "clone_value(t, a, v) instead, but that may have a similar issue.
|
// takes "clone_value(t, a, v) instead, but that may have a similar issue.
|
||||||
pub fn from_ro(
|
pub fn from_ro(
|
||||||
|
@ -345,15 +345,11 @@ impl FilterComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate(&self, schema: &SchemaTransaction) -> Result<FilterComp, SchemaError> {
|
pub fn validate(&self, schema: &SchemaTransaction) -> Result<FilterComp, SchemaError> {
|
||||||
// TODO:
|
|
||||||
// First, normalise (if possible)
|
|
||||||
// Then, validate
|
|
||||||
|
|
||||||
// Optimisation is done at another stage.
|
// Optimisation is done at another stage.
|
||||||
|
|
||||||
// This probably needs some rework
|
// This probably needs some rework
|
||||||
|
|
||||||
// TODO: Getting this each recursion could be slow. Maybe
|
// Getting this each recursion could be slow. Maybe
|
||||||
// we need an inner functon that passes the reference?
|
// we need an inner functon that passes the reference?
|
||||||
let schema_attributes = schema.get_attributes();
|
let schema_attributes = schema.get_attributes();
|
||||||
let schema_name = schema_attributes
|
let schema_name = schema_attributes
|
||||||
|
|
|
@ -12,7 +12,6 @@ use crate::proto::v1::{AuthAllowed, AuthCredential, AuthState};
|
||||||
enum CredState {
|
enum CredState {
|
||||||
Success(Vec<Claim>),
|
Success(Vec<Claim>),
|
||||||
Continue(Vec<AuthAllowed>),
|
Continue(Vec<AuthAllowed>),
|
||||||
// TODO: Should we have a reason in Denied so that we
|
|
||||||
Denied(&'static str),
|
Denied(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,27 +35,32 @@ impl CredHandler {
|
||||||
match self {
|
match self {
|
||||||
CredHandler::Anonymous => {
|
CredHandler::Anonymous => {
|
||||||
creds.iter().fold(
|
creds.iter().fold(
|
||||||
CredState::Denied("non-anonymous credential provided"),
|
CredState::Continue(vec![AuthAllowed::Anonymous]),
|
||||||
|acc, cred| {
|
|acc, cred| {
|
||||||
// TODO: if denied, continue returning denied.
|
// There is no "continuation" from this type - we only set it at
|
||||||
// TODO: if continue, contunue returning continue.
|
// the start assuming there is no values in the iter so we can tell
|
||||||
// How to do this correctly?
|
// the session to continue up to some timelimit.
|
||||||
|
match acc {
|
||||||
// There is no "continuation" from this type.
|
// If denied, continue returning denied.
|
||||||
match cred {
|
CredState::Denied(_) => acc,
|
||||||
AuthCredential::Anonymous => {
|
// We have a continue or success, it's important we keep checking here
|
||||||
// For anonymous, no claims will ever be issued.
|
// after the success, because if they sent "multiple" anonymous or
|
||||||
CredState::Success(Vec::new())
|
// they sent anon + password, we need to handle both cases. Double anon
|
||||||
}
|
// is okay, but anything else is instant failure, even if we already
|
||||||
|
// had a success.
|
||||||
_ => {
|
_ => {
|
||||||
// Should we have a reason in Denied so that we can say why denied?
|
match cred {
|
||||||
acc
|
AuthCredential::Anonymous => {
|
||||||
// CredState::Denied
|
// For anonymous, no claims will ever be issued.
|
||||||
|
CredState::Success(Vec::new())
|
||||||
|
}
|
||||||
|
_ => CredState::Denied("non-anonymous credential provided"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} // end match acc
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
} // end credhandler::anonymous
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +112,7 @@ impl AuthSession {
|
||||||
// Is the whole account locked?
|
// Is the whole account locked?
|
||||||
// What about in memory account locking? Is that something
|
// What about in memory account locking? Is that something
|
||||||
// we store in the account somehow?
|
// we store in the account somehow?
|
||||||
// TODO: Implement handler locking!
|
// TODO #59: Implement handler locking!
|
||||||
|
|
||||||
AuthSession {
|
AuthSession {
|
||||||
account: account,
|
account: account,
|
||||||
|
@ -149,7 +153,7 @@ impl AuthSession {
|
||||||
// Alternately, open a write, and commit the needed security metadata here
|
// Alternately, open a write, and commit the needed security metadata here
|
||||||
// now rather than async (probably better for lock-outs etc)
|
// now rather than async (probably better for lock-outs etc)
|
||||||
//
|
//
|
||||||
// TODO: Async message the account owner about the login?
|
// TODO #59: Async message the account owner about the login?
|
||||||
// If this fails, how can we in memory lock the account?
|
// If this fails, how can we in memory lock the account?
|
||||||
//
|
//
|
||||||
// The lockouts could also be an in-memory concept too?
|
// The lockouts could also be an in-memory concept too?
|
||||||
|
@ -159,7 +163,6 @@ impl AuthSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn valid_auth_mechs(&self) -> Vec<AuthAllowed> {
|
pub fn valid_auth_mechs(&self) -> Vec<AuthAllowed> {
|
||||||
// TODO: This needs logging ....
|
|
||||||
if self.finished {
|
if self.finished {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,7 +18,7 @@ pub struct IdmServer {
|
||||||
// variaous accounts, and we have a good idea of how to structure the
|
// variaous accounts, and we have a good idea of how to structure the
|
||||||
// in memory caches related to locking.
|
// in memory caches related to locking.
|
||||||
//
|
//
|
||||||
// TODO: This needs a mark-and-sweep gc to be added.
|
// TODO #60: This needs a mark-and-sweep gc to be added.
|
||||||
sessions: CowCell<BTreeMap<Uuid, AuthSession>>,
|
sessions: CowCell<BTreeMap<Uuid, AuthSession>>,
|
||||||
// Need a reference to the query server.
|
// Need a reference to the query server.
|
||||||
qs: QueryServer,
|
qs: QueryServer,
|
||||||
|
@ -39,7 +39,7 @@ pub struct IdmServerReadTransaction<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdmServer {
|
impl IdmServer {
|
||||||
// TODO: Make number of authsessions configurable!!!
|
// TODO #59: Make number of authsessions configurable!!!
|
||||||
pub fn new(qs: QueryServer) -> IdmServer {
|
pub fn new(qs: QueryServer) -> IdmServer {
|
||||||
IdmServer {
|
IdmServer {
|
||||||
sessions: CowCell::new(BTreeMap::new()),
|
sessions: CowCell::new(BTreeMap::new()),
|
||||||
|
@ -135,7 +135,6 @@ impl<'a> IdmServerWriteTransaction<'a> {
|
||||||
|
|
||||||
Ok(AuthResult {
|
Ok(AuthResult {
|
||||||
sessionid: sessionid,
|
sessionid: sessionid,
|
||||||
// TODO: Change this to a better internal type?
|
|
||||||
state: AuthState::Continue(next_mech),
|
state: AuthState::Continue(next_mech),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ impl Actor for IntervalActor {
|
||||||
type Context = actix::Context<Self>;
|
type Context = actix::Context<Self>;
|
||||||
|
|
||||||
fn started(&mut self, ctx: &mut Self::Context) {
|
fn started(&mut self, ctx: &mut Self::Context) {
|
||||||
// TODO: This timeout could be configurable from config?
|
// TODO #65: This timeout could be configurable from config?
|
||||||
ctx.run_interval(Duration::from_secs(PURGE_TIMEOUT), move |act, _ctx| {
|
ctx.run_interval(Duration::from_secs(PURGE_TIMEOUT), move |act, _ctx| {
|
||||||
act.purge_recycled();
|
act.purge_recycled();
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,7 +10,9 @@ extern crate actix_web;
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
extern crate r2d2;
|
extern crate r2d2;
|
||||||
extern crate r2d2_sqlite;
|
extern crate r2d2_sqlite;
|
||||||
|
extern crate rand;
|
||||||
extern crate rusqlite;
|
extern crate rusqlite;
|
||||||
|
extern crate time;
|
||||||
extern crate uuid;
|
extern crate uuid;
|
||||||
|
|
||||||
extern crate bytes;
|
extern crate bytes;
|
||||||
|
@ -39,11 +41,9 @@ mod async_log;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod audit;
|
mod audit;
|
||||||
mod be;
|
mod be;
|
||||||
// TODO: Should this be public?
|
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
mod entry;
|
mod entry;
|
||||||
mod event;
|
mod event;
|
||||||
// TODO: Does this need pub?
|
|
||||||
mod filter;
|
mod filter;
|
||||||
mod interval;
|
mod interval;
|
||||||
mod modify;
|
mod modify;
|
||||||
|
|
|
@ -60,9 +60,6 @@ pub struct ModifyList<VALID> {
|
||||||
mods: Vec<Modify>,
|
mods: Vec<Modify>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: ModifyList should be like filter and have valid/invalid to schema.
|
|
||||||
// Or do we not care because the entry will be invalid at the end?
|
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a ModifyList<ModifyValid> {
|
impl<'a> IntoIterator for &'a ModifyList<ModifyValid> {
|
||||||
type Item = &'a Modify;
|
type Item = &'a Modify;
|
||||||
type IntoIter = slice::Iter<'a, Modify>;
|
type IntoIter = slice::Iter<'a, Modify>;
|
||||||
|
|
|
@ -106,7 +106,7 @@ fn apply_memberof(
|
||||||
let mut mo_set: Vec<_> = groups
|
let mut mo_set: Vec<_> = groups
|
||||||
.iter()
|
.iter()
|
||||||
.map(|g| {
|
.map(|g| {
|
||||||
// TODO: This could be more effecient
|
// TODO #61: This could be more effecient
|
||||||
let mut v = vec![g.get_uuid().clone()];
|
let mut v = vec![g.get_uuid().clone()];
|
||||||
match g.get_ava("memberof") {
|
match g.get_ava("memberof") {
|
||||||
Some(mos) => {
|
Some(mos) => {
|
||||||
|
@ -129,8 +129,8 @@ fn apply_memberof(
|
||||||
|
|
||||||
// first add a purged memberof to remove all mo we no longer
|
// first add a purged memberof to remove all mo we no longer
|
||||||
// support.
|
// support.
|
||||||
// TODO: Could this be more efficient
|
// TODO #61: Could this be more efficient
|
||||||
// TODO: Could this affect replication? Or should the CL work out the
|
// TODO #68: Could this affect replication? Or should the CL work out the
|
||||||
// true diff of the operation?
|
// true diff of the operation?
|
||||||
let mo_purge = vec![
|
let mo_purge = vec![
|
||||||
Modify::Present("class".to_string(), "memberof".to_string()),
|
Modify::Present("class".to_string(), "memberof".to_string()),
|
||||||
|
@ -170,7 +170,7 @@ impl Plugin for MemberOf {
|
||||||
"memberof"
|
"memberof"
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We could make this more effecient by limiting change detection to ONLY member/memberof
|
// TODO #61: We could make this more effecient by limiting change detection to ONLY member/memberof
|
||||||
// attrs rather than any attrs.
|
// attrs rather than any attrs.
|
||||||
|
|
||||||
fn post_create(
|
fn post_create(
|
||||||
|
|
|
@ -12,6 +12,19 @@ use std::collections::HashSet;
|
||||||
|
|
||||||
pub struct Protected {}
|
pub struct Protected {}
|
||||||
|
|
||||||
|
// Here is the declaration of all the attrs that can be altered by
|
||||||
|
// a call on a system object. We trust they are allowed because
|
||||||
|
// schema will have checked this, and we don't allow class changes!
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ALLOWED_ATTRS: HashSet<&'static str> = {
|
||||||
|
let mut m = HashSet::new();
|
||||||
|
m.insert("must");
|
||||||
|
m.insert("may");
|
||||||
|
m
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl Plugin for Protected {
|
impl Plugin for Protected {
|
||||||
fn id() -> &'static str {
|
fn id() -> &'static str {
|
||||||
"plugin_protected"
|
"plugin_protected"
|
||||||
|
@ -102,15 +115,9 @@ impl Plugin for Protected {
|
||||||
Modify::Removed(a, _) => a,
|
Modify::Removed(a, _) => a,
|
||||||
Modify::Purged(a) => a,
|
Modify::Purged(a) => a,
|
||||||
};
|
};
|
||||||
// TODO: This should be a set or some kind of structure?
|
match ALLOWED_ATTRS.get(a.as_str()) {
|
||||||
// I had issues statically allocating this though ... alternately
|
Some(_) => Ok(()),
|
||||||
// we could make it a "configurable" type?
|
None => Err(OperationError::SystemProtectedObject),
|
||||||
if a == "must" {
|
|
||||||
Ok(())
|
|
||||||
} else if a == "may" {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(OperationError::SystemProtectedObject)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -46,7 +46,7 @@ impl QueryServerV1 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We could move most of the be/schema/qs setup and startup
|
// TODO #54: We could move most of the be/schema/qs setup and startup
|
||||||
// outside of this call, then pass in "what we need" in a cloneable
|
// outside of this call, then pass in "what we need" in a cloneable
|
||||||
// form, this way we could have seperate Idm vs Qs threads, and dedicated
|
// form, this way we could have seperate Idm vs Qs threads, and dedicated
|
||||||
// threads for write vs read
|
// threads for write vs read
|
||||||
|
@ -71,7 +71,7 @@ impl QueryServerV1 {
|
||||||
let query_server = QueryServer::new(be, schema);
|
let query_server = QueryServer::new(be, schema);
|
||||||
|
|
||||||
let mut audit_qsc = AuditScope::new("query_server_init");
|
let mut audit_qsc = AuditScope::new("query_server_init");
|
||||||
// TODO: Should the IDM parts be broken out to the IdmSerner?
|
// TODO #62: Should the IDM parts be broken out to the IdmServer?
|
||||||
// What's important about this initial setup here is that it also triggers
|
// What's important about this initial setup here is that it also triggers
|
||||||
// the schema and acp reload, so they are now configured correctly!
|
// the schema and acp reload, so they are now configured correctly!
|
||||||
// Initialise the schema core.
|
// Initialise the schema core.
|
||||||
|
@ -264,13 +264,14 @@ impl Handler<WhoamiMessage> for QueryServerV1 {
|
||||||
fn handle(&mut self, msg: WhoamiMessage, _: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: WhoamiMessage, _: &mut Self::Context) -> Self::Result {
|
||||||
let mut audit = AuditScope::new("whoami");
|
let mut audit = AuditScope::new("whoami");
|
||||||
let res = audit_segment!(&mut audit, || {
|
let res = audit_segment!(&mut audit, || {
|
||||||
// TODO: Move this to IdmServer!!!
|
// TODO #62: Move this to IdmServer!!!
|
||||||
// Begin a read
|
// Begin a read
|
||||||
let qs_read = self.qs.read();
|
let qs_read = self.qs.read();
|
||||||
|
|
||||||
// Make an event from the whoami request. This will process the event and
|
// Make an event from the whoami request. This will process the event and
|
||||||
// generate a selfuuid search.
|
// generate a selfuuid search.
|
||||||
// TODO: This current handles the unauthenticated check, and will
|
//
|
||||||
|
// This current handles the unauthenticated check, and will
|
||||||
// trigger the failure, but if we can manage to work out async
|
// trigger the failure, but if we can manage to work out async
|
||||||
// then move this to core.rs, and don't allow Option<UAT> to get
|
// then move this to core.rs, and don't allow Option<UAT> to get
|
||||||
// this far.
|
// this far.
|
||||||
|
|
|
@ -68,12 +68,10 @@ pub struct UserAuthToken {
|
||||||
|
|
||||||
/* ===== low level proto types ===== */
|
/* ===== low level proto types ===== */
|
||||||
|
|
||||||
// TODO: We probably need a proto entry to transform our
|
// ProtoEntry vs Entry
|
||||||
// server core entry into. We also need to get from proto
|
// There is a good future reason for this seperation. It allows changing
|
||||||
// entry to our actual entry.
|
// the in memory server core entry type, without affecting the protoEntry type
|
||||||
//
|
//
|
||||||
// There is agood future reason for this seperation. It allows changing
|
|
||||||
// the in memory server core entry type, without affecting the proto
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Entry {
|
pub struct Entry {
|
||||||
|
|
|
@ -1183,7 +1183,7 @@ pub struct SchemaWriteTransaction<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SchemaWriteTransaction<'a> {
|
impl<'a> SchemaWriteTransaction<'a> {
|
||||||
// TODO: Schema probably needs to be part of the backend, so that commits are wholly atomic
|
// Schema probably needs to be part of the backend, so that commits are wholly atomic
|
||||||
// but in the current design, we need to open be first, then schema, but we have to commit be
|
// but in the current design, we need to open be first, then schema, but we have to commit be
|
||||||
// first, then schema to ensure that the be content matches our schema. Saying this, if your
|
// first, then schema to ensure that the be content matches our schema. Saying this, if your
|
||||||
// schema commit fails we need to roll back still .... How great are transactions.
|
// schema commit fails we need to roll back still .... How great are transactions.
|
||||||
|
|
|
@ -329,7 +329,7 @@ pub trait QueryServerTransaction {
|
||||||
//
|
//
|
||||||
// For passwords, hashing and changes will take place later.
|
// For passwords, hashing and changes will take place later.
|
||||||
//
|
//
|
||||||
// TODO: It could be argued that we should have a proper "Value" type, so that we can
|
// TODO #66: It could be argued that we should have a proper "Value" type, so that we can
|
||||||
// take care of this a bit cleaner, and do the checks in that, but I think for
|
// take care of this a bit cleaner, and do the checks in that, but I think for
|
||||||
// now this is good enough.
|
// now this is good enough.
|
||||||
fn clone_value(
|
fn clone_value(
|
||||||
|
@ -581,7 +581,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
||||||
|
|
||||||
// Log the request
|
// Log the request
|
||||||
|
|
||||||
// TODO: Do we need limits on number of creates, or do we constraint
|
// TODO #67: Do we need limits on number of creates, or do we constraint
|
||||||
// based on request size in the frontend?
|
// based on request size in the frontend?
|
||||||
|
|
||||||
// Copy the entries to a writeable form.
|
// Copy the entries to a writeable form.
|
||||||
|
@ -853,7 +853,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Has an appropriate amount of time/condition past (ie replication events?)
|
// TODO #68: Has an appropriate amount of time/condition past (ie replication events?)
|
||||||
|
|
||||||
// Delete them
|
// Delete them
|
||||||
let mut audit_be = AuditScope::new("backend_delete");
|
let mut audit_be = AuditScope::new("backend_delete");
|
||||||
|
|
Loading…
Reference in a new issue