1extern crate actix;
55extern crate actix_web;
56extern crate base64;
57extern crate chrono;
58extern crate failure;
59extern crate futures;
60extern crate rand;
61
62#[macro_use]
63extern crate diesel;
64#[macro_use]
65extern crate failure_derive;
66#[macro_use]
67extern crate log;
68
69mod sql;
70
71use chrono::prelude::Utc;
72use chrono::NaiveDateTime;
73
74use std::rc::Rc;
75
76use failure::Error;
77
78use actix::Addr;
79
80use actix_web::error::{self, Error as ActixWebError};
82use actix_web::http::header::HeaderValue;
83use actix_web::middleware::identity::{Identity, IdentityPolicy};
84use actix_web::middleware::Response as MiddlewareResponse;
85use actix_web::{HttpMessage, HttpRequest, HttpResponse};
86
87use futures::future::{err as FutErr, ok as FutOk};
89use futures::Future;
90
91use sql::{DeleteIdentity, FindIdentity, SqlActor, SqlIdentityModel, UpdateIdentity, Variant};
93
94use rand::Rng;
96
97const DEFAULT_RESPONSE_HDR: &'static str = "X-Actix-Auth";
98const DEFAULT_POOL_SIZE: usize = 3;
99
100#[derive(Debug, Fail)]
102enum SqlIdentityError {
103 #[allow(dead_code)]
104 #[fail(display = "sql variant not supported")]
105 SqlVariantNotSupported,
106
107 #[fail(display = "token not found")]
108 TokenNotFound,
109
110 #[fail(display = "token failed to set in header")]
111 TokenNotSet,
112
113 #[fail(display = "token not provided but required, bad request")]
114 TokenRequired,
115}
116
117enum SqlIdentityState {
118 Created,
119 Updated,
120 Deleted,
121 Unchanged,
122}
123
124pub struct SqlIdentity {
126 id: i64,
127 state: SqlIdentityState,
128 identity: Option<String>,
129 token: Option<String>,
130 ip: Option<String>,
131 user_agent: Option<String>,
132 created: NaiveDateTime,
133 inner: Rc<SqlIdentityInner>,
134}
135
136impl Identity for SqlIdentity {
137 fn identity(&self) -> Option<&str> {
139 self.identity.as_ref().map(|s| s.as_ref())
140 }
141
142 fn remember(&mut self, value: String) {
148 self.identity = Some(value);
149
150 let mut arr = [0u8; 24];
152 rand::thread_rng().fill(&mut arr[..]);
153 self.token = Some(base64::encode(&arr));
154
155 self.state = SqlIdentityState::Created;
156 }
157
158 fn forget(&mut self) {
160 self.identity = None;
161 self.state = SqlIdentityState::Deleted;
162 }
163
164 fn write(&mut self, resp: HttpResponse) -> Result<MiddlewareResponse, ActixWebError> {
170 match self.state {
171 SqlIdentityState::Created => {
172 self.state = SqlIdentityState::Unchanged;
173 Ok(MiddlewareResponse::Future(self.inner.create(self, resp)))
174 },
175
176 SqlIdentityState::Updated if self.token.is_some() && self.identity.is_some() => {
177 self.state = SqlIdentityState::Unchanged;
178 Ok(MiddlewareResponse::Future(self.inner.save(self, resp)))
179 }
180
181 SqlIdentityState::Deleted if self.token.is_some() => {
182 let token = self.token.as_ref().expect("[SIS::Deleted] Token is None!");
183 self.state = SqlIdentityState::Unchanged;
184 Ok(MiddlewareResponse::Future(self.inner.remove(token, resp)))
185 }
186
187 SqlIdentityState::Deleted | SqlIdentityState::Updated => {
188 Err(error::ErrorBadRequest(SqlIdentityError::TokenRequired))
190 }
191
192 _ => {
193 self.state = SqlIdentityState::Unchanged;
194 Ok(MiddlewareResponse::Done(resp))
195 }
196 }
197 }
198}
199
200struct SqlIdentityInner {
202 addr: Addr<SqlActor>,
203 hdr: &'static str,
204}
205
206impl SqlIdentityInner {
207 fn new(addr: Addr<SqlActor>, hdr: &'static str) -> SqlIdentityInner {
213 SqlIdentityInner { addr, hdr }
214 }
215
216 fn create(&self, identity: &SqlIdentity, mut resp: HttpResponse) -> Box<Future<Item = HttpResponse, Error = ActixWebError>> {
217 if let Some(ref token) = identity.token {
218 let headers = resp.headers_mut();
219
220 if let Ok(token) = token.parse() {
221 headers.append(self.hdr, token);
222 } else {
223 error!("Failed to parse token to place in header!");
224 return Box::new(FutErr(error::ErrorInternalServerError(
225 SqlIdentityError::TokenNotSet,
226 )));
227 }
228 } else {
229 error!("Identity token not set!");
230 return Box::new(FutErr(error::ErrorUnauthorized(
231 SqlIdentityError::TokenNotFound,
232 )));
233 }
234
235 Box::new(
236 self.addr
237 .send(UpdateIdentity::create(identity))
238 .map_err(ActixWebError::from)
239 .and_then(move |res| match res {
240 Ok(_) => Ok(resp),
241 Err(e) => {
242 error!("ERROR: {:?}", e);
243 Err(error::ErrorInternalServerError(e))
244 }
245 }),
246 )
247 }
248
249 fn save(
251 &self,
252 identity: &SqlIdentity,
253 resp: HttpResponse,
254 ) -> Box<Future<Item = HttpResponse, Error = ActixWebError>> {
255
256 Box::new(
257 self.addr
258 .send(UpdateIdentity::update(identity))
259 .map_err(ActixWebError::from)
260 .and_then(move |res| match res {
261 Ok(_) => Ok(resp),
262 Err(e) => {
263 error!("ERROR: {:?}", e);
264 Err(error::ErrorInternalServerError(e))
265 }
266 }),
267 )
268 }
269
270 fn remove(
272 &self,
273 token: &str,
274 resp: HttpResponse,
275 ) -> Box<Future<Item = HttpResponse, Error = ActixWebError>> {
276 Box::new(
277 self.addr
278 .send(DeleteIdentity {
279 token: token.to_string(),
280 })
281 .map_err(ActixWebError::from)
282 .and_then(move |res| match res {
283 Ok(_) => Ok(resp),
284 Err(e) => {
285 error!("ERROR: {:?}", e);
286 Err(error::ErrorInternalServerError(e))
287 }
288 }),
289 )
290 }
291
292 fn load<S>(
294 &self,
295 req: &HttpRequest<S>,
296 ) -> Box<Future<Item = Option<SqlIdentityModel>, Error = ActixWebError>> {
297 let headers = req.headers();
298 let auth_header = headers.get("Authorization");
299
300 if let Some(auth_header) = auth_header {
301 if let Ok(auth_header) = auth_header.to_str() {
304 let mut iter = auth_header.split(' ');
305 let scheme = iter.next();
306 let token = iter.next();
307
308 if scheme.is_some() && token.is_some() {
309 let _scheme = scheme.expect("[SII::load] Scheme is None!");
310 let token = token.expect("[SII::load] Token is None!");
311
312 return Box::new(
313 self.addr
314 .send(FindIdentity {
315 token: token.to_string(),
316 })
317 .map_err(ActixWebError::from)
318 .and_then(move |res| match res {
319 Ok(val) => Ok(Some(val)),
320 Err(e) => {
321 warn!("WARN: {:?}", e);
322 Ok(None)
323 }
324 }),
325 );
326 }
327 }
328 }
329
330 Box::new(FutOk(None))
331 }
332}
333
334#[derive(Clone)]
336pub struct SqlIdentityPolicy(Rc<SqlIdentityInner>);
337
338#[derive(Clone)]
339pub struct SqlIdentityBuilder {
340 pool: usize,
341 uri: String,
342 hdr: &'static str,
343 variant: Variant,
344}
345
346impl SqlIdentityBuilder {
347 pub fn new<T: Into<String>>(uri: T) -> SqlIdentityBuilder {
375 let uri: String = uri.into();
376 let variant = SqlIdentityBuilder::variant(&uri);
377
378 SqlIdentityBuilder {
379 pool: DEFAULT_POOL_SIZE,
380 uri: uri,
381 hdr: DEFAULT_RESPONSE_HDR,
382 variant: variant,
383 }
384 }
385
386 fn variant(uri: &str) -> Variant {
387 if uri.starts_with("mysql://") {
388 return Variant::Mysql;
389 } else if uri.starts_with("postgres://") || uri.starts_with("postgresql://") {
390 return Variant::Pg;
391 } else {
392 return Variant::Sqlite;
393 }
394 }
395
396 pub fn response_header(mut self, hdr: &'static str) -> SqlIdentityBuilder {
402 self.hdr = hdr;
403 self
404 }
405
406 pub fn pool_size(mut self, count: usize) -> SqlIdentityBuilder {
412 self.pool = count;
413 self
414 }
415
416 pub fn finish(self) -> Result<SqlIdentityPolicy, Error> {
422 info!("Registering identity provider: {:?}", self.variant);
423
424 Ok(SqlIdentityPolicy(Rc::new(SqlIdentityInner::new(
425 match self.variant {
426 Variant::Sqlite => SqlActor::sqlite(self.pool, &self.uri)?,
427 Variant::Mysql => SqlActor::mysql(self.pool, &self.uri)?,
428 Variant::Pg => SqlActor::pg(self.pool, &self.uri)?,
429 },
430 self.hdr,
431 ))))
432 }
433
434 pub fn sqlite(mut self) -> SqlIdentityBuilder {
439 self.variant = Variant::Sqlite;
440 self
441 }
442
443 pub fn mysql(mut self) -> SqlIdentityBuilder {
448 self.variant = Variant::Mysql;
449 self
450 }
451
452 pub fn postgresql(mut self) -> SqlIdentityBuilder {
457 self.variant = Variant::Pg;
458 self
459 }
460}
461
462impl<S> IdentityPolicy<S> for SqlIdentityPolicy {
463 type Identity = SqlIdentity;
464 type Future = Box<Future<Item = SqlIdentity, Error = ActixWebError>>;
465
466 fn from_request(&self, req: &HttpRequest<S>) -> Self::Future {
472 let inner = Rc::clone(&self.0);
473 let ip = req.connection_info()
474 .remote()
475 .unwrap_or("0.0.0.0")
476 .to_owned();
477 let unk = HeaderValue::from_static("Unknown");
478 let ua = req.headers()
479 .get("user-agent")
480 .unwrap_or(&unk)
481 .to_str()
482 .unwrap_or("Unknown")
483 .to_owned();
484
485 Box::new(self.0.load(req).map(move |ident| {
486 if let Some(id) = ident {
487 let uip = match id.ip {
488 Some(ref nip) if &ip == nip => nip.clone(),
489 _ => ip,
490 };
491
492 SqlIdentity {
493 id: id.id,
494 identity: Some(id.userid),
495 token: Some(id.token),
496 ip: Some(uip),
497 user_agent: Some(ua),
498 created: id.created,
499 state: SqlIdentityState::Updated,
500 inner: inner,
501 }
502 } else {
503 SqlIdentity {
504 id: -1,
505 identity: None,
506 token: None,
507 ip: Some(ip),
508 user_agent: Some(ua),
509 created: Utc::now().naive_utc(),
510 state: SqlIdentityState::Unchanged,
511 inner: inner,
512 }
513 }
514 }))
515 }
516}