feat: add authorization for graphql
This commit is contained in:
parent
7ef8f7d2b2
commit
d51fc6fb8c
@ -22,4 +22,5 @@ chrono = { version = "*", features = ["serde"] }
|
||||
rust-crypto = "^0.2"
|
||||
juniper = "0.10"
|
||||
juniper_codegen = "0.10"
|
||||
juniper_rocket = "0.1.3"
|
||||
juniper_rocket = "0.1.3"
|
||||
rand = "0.6.0"
|
3
migrations/2018-11-21-070343_add token table/down.sql
Normal file
3
migrations/2018-11-21-070343_add token table/down.sql
Normal file
@ -0,0 +1,3 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
|
||||
DROP TABLE token;
|
9
migrations/2018-11-21-070343_add token table/up.sql
Normal file
9
migrations/2018-11-21-070343_add token table/up.sql
Normal file
@ -0,0 +1,9 @@
|
||||
-- Your SQL goes here
|
||||
-- Table Definition ----------------------------------------------
|
||||
|
||||
CREATE TABLE tokens (
|
||||
id SERIAL NOT NULL PRIMARY KEY,
|
||||
user_id integer NOT NULL REFERENCES users(id),
|
||||
value text NOT NULL,
|
||||
expire_at timestamp without time zone NOT NULL DEFAULT (CURRENT_TIMESTAMP + '1 day'::interval)
|
||||
);
|
@ -19,6 +19,8 @@ extern crate serde;
|
||||
extern crate serde_derive;
|
||||
extern crate tera;
|
||||
|
||||
extern crate rand;
|
||||
|
||||
|
||||
use dotenv::dotenv;
|
||||
use rocket_contrib::Template;
|
||||
@ -50,6 +52,8 @@ fn main() {
|
||||
article::single_article,
|
||||
article::get_article_by_url,
|
||||
article::static_content,
|
||||
|
||||
graphql::graphql_authorization,
|
||||
graphql::graphiql,
|
||||
graphql::get_graphql_handler,
|
||||
graphql::post_graphql_handler
|
||||
|
@ -2,15 +2,14 @@ use chrono::NaiveDateTime;
|
||||
use chrono::prelude::*;
|
||||
use crate::pg_pool::DbConn;
|
||||
use crate::request::ArticleEditForm;
|
||||
use crate::schema::articles;
|
||||
use crate::schema::articles::dsl::*;
|
||||
use crate::schema::setting;
|
||||
use crate::schema::users;
|
||||
use crypto::digest::Digest;
|
||||
use crypto::sha3::Sha3;
|
||||
use diesel::prelude::*;
|
||||
use diesel::result::Error;
|
||||
use rocket::request::FlashMessage;
|
||||
use crate::schema::{articles, users, tokens, setting};
|
||||
use rand;
|
||||
|
||||
|
||||
#[derive(Queryable, Debug, Serialize, Insertable, AsChangeset, GraphQLObject)]
|
||||
pub struct Article {
|
||||
@ -26,6 +25,8 @@ pub struct Article {
|
||||
|
||||
impl Article {
|
||||
pub fn load_all(include_unpublished: bool, conn: &DbConn) -> Vec<Article> {
|
||||
use crate::schema::articles::dsl::*;
|
||||
use crate::schema::articles;
|
||||
if include_unpublished {
|
||||
articles::table.order(publish_at.desc()).load::<Article>(&**conn).expect("something wrong")
|
||||
} else {
|
||||
@ -100,6 +101,23 @@ impl User {
|
||||
hasher.input_str(password);
|
||||
hasher.result_str()
|
||||
}
|
||||
|
||||
pub fn find_by_id(id:i32, conn: &DbConn) -> Option<User> {
|
||||
use crate::schema::users;
|
||||
let fetched_user = users::table.filter(users::id.eq(id)).first::<User>(&**conn);
|
||||
match fetched_user {
|
||||
Ok(user) => Some(user),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
pub fn find_by_username(username:&str, conn: &DbConn) -> Option<User> {
|
||||
use crate::schema::users;
|
||||
let fetched_user = users::table.filter(users::username.eq(username.to_string())).first::<User>(&**conn);
|
||||
match fetched_user {
|
||||
Ok(user) => Some(user),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive_FromForm]
|
||||
@ -125,4 +143,54 @@ impl <'a> SerializeFlashMessage<'a> {
|
||||
Some(f) => Some(SerializeFlashMessage{ name: &f.name().clone(), message: &f.msg().clone() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Queryable, Debug, Serialize, Insertable, AsChangeset, GraphQLObject)]
|
||||
pub struct Token {
|
||||
pub id: i32,
|
||||
pub user_id: i32,
|
||||
pub value: String,
|
||||
pub expire_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
#[derive(Insertable, AsChangeset)]
|
||||
#[table_name="tokens"]
|
||||
pub struct NewToken{
|
||||
pub user_id: i32,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
|
||||
impl Token {
|
||||
|
||||
pub fn new(user_id: i32, conn: &DbConn) -> Token {
|
||||
let token = NewToken{
|
||||
user_id: user_id,
|
||||
value: Token::rand(),
|
||||
};
|
||||
diesel::insert_into(tokens::table).values(&token).get_result(&**conn).expect("can not create token")
|
||||
}
|
||||
|
||||
pub fn validate(token:String, conn: &DbConn) -> Option<User> {
|
||||
use crate::schema::{tokens, tokens::dsl::*};
|
||||
let now = Utc::now().naive_utc();
|
||||
let fetched_token = tokens::table.filter(value.eq(token)).filter(expire_at.gt(now)).first::<Token>(&**conn);
|
||||
match fetched_token {
|
||||
Ok(token) => {
|
||||
User::find_by_id(token.user_id, conn)
|
||||
},
|
||||
Err(_) => None
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn rand() -> String {
|
||||
use rand::RngCore;
|
||||
let mut ret = String::new();
|
||||
for _ in (1..32) {
|
||||
ret.push((rand::random::<u8>() % 255) as char);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
@ -3,13 +3,35 @@ use crate::pg_pool::DbConn;
|
||||
use rocket::State;
|
||||
use crate::graphql::Schema;
|
||||
use juniper_rocket::{GraphQLRequest, GraphQLResponse};
|
||||
|
||||
use rocket_contrib::json::Json;
|
||||
use rocket::request::Form;
|
||||
use crate::request::LoginForm;
|
||||
use rocket::response::Failure;
|
||||
use rocket::http::Status;
|
||||
use crate::models::{User, Token};
|
||||
|
||||
#[get("/graphiql")]
|
||||
fn graphiql() -> content::Html<String> {
|
||||
juniper_rocket::graphiql_source("/graphql")
|
||||
}
|
||||
|
||||
#[post("/graphql/authorization", data = "<user>")]
|
||||
pub fn graphql_authorization(user: Form<LoginForm>, conn: DbConn) -> Result<Json<Token>, Failure> {
|
||||
let user_form = user.get();
|
||||
let fetched_user = User::find_by_username(&user_form.username, &conn);
|
||||
|
||||
if let None = fetched_user {
|
||||
return Err(Failure(Status::Unauthorized));
|
||||
}
|
||||
let user: User = fetched_user.unwrap();
|
||||
|
||||
if !user.authenticated(user_form.password.as_str()) {
|
||||
return Err(Failure(Status::Unauthorized));
|
||||
}
|
||||
Ok(Json(Token::new(user.id, &conn)))
|
||||
}
|
||||
|
||||
|
||||
#[get("/graphql?<request>")]
|
||||
fn get_graphql_handler(context: DbConn, request: GraphQLRequest, state: State<Schema>) -> GraphQLResponse {
|
||||
let schema = state;
|
||||
|
@ -17,6 +17,15 @@ table! {
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
tokens (id) {
|
||||
id -> Int4,
|
||||
user_id -> Int4,
|
||||
value -> Text,
|
||||
expire_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
users (id) {
|
||||
id -> Int4,
|
||||
@ -28,9 +37,11 @@ table! {
|
||||
}
|
||||
|
||||
joinable!(articles -> users (user_id));
|
||||
joinable!(tokens -> users (user_id));
|
||||
|
||||
allow_tables_to_appear_in_same_query!(
|
||||
articles,
|
||||
setting,
|
||||
tokens,
|
||||
users,
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user