1 /*MT*
2
3 MediaTomb - http://www.mediatomb.cc/
4
5 auth.cc - this file is part of MediaTomb.
6
7 Copyright (C) 2005 Gena Batyan <bgeradz@mediatomb.cc>,
8 Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>
9
10 Copyright (C) 2006-2010 Gena Batyan <bgeradz@mediatomb.cc>,
11 Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>,
12 Leonhard Wimmer <leo@mediatomb.cc>
13
14 MediaTomb is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License version 2
16 as published by the Free Software Foundation.
17
18 MediaTomb is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 version 2 along with MediaTomb; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
26
27 $Id$
28 */
29
30 /// \file auth.cc
31
32 #include "pages.h" // API
33
34 #include <chrono>
35
36 #include "config/config_manager.h"
37 #include "session_manager.h"
38 #include "util/tools.h"
39
40 static constexpr auto LOGIN_TIMEOUT = std::chrono::seconds(10);
41
get_time()42 static std::chrono::seconds get_time()
43 {
44 return std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()).time_since_epoch();
45 }
46
generate_token()47 static std::string generate_token()
48 {
49 auto expiration = get_time() + LOGIN_TIMEOUT;
50 std::string salt = generateRandomId();
51 return fmt::format("{}_{}", expiration.count(), salt);
52 }
53
check_token(const std::string & token,const std::string & password,const std::string & encPassword)54 static bool check_token(const std::string& token, const std::string& password, const std::string& encPassword)
55 {
56 std::vector<std::string> parts = splitString(token, '_');
57 if (parts.size() != 2)
58 return false;
59 auto expiration = std::chrono::seconds(std::stol(parts[0]));
60 if (expiration < get_time())
61 return false;
62 std::string checksum = hexStringMd5(token + password);
63 return (checksum == encPassword);
64 }
65
Auth(std::shared_ptr<ContentManager> content)66 Web::Auth::Auth(std::shared_ptr<ContentManager> content)
67 : WebRequestHandler(std::move(content))
68 , timeout(std::chrono::seconds(60 * config->getIntOption(CFG_SERVER_UI_SESSION_TIMEOUT)))
69 {
70 }
71
process()72 void Web::Auth::process()
73 {
74 std::string action = param("action");
75 auto root = xmlDoc->document_element();
76
77 if (action.empty()) {
78 root.append_child("error").append_child(pugi::node_pcdata).set_value("req_type auth: no action given");
79 return;
80 }
81
82 if (action == "get_config") {
83 auto cfg = root.append_child("config");
84 cfg.append_attribute("accounts") = accountsEnabled();
85 cfg.append_attribute("show-tooltips") = config->getBoolOption(CFG_SERVER_UI_SHOW_TOOLTIPS);
86 cfg.append_attribute("poll-when-idle") = config->getBoolOption(CFG_SERVER_UI_POLL_WHEN_IDLE);
87 cfg.append_attribute("poll-interval") = config->getIntOption(CFG_SERVER_UI_POLL_INTERVAL);
88
89 /// CREATE XML FRAGMENT FOR ITEMS PER PAGE
90 auto ipp = cfg.append_child("items-per-page");
91 xml2JsonHints->setArrayName(ipp, "option");
92 ipp.append_attribute("default") = config->getIntOption(CFG_SERVER_UI_DEFAULT_ITEMS_PER_PAGE);
93
94 auto menu_opts = config->getArrayOption(CFG_SERVER_UI_ITEMS_PER_PAGE_DROPDOWN);
95 for (auto&& menu_opt : menu_opts) {
96 ipp.append_child("option").append_child(pugi::node_pcdata).set_value(menu_opt.c_str());
97 }
98
99 #ifdef HAVE_INOTIFY
100 cfg.append_attribute("have-inotify") = config->getBoolOption(CFG_IMPORT_AUTOSCAN_USE_INOTIFY);
101 #else
102 cfg.append_attribute("have-inotify") = false;
103 #endif
104
105 auto actions = cfg.append_child("actions");
106 xml2JsonHints->setArrayName(actions, "action");
107 //actions->appendTextChild("action", "fokel1");
108 //actions->appendTextChild("action", "fokel2");
109
110 auto friendlyName = cfg.append_child("friendlyName").append_child(pugi::node_pcdata);
111 friendlyName.set_value(config->getOption(CFG_SERVER_NAME).c_str());
112
113 auto gerberaVersion = cfg.append_child("version").append_child(pugi::node_pcdata);
114 gerberaVersion.set_value(GERBERA_VERSION);
115 } else if (action == "get_sid") {
116 log_debug("checking/getting sid...");
117 std::shared_ptr<Session> session;
118 std::string sid = param("sid");
119
120 if (sid.empty() || !(session = sessionManager->getSession(sid))) {
121 session = sessionManager->createSession(timeout);
122 root.append_attribute("sid_was_valid") = false;
123 } else {
124 session->clearUpdateIDs();
125 root.append_attribute("sid_was_valid") = true;
126 }
127 root.append_attribute("sid") = session->getID().c_str();
128
129 if (!session->isLoggedIn() && !accountsEnabled()) {
130 session->logIn();
131 //throw SessionException("not logged in");
132 }
133 root.append_attribute("logged_in") = session->isLoggedIn();
134 } else if (action == "logout") {
135 check_request();
136 std::string sid = param("sid");
137 auto session = sessionManager->getSession(sid);
138 if (!session)
139 throw_std_runtime_error("illegal session id");
140 sessionManager->removeSession(sid);
141 } else if (action == "get_token") {
142 check_request(false);
143
144 // sending token
145 std::string token = generate_token();
146 session->put("token", token);
147 root.append_child("token").append_child(pugi::node_pcdata).set_value(token.c_str());
148 } else if (action == "login") {
149 check_request(false);
150
151 // authentication
152 std::string username = param("username");
153 std::string encPassword = param("password");
154 std::string sid = param("sid");
155
156 if (username.empty() || encPassword.empty())
157 throw LoginException("Missing username or password");
158
159 auto session = sessionManager->getSession(sid);
160 if (!session)
161 throw_std_runtime_error("illegal session id");
162
163 std::string correctPassword = sessionManager->getUserPassword(username);
164
165 if (correctPassword.empty() || !check_token(session->get("token"), correctPassword, encPassword))
166 throw LoginException("Invalid username or password");
167
168 session->logIn();
169 } else
170 throw_std_runtime_error("illegal action given to req_type auth");
171 }
172