#include "content.hpp" #include "user.hpp" #include "strusCms.hpp" #include "captcha.hpp" #include "cracklib.hpp" #include #include #include #include #include #include "cryptopp/sha.h" #include "cryptopp/filters.h" #include "cryptopp/hex.h" #include #include #include namespace apps { // user user::user( strusCms &cms ) : master( cms ) { cms.dispatcher( ).assign( "/login", &user::login, this ); cms.mapper( ).assign( "login" ); cms.dispatcher( ).assign( "/logout", &user::logout, this ); cms.mapper( ).assign( "logout" ); cms.dispatcher( ).assign( "/register", &user::register_user, this ); cms.mapper( ).assign( "register" ); cms.dispatcher( ).assign( "/confirm_register", &user::confirm_register, this ); //?? //~ cms.mapper( ).assign( "confirm_register" ); cms.dispatcher( ).assign( "/api/users", &user::api_users, this ); cms.dispatcher( ).assign( "/api/user/(\\w+)", &user::api_user, this, 1 ); } User user::getUserData( const std::string username ) { User user; cppdb::session sql( cms.conn ); cppdb::result r; r = sql << "SELECT username, printname, email FROM user WHERE username=?" << username << cppdb::row; if( r.empty( ) ) { return user; } r >> user.name; r >> user.printName; r >> user.email; return user; } void user::login( ) { content::user c( cms ); ini( c ); if( request( ).request_method( ) == "POST" && session( ).is_set( "prelogin" ) ) { c.login.load( context( ) ); if( c.login.validate( ) ) { session( ).reset_session( ); session( ).erase( "prelogin" ); session( )["username"] = c.login.username.value( ); session( ).expose( "username" ); User u = getUserData( c.login.username.value( ) ); session( )["printName"] = u.printName; session( ).expose( "printName" ); response( ).set_redirect_header( cms.root( ) ); } else { booster::ptime::sleep( booster::ptime( 5, 0 ) ); } } session( ).set( "prelogin", "" ); render( "login", c ); } void user::logout( ) { content::user c( cms ); session( ).clear( ); ini( c ); render( "logout", c ); } void user::register_user( ) { content::user c( cms ); ini( c ); if( request( ).request_method( ) == "POST" ) { c.register_user.load( context( ) ); if( c.register_user.validate( ) ) { std::string code = registration_start( c.register_user.username.value( ), c.register_user.password.value( ), c.register_user.printName.value( ), c.register_user.email.value( ) ); cms.mail.subject = "Registration request"; std::ostringstream oss; oss << "Your registration code is: " << code << "\n"; cms.mail.body = oss.str( ); cms.mail.to = c.register_user.email.value( ); cms.mail.send( ); if( cms.mail.hasError( ) ) { c.register_user.email.valid( false ); c.register_user.email.error_message( "Can't send email to this address" ); booster::ptime::sleep( booster::ptime( 5, 0 ) ); delete_user( c.register_user.username.value( ) ); std::cerr << "SEND MAIL ERROR: " << cms.mail.getLastError( ) << std::endl; } else { response( ).set_redirect_header( cms.root( ) + "/confirm_register" ); } } else { booster::ptime::sleep( booster::ptime( 5, 0 ) ); } } render( "register_user", c ); } void user::confirm_register( ) { content::user c( cms ); ini( c ); if( request( ).request_method( ) == "POST" ) { c.confirm_register.load( context( ) ); if( c.confirm_register.validate( ) ) { if( cms.user.verify_registration_code( c.confirm_register.code.value( ) ) ) { response( ).set_redirect_header( cms.root( ) + "/login" ); } else { booster::ptime::sleep( booster::ptime( 5, 0 ) ); c.confirm_register.code.valid( false ); } } else { booster::ptime::sleep( booster::ptime( 5, 0 ) ); } } render( "confirm_register", c ); } void user::api_users( ) { cppdb::session sql( cms.conn ); cppdb::result r; r = sql << "SELECT username, printname, email FROM user"; std::vector users; while( r.next( ) ) { User user; r >> user.name; r >> user.printName; r >> user.email; users.push_back( user ); } cppcms::json::value j; j = users; response( ).out( ) << j; } void user::api_user( std::string username ) { User user = getUserData( username ); cppcms::json::value j; j = user; response( ).out( ) << j; } // TODO: make this a salted hash bool user::check_login( const std::string user, const std::string password ) { if( user.empty( ) || password.empty( ) ) { return false; } cppdb::session sql( cms.conn ); cppdb::result r; r = sql << "SELECT id, password FROM user WHERE username=?" << user << cppdb::row; if( r.empty( ) ) { return false; } int id; r >> id; std::string pass; r >> pass; if( password != pass ) { return false; } std::time_t now_time = std::time( 0 ); std::tm now = *std::localtime( &now_time ); cppdb::statement stmt; stmt = sql << "INSERT INTO login(user_id, last_login) VALUES(?, ?)" << id << now; stmt.exec( ); return true; } bool user::user_exists( const std::string user ) { if( user.empty( ) ) { return false; } cppdb::session sql( cms.conn ); cppdb::result r; r = sql << "SELECT username FROM user WHERE username=?" << user << cppdb::row; if( r.empty( ) ) { return false; } return true; } namespace { std::string generate_token( ) { char chars[] = "ABCDEF1234567890"; std::string token; for( int i = 0; i < 12; i++ ) { token += chars[rand( ) % 16]; } return token; } std::string compute_token_hash( const std::string user, const std::string token ) { CryptoPP::SHA256 sha; std::ostringstream oss; oss << user << token; std::string source = oss.str( ); std::string value; CryptoPP::StringSource ss( source, true, new CryptoPP::HashFilter( sha, new CryptoPP::HexEncoder( new CryptoPP::StringSink( value ) ) ) ); return value; } } std::string user::registration_start( const std::string user, const std::string password, const std::string printName, const std::string email ) { std::time_t now_time = std::time( 0 ); std::tm now = *std::localtime( &now_time ); std::string token = generate_token( ); std::string code = compute_token_hash( user, token ); cppdb::session sql( cms.conn ); cppdb::statement stmt; stmt = sql << "INSERT INTO user(username, password, printName, email, status, registration_start, code ) VALUES( ?, ?, ?, ?, 'R', ?, ? )" << user << password << printName << email << now << code; stmt.exec( ); return code; } bool user::verify_registration_code( std::string code ) { cppdb::session sql( cms.conn ); cppdb::statement stmt; stmt = sql << "UPDATE user set status='A' WHERE code=?" << code; stmt.exec( ); if( stmt.affected( ) == 1 ) { return true; } return false; } void user::delete_user( std::string user ) { cppdb::session sql( cms.conn ); cppdb::statement stmt; stmt = sql << "DELETE FROM user WHERE username=?" << user; stmt.exec( ); } void user::ini( content::user &c ) { master::ini( c ); struct captcha ca = generateCaptcha( ); last_captcha = new_captcha; new_captcha = ca.text; c.captcha_base64 = ca.base64; } } // namespace apps namespace content { user::user( apps::strusCms &cms ) : login( cms ), register_user( cms ), confirm_register( cms ) { } // login login_form::login_form( apps::strusCms &cms ) : cppcms::form( ), cms( cms ) { username.message( "Your login" ); username.error_message( "The login is illegal" ); password.message( "Your password" ); password.error_message( "Your password is illegal" ); submit.value( "Log in" ); add( username ); add( password ); add( submit ); username.non_empty( ); password.non_empty( ); } bool login_form::validate( ) { if( !form::validate( ) ) { return false; } if( !cms.user.check_login( username.value( ), password.value( ) ) ) { username.valid( false ); password.valid( false ); password.clear( ); return false; } return true; } // register user register_user_form::register_user_form( apps::strusCms &cms ) : cppcms::form( ), cms( cms ) { username.message( "Your login" ); username.error_message( "Your login is illegal" ); printName.message( "Your real name (optional)" ); password.message( "Your password" ); password.error_message( "Your password is illegal" ); password2.message( "Your password (again)" ); password2.error_message( "Your password doesn't match" ); email.message( "Email" ); email.error_message( "Your email address is not valid" ); captcha.message( "Enter the correct captcha" ); captcha.error_message( "Captcha didn't match" ); submit.value( "Register user" ); add( username ); add( printName ); add( password ); add( password2 ); add( email ); add( captcha ); add( submit ); username.non_empty( ); password.non_empty( ); password2.non_empty( ); email.non_empty( ); captcha.non_empty( ); } bool register_user_form::validate( ) { if( !form::validate( ) ) { return false; } if( cms.user.user_exists( username.value( ) ) ) { username.valid( false ); password.valid( false ); username.error_message( "Username is taken" ); return false; } if( password.value( ).compare( password2.value( ) ) != 0 ) { password.valid( false ); password2.valid( false ); password2.error_message( "Passwords didn't match" ); return false; } PasswordCheck check = checkPassword( username.value( ), printName.value( ), password.value( ) ); if( !check.ok ) { password.valid( false ); password2.valid( false ); password.error_message( check.msg ); password2.error_message( check.msg ); } if( captcha.value( ).compare( cms.user.last_captcha ) != 0 ) { captcha.valid( false ); captcha.clear( ); return false; } return true; } // confirm user registration form confirm_register_form::confirm_register_form( apps::strusCms &cms ) : cppcms::form( ), cms( cms ) { code.error_message( "The code you provided is not correct" ); submit.value( "Verify" ); add( code ); add( submit ); code.non_empty( ); } bool confirm_register_form::validate( ) { if( !form::validate( ) ) { return false; } return true; } } // namespace content