1 /*
2  * RSessionContext.hpp
3  *
4  * Copyright (C) 2021 by RStudio, PBC
5  *
6  * Unless you have received this program directly from RStudio pursuant
7  * to the terms of a commercial license agreement with RStudio, then
8  * this program is licensed to you under the terms of version 3 of the
9  * GNU Affero General Public License. This program is distributed WITHOUT
10  * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12  * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13  *
14  */
15 
16 #ifndef CORE_R_UTIL_R_SESSION_CONTEXT_HPP
17 #define CORE_R_UTIL_R_SESSION_CONTEXT_HPP
18 
19 #include <string>
20 #include <vector>
21 #include <sstream>
22 #include <iomanip>
23 #include <iostream>
24 
25 #include <boost/function.hpp>
26 
27 #define kProjectNone               "none"
28 #define kUserIdLen                 5
29 #define kProjectIdLen              8
30 #define kProjectNoneId             "cfc78a31"
31 #define kJupyterLabId              "21f2ed72"
32 #define kJupyterNotebookId         "2cb256d2"
33 #define kWorkspacesId              "3c286bd3"
34 #define kVSCodeId                  "3c9ab5a7"
35 
36 #define kWorkbenchRStudio          "RStudio"
37 #define kWorkbenchJupyterLab       "JupyterLab"
38 #define kWorkbenchJupyterNotebook  "Jupyter Notebook"
39 #define kWorkbenchVSCode           "VS Code"
40 
41 #ifdef _WIN32
42 typedef unsigned int uid_t;
geteuid()43 inline uid_t geteuid()
44 {
45    return 0;
46 }
47 #endif
48 
49 namespace rstudio {
50 namespace core {
51 
52 class FilePath;
53 
54 namespace r_util {
55 
56 enum SessionScopeState
57 {
58    ScopeValid,
59    ScopeInvalidSession,
60    ScopeInvalidProject,
61    ScopeMissingProject,
62 };
63 
64 void setMinUid(uid_t minUid);
65 std::string obfuscatedUserId(uid_t uid);
66 
67 class ProjectId
68 {
69 public:
ProjectId()70    ProjectId()
71    {}
72 
ProjectId(const std::string & id)73    ProjectId(const std::string &id)
74    {
75       if (id.length() == kProjectIdLen)
76       {
77          id_ = id;
78          userId_ = obfuscatedUserId(::geteuid());
79       }
80       else if (id.length() == (kProjectIdLen + kUserIdLen))
81       {
82          userId_ = id.substr(0, kUserIdLen);
83          id_ = id.substr(kUserIdLen);
84       }
85    }
86 
ProjectId(const std::string & id,const std::string & userId)87    ProjectId(const std::string& id, const std::string& userId):
88       id_(id), userId_(userId)
89    {}
90 
ProjectId(const std::string & id,uid_t userId)91    ProjectId(const std::string& id, uid_t userId):
92       id_(id)
93    {
94       userId_ = obfuscatedUserId(userId);
95    }
96 
asString() const97    std::string asString() const
98    {
99       return userId_ + id_;
100    }
101 
operator ==(const ProjectId & other) const102    bool operator==(const ProjectId &other) const
103    {
104       return id_  == other.id_ && userId_ == other.userId_;
105    }
106 
operator <(const ProjectId & other) const107    bool operator<(const ProjectId &other) const
108    {
109        return id_ < other.id_ ||
110               (id_  == other.id_  && userId_  < other.userId_);
111    }
112 
id() const113    const std::string& id() const
114    {
115       return id_;
116    }
117 
userId() const118    const std::string& userId() const
119    {
120       return userId_;
121    }
122 
123 private:
124    std::string id_;
125    std::string userId_;
126 };
127 
128 typedef boost::function<std::string(const ProjectId&)> ProjectIdToFilePath;
129 typedef boost::function<ProjectId(const std::string&)> FilePathToProjectId;
130 
131 class SessionScope
132 {
133 private:
SessionScope(const ProjectId & project,const std::string & id)134    SessionScope(const ProjectId& project, const std::string& id)
135       : project_(project), id_(id)
136    {
137    }
138 
139 public:
140 
141    static SessionScope fromProject(
142                            const std::string& project,
143                            const std::string& id,
144                            const FilePathToProjectId& filePathToProjectId);
145 
146    static std::string projectPathForScope(
147                            const SessionScope& scope,
148                            const ProjectIdToFilePath& projectIdToFilePath);
149 
150    static SessionScope fromProjectId(const ProjectId& project,
151                                      const std::string& id);
152 
153    static SessionScope projectNone(const std::string& id);
154 
155    static SessionScope jupyterLabSession(const std::string& id);
156    static SessionScope jupyterNotebookSession(const std::string& id);
157 
158    static SessionScope vscodeSession(const std::string& id);
159 
SessionScope()160    SessionScope()
161    {
162    }
163 
164    bool isProjectNone() const;
165 
166    bool isWorkspaces() const;
167 
168    bool isJupyter() const;
169    bool isJupyterLab() const;
170    bool isJupyterNotebook() const;
171    bool isVSCode() const;
172 
173    std::string workbench() const;
174 
project() const175    const std::string project() const { return project_.asString(); }
176 
id() const177    const std::string& id() const { return id_; }
178 
projectId() const179    const ProjectId& projectId() const { return project_; }
180 
empty() const181    bool empty() const { return project_.id().empty(); }
182 
operator ==(const SessionScope & other) const183    bool operator==(const SessionScope &other) const {
184       return project_ == other.project_ && id_ == other.id_;
185    }
186 
operator !=(const SessionScope & other) const187    bool operator!=(const SessionScope &other) const {
188       return !(*this == other);
189    }
190 
operator <(const SessionScope & other) const191    bool operator<(const SessionScope &other) const {
192        return project_ < other.project_ ||
193               (project_ == other.project_ && id_ < other.id_);
194    }
195 
196 private:
197    ProjectId project_;
198    std::string id_;
199 };
200 
201 SessionScopeState validateSessionScope(const SessionScope& scope,
202                           const core::FilePath& userHomePath,
203                           const core::FilePath& userScratchPath,
204                           core::r_util::ProjectIdToFilePath projectIdToFilePath,
205                           bool projectSharingEnabled,
206                           std::string* pProjectFilePath);
207 
208 bool isSharedPath(const std::string& projectPath,
209                   const core::FilePath& userHomePath);
210 
211 std::string urlPathForSessionScope(const SessionScope& scope);
212 
213 std::string createSessionUrl(const std::string& hostPageUrl,
214                              const SessionScope& scope);
215 
216 void parseSessionUrl(const std::string& url,
217                      SessionScope* pScope,
218                      std::string* pUrlPrefix,
219                      std::string* pUrlWithoutPrefix,
220                      std::string* pBaseUrl = nullptr);
221 
222 
223 struct SessionContext
224 {
SessionContextrstudio::core::r_util::SessionContext225    SessionContext()
226    {
227    }
228 
SessionContextrstudio::core::r_util::SessionContext229    explicit SessionContext(const std::string& username,
230                            const SessionScope& scope = SessionScope())
231       : username(username), scope(scope)
232    {
233    }
234    std::string username;
235    SessionScope scope;
236 
emptyrstudio::core::r_util::SessionContext237    bool empty() const { return username.empty(); }
238 
operator ==rstudio::core::r_util::SessionContext239    bool operator==(const SessionContext &other) const {
240       return username == other.username && scope == other.scope;
241    }
242 
operator <rstudio::core::r_util::SessionContext243    bool operator<(const SessionContext &other) const {
244        return username < other.username ||
245               (username == other.username && scope < other.scope);
246    }
247 };
248 
249 
250 std::ostream& operator<< (std::ostream& os, const SessionContext& context);
251 
252 std::string sessionScopeFile(std::string prefix,
253                              const SessionScope& scope);
254 
255 std::string sessionScopePrefix(const std::string& username);
256 
257 std::string sessionScopesPrefix(const std::string& username);
258 
259 std::string sessionContextFile(const SessionContext& context);
260 
261 std::string generateScopeId();
262 std::string generateScopeId(const std::vector<std::string>& reserved);
263 
264 
265 } // namespace r_util
266 } // namespace core
267 } // namespace rstudio
268 
269 
270 #endif // CORE_R_UTIL_R_SESSION_CONTEXT_HPP
271