|
- ///////////////////////////////////////////////////////////////////////////////
- //
- // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
- //
- // See accompanying file COPYING.TXT file for licensing details.
- //
- ///////////////////////////////////////////////////////////////////////////////
- #ifndef CPPCMS_SESSION_INTERFACE_H
- #define CPPCMS_SESSION_INTERFACE_H
-
- #include <cppcms/defs.h>
- #include <booster/noncopyable.h>
- #include <booster/hold_ptr.h>
- #include <booster/shared_ptr.h>
- #include <cppcms/cstdint.h>
- #include <cppcms/cppcms_error.h>
- #include <cppcms/serialization_classes.h>
- #include <string>
- #include <map>
- #include <set>
- #include <booster/auto_ptr_inc.h>
- #include <sstream>
- #include <typeinfo>
-
- namespace cppcms {
- namespace impl {
- struct cached_settings;
- }
- namespace http {
- class context;
- class request;
- class response;
- class cookie;
- }
-
- class session_api;
- class session_pool;
-
- ///
- /// \brief This exception is thrown when CSRF attempt is suspected:
- ///
- class CPPCMS_API request_forgery_error : public cppcms_error {
- public:
- /// Create an exception object
- request_forgery_error() :
- cppcms_error("Cross site request forgery detected")
- {
- }
- };
-
-
- ///
- /// API to handle session cookies.
- ///
- /// This API allows two things:
- ///
- /// (a) Integration with 3rd part web technologies to access CppCMS session, i.e. using CppCMS session
- /// from PHP or Java Servlets
- ///
- /// (b) An API that allows to translate cookies session tracking system to a different
- /// method when cookies do not suite the design - for example for internal RPC
- /// systems, etc. Note incorrect use of non-cookies medium may expose you
- /// to security issues
- ///
- /// \ver{v1_2}
- class CPPCMS_API session_interface_cookie_adapter : public booster::noncopyable {
- public:
- virtual ~session_interface_cookie_adapter();
- ///
- /// Set a new cookie value
- ///
- virtual void set_cookie(http::cookie const &updated_cookie) = 0;
- ///
- /// Get value of a cookie, it is guaranted that name is what session_interface::session_cookie_name() returns
- ///
- virtual std::string get_session_cookie(std::string const &name) = 0;
- ///
- /// Get all cookie keys
- ///
- virtual std::set<std::string> get_cookie_names() = 0;
- };
-
- ///
- /// \brief This class provides an access to an application for session management
- ///
- /// Usually it is accessed via application::session member function.
- ///
- /// Note, when the application is asynchronous, the sessions should be loaded manually as
- /// fetching information from session may be not so cheap
- ///
- /// Generally, session data is represented as a map of key-value strings that can be read
- /// and written. All changes in session should be done before headers are written to the output
- /// (before requesting an output stream from http::response object)
- ///
- /// Each of the values in session may be also exposed to client as cookie. For example if
- /// you want to disclose "foo" session key to the client side (Java Script) and session
- /// cookie is cppcms_session=S231abc23c34ca242352a then a cookie with name
- /// cppcms_session_foo will be created caring the value of this key.
- ///
- /// Notes:
- /// - Be careful with values you pass, cookies can carry quite a limited range of strings
- /// so it is your responsibility to make sure that these values are actually legal.
- /// - Of course the client side can alter these cookies but this would not have an effect
- /// on the actual values fetched using session object. But still remember you should not
- /// relay on cookies values on server side for obvious security reasons.
- ///
- class CPPCMS_API session_interface : private booster::noncopyable {
- public:
-
- ///
- /// Create cppcms::service independent session interface to be used
- /// for implementing interoperability with non-cppcms based web platforms
- ///
- /// \ver{v1_2}
- session_interface(session_pool &pool,session_interface_cookie_adapter &adapter);
-
- ///
- /// Creates session interface for the context - never should be used by users
- /// directly
- ///
- session_interface(http::context &);
-
- ///
- /// destructor...
- ///
- ~session_interface();
- ///
- /// Check if a \a key is set (assigned some value to it) in the session
- ///
- bool is_set(std::string const &key);
- ///
- /// Erase specific \a key from the session
- ///
- void erase(std::string const &key);
- ///
- /// Remove all keys from the session and delete the session at all. (i.e. empty session is automatically deleted)
- ///
- void clear();
-
- ///
- /// Returns true if specific \a key is exposed to client via cookies
- ///
- bool is_exposed(std::string const &key);
- ///
- /// Set exposition of the \a key to client side, if val is true the value will be exposed, otherwise hidden and cookie
- /// will be deleted.
- ///
- void expose(std::string const &key,bool val=true);
- ///
- /// Disable exposition of a \a key. Same as expose(key,false);
- ///
- void hide(std::string const &key);
-
- ///
- /// Get the reference to a value for a \a key. Note if \a key is not exist, empty string is created in session object
- /// and reference to it returned (similarly to std::map's operator[])
- ///
- std::string &operator[](std::string const &key);
- ///
- /// Set value \a v for a session key \a key
- ///
- void set(std::string const &key,std::string const &v);
-
- ///
- /// Get a value for a session \a key. If it is not set, throws cppcms_error. It is good idea to call is_set before
- /// you call this function.
- ///
- std::string get(std::string const &key);
-
- ///
- /// Get a value for a session \a key. If it is not set, returns default_value
- ///
- std::string get(std::string const &key,std::string const &default_value);
-
- ///
- /// Get convert the value that is set for a key \a key to type T using std::iostream. For example you can
- /// read a number using int n=session().get<int>("number").
- ///
- /// - it throws cppcms_error if a key \a key not set/
- /// - it throws std::bad_cast of the conversion using std::iostream fails
- ///
- /// Note: the conversion is locale independent (uses C locale)
- ///
- template<typename T>
- T get(std::string const &key)
- {
- std::istringstream ss(get(key));
- ss.imbue(std::locale::classic());
- T value;
- ss>>value;
- if(ss.fail() || !ss.eof())
- throw booster::bad_cast();
- return value;
- }
-
- ///
- /// Assign a \a value of type \a T to \a key converting it to string using std::iostream.
- /// For example session().set("num",100);
- /// Note: the conversion is locale independent (uses C locale)
- ///
- template<typename T>
- void set(std::string const &key,T const &value)
- {
- std::ostringstream ss;
- ss.imbue(std::locale::classic());
- ss<<value;
- set(key,ss.str());
- }
-
- ///
- /// Serialize an \a object and store it under a \a key.
- ///
- /// The serialization is done using cppcms::serialization_traits
- ///
- template<typename Serializable>
- void store_data(std::string const &key,Serializable const &object)
- {
- std::string buffer;
- serialization_traits<Serializable>::save(object,buffer);
- set(key,buffer);
- }
-
- ///
- /// Fetch an \a object under \a key from the session deserializing it.
- ///
- /// The serialization is done using cppcms::serialization_traits
- ///
- /// Throws cppcms_error if the key is not set, may throw archive_error if deserialization fails
- /// (assuming that serialization uses cppcms::archive.
- ///
- template<typename Serializable>
- void fetch_data(std::string const &key,Serializable &object)
- {
- std::string buffer=get(key);
- serialization_traits<Serializable>::load(buffer,object);
- }
-
- ///
- /// This enum defines the way session timeout is managed
- ///
- enum {
- fixed, ///< Once the session is created it will expire in age() second from the moment it created
- renew, ///< Once the session expires in age() seconds of inactivity, once user sends an HTTP request again
- ///< it is renewed
- browser ///< The session is kept as long as browser keeps it (does not get closed). In addition the "renew" expiration
- ///< policy is valid. So if user does not close his browser but is not active, it will expire in age() seconds.
- };
-
- ///
- /// Get the maximal age of the session in seconds
- ///
- int age();
- ///
- /// Set the maximal age of the session in seconds
- ///
- void age(int t);
- ///
- /// Reset the maximal age of the session to default (session.timeout settings value or 24 hours if not set)
- ///
- void default_age();
-
- ///
- /// Get the expiration policy of the session: renew, fixed or browser
- ///
- int expiration();
- ///
- /// Set the expiration policy of the session: renew, fixed or browser
- ///
- void expiration(int h);
- ///
- /// Reset the expiration policy to the default (session.expire settings value or browser if not set)
- ///
- void default_expiration();
-
- ///
- /// Set store on server side option for session. If srv is true then the session will be always stored on server
- /// side and not in cookies only (required "server" or "both" type storage in session.location setting
- ///
- /// Rationale: client side storage using encrypted or signed cookies is very efficient, however it lacks of one
- /// important security feature: there is no other way to control their expiration but using timeout.
- /// So user may just revert the cookie to the old state to get back in time and restore its own session.
- ///
- /// So it is recommended to use server side storage in such critical cases, like soling captcha or playing a game
- /// where you can't return to previous status when storing the data in the session object.
- ///
- void on_server(bool srv);
-
- ///
- /// Get on_server session property
- ///
- bool on_server();
-
-
- ///
- /// Set the cookie that represents the current session (the value of the cookie)
- ///
- /// This function should be used only by user implementations of session storage
- ///
- void set_session_cookie(std::string const &data);
- ///
- /// Remove the cookie of the current session
- ///
- /// This function should be used only by user implementations of session storage
- ///
- void clear_session_cookie();
-
- ///
- /// Get the value of the cookie that represents session on the client
- ///
- /// This function should be used only by user implementations of session storage
- ///
- std::string get_session_cookie();
-
- ///
- /// Load the session, should be called one when dealing with sessions on asynchronous API where sessions
- /// are not loaded by default. This function returns true if any data was loaded.
- ///
- bool load();
-
- ///
- /// Set alternative cookies interface and load session data, returns same value as load, note
- /// if any data was loaded from cookies it would be discarded
- ///
- /// It can be used for use of an alternative session state medium
- ///
- /// \ver{v1_2}
- bool set_cookie_adapter_and_reload(session_interface_cookie_adapter &adapter);
-
- ///
- /// Save the session data, generally should not be called as it is saved automatically. However when
- /// writing asynchronous application and using custom slow storage devices like SQL it may be useful to control
- /// when and how save() is called.
- ///
- void save();
-
- ///
- /// Returns true if the underlying session back-end uses blocking API so it is unsuitable to be called
- /// from asynchronous event loop. This can be used to decide how to load session for specific connection.
- ///
- /// If the API is blocking you probably should load and save session from thread pool rather then
- /// from event loop when using asynchronous applications.
- ///
- bool is_blocking();
-
- ///
- /// When using session id based session - force generation of new session id to prevent session
- /// fixation attacks
- ///
- void reset_session();
-
-
- ///
- /// Check that the CSRF token is the same as in the session object, it does not do any checks, whether
- /// CSRF enabled or the request method is correct. It should be used for custom request handling (like
- /// custom content types for RESTful services.
- ///
- /// Returns true if the token is valid, otherwise returns false
- ///
- bool validate_csrf_token(std::string const &str);
- ///
- /// Check that there is no Cross Site Request Forgery Attempt.
- ///
- /// If CSRF checks enabled it validates that there is a valid CSRF token is submitted via POST request
- /// or via X-CSRF-Token header for AJAX requests.
- ///
- /// \note it is checked for POST requests only.
- ///
- void validate_request_origin();
-
- ///
- /// Set CSRF validation mode.
- ///
- /// If \a required \c is true then validate_request_origin() would throw \ref request_forgery_error
- /// if the CSRF token is not valid.
- ///
- /// Setting it to false would prevent from validate_request_origin() to do any checks.
- ///
- /// \note The default is defined in the configuration property \c security.csrf.automatic. If
- /// its value is not set the default is \c true
- ///
- /// It is useful when some parts of the application do not require CSRF validation regardless
- /// the status of sepecifc session owner
- ///
- void request_origin_validation_is_required(bool required);
-
- ///
- /// Get CSRF token that is stored in the session that can be used for validation
- /// of the request origin
- ///
- std::string get_csrf_token();
- ///
- /// Get the cooke name that holds CSRF token. Note it can be used only if security.csrf.exposed
- /// is set to true (which is not by default)
- ///
- std::string get_csrf_token_cookie_name();
-
- ///
- /// Get the session cookie name
- ///
- /// \ver{v1_2}
- std::string session_cookie_name();
-
- ///
- /// Retrun a set of keys that are defined for a current session;
- ///
- /// \ver{v1_2}
- std::set<std::string> key_set();
- private:
- friend class http::response;
- friend class http::request;
-
- void init();
-
- impl::cached_settings const &cached_settings();
-
- struct entry;
-
- typedef std::map<std::string,entry> data_type;
- data_type data_,data_copy_;
- http::context *context_;
-
- // Cached defaults
- int timeout_val_def_;
- int how_def_;
-
- // User Values
- int timeout_val_;
- int how_;
-
- // Information from session data
- time_t timeout_in_;
-
- uint32_t new_session_ : 1;
- uint32_t saved_ : 1;
- uint32_t on_server_ : 1;
- uint32_t loaded_ : 1;
- uint32_t reset_ : 1;
- uint32_t csrf_checked_ : 1;
- uint32_t csrf_do_validation_ : 1;
- uint32_t csrf_validation_ : 1;
- uint32_t reserved_ : 24;
-
- std::string temp_cookie_;
-
- // storage itself
-
- booster::shared_ptr<session_api> storage_;
- struct _data;
- booster::hold_ptr<_data> d; // for future use
-
- int cookie_age();
- time_t session_age();
-
- void check();
- void update_exposed(bool);
-
-
- void set_session_cookie(int64_t age,std::string const &data,std::string const &key=std::string());
-
- void save_data(std::map<std::string,entry> const &data,std::string &s);
- void load_data(std::map<std::string,entry> &data,std::string const &s);
- std::string generate_csrf_token();
- };
-
- } // cppcms
-
-
- #endif
|