Governance/session log#22415
Governance/session log#22415vitlibar merged 5 commits intoClickHouse:masterfrom Enmk:governance/session_log
Conversation
src/Access/AccessControlManager.h
Outdated
There was a problem hiding this comment.
Code style: please move const UUID & user_id to the next line.
docker/test/fasttest/run.sh
Outdated
src/Interpreters/SessionLog.cpp
Outdated
There was a problem hiding this comment.
I'm not sure we need the column authenticated by because it's already included in the system.users table. Or it's better to name it auth_type, i.e. exactly how it's named in system.users.
src/Interpreters/SessionLog.cpp
Outdated
There was a problem hiding this comment.
profiles and roles can be changed by a logged user via commands SET PROFILE and SET ROLE. Maybe it's worth to log those changes too, what do you think?
There was a problem hiding this comment.
well that could be a good extension to add after merging this PR
src/Interpreters/SessionLog.h
Outdated
There was a problem hiding this comment.
settings_profile seems to be not used
src/Interpreters/Context.cpp
Outdated
There was a problem hiding this comment.
Custom code in destructors rarely looks good. I think we can add a new member ext::scope_guard add_logout_event_to_session_log to the Context class, and set it in setUser() right after you add a login event to system.session_log. It allows to keep the destructor default, keep all session_log-related code in one place, and ensure that we add a logout event only if we add a login event.
There was a problem hiding this comment.
unfortunately, ext::scope_guard would make Context uncopyable and I would have to craft a custom full-blown constructor.
src/Interpreters/Context.cpp
Outdated
There was a problem hiding this comment.
I think it's better to move more code away from Context.cpp. We could add a function like
SessionLog::addLoginEvent(const Context & context)
which will collect all the required information from the session context, make a session log element and add it just in one function.
src/Interpreters/Context.cpp
Outdated
There was a problem hiding this comment.
We don't throw any exceptions not derived from std::exception, so this part seems to be unnecessary.
src/Access/SettingsProfilesCache.cpp
Outdated
There was a problem hiding this comment.
It's a bit more complicated. A parent profile can have its own parent profile, and so on. substituteProfiles() is a better place to collect all such parent profiles. And are you sure we need those parent profiles in system.session_log? Just a list of enabled roles isn't enough?
There was a problem hiding this comment.
IIRC profiles can be assigned not only to roles, but to users too, and maybe to something else. So roles alone are definitely not enough.
src/Interpreters/SessionLog.cpp
Outdated
There was a problem hiding this comment.
There are two kinds of active roles. Current roles are the roles which are chosen to be used by user. Enabled roles includes
- all the
current roles, and - all the roles which are granted to the
current roles, and - all the roles which are granted to
2, and - all the roles which are granted to
3and so on.
I mean maybe we want to see the current roles in the session_log table, not the enabled ones, what do you think?
src/Interpreters/SessionLog.cpp
Outdated
There was a problem hiding this comment.
It's maybe better to use Authentication::TypeInfo to fill those enum values. Just to repeat the names of these constants one less time.
There was a problem hiding this comment.
Maybe it would be better to turn your test to an integration test. Just to affect other tests less. But I don't insist.
src/Interpreters/Context.cpp
Outdated
There was a problem hiding this comment.
The function's name doesn't look good. The function doesn't terminate any session, because to terminate a session you kind of should destroy the corresponding TCPHandler. And because of you're definitely not going to do something like this here I think it's a bad name for the function. Let's rename the function to addLogOutToSessionLog() and move the condition where you check if it's a session context to the destructor.
src/Interpreters/Context.cpp
Outdated
There was a problem hiding this comment.
It's more obvious to pass *this (i.e. the context) to addLogOut(), not access. And the same thing for addLogIn(). Also access (the private variable) can be null, please use the function getAccess() (which never returns null) instead.
And too many checks here, it seems the following would be enough:
auto session_log = getSessionLog();
if (session_log)
session_log->addLogOut(*this);
Here no locks are required.
src/Interpreters/Context.cpp
Outdated
There was a problem hiding this comment.
I've thought about this more. It seems after all the destructor is simply not the best place for this logic. I believe we should call this function from handlers, i.e. from TCPHandler, HTTPHandler and so on. The reasons:
- Handlers provide more flexibility for the moment when we adds a logout event (for example we can do what we want with named sessions in
HTTPHandlerif we are not bound to the destructor); - Destructors look better without any logic except freeing the resources;
There was a problem hiding this comment.
Moved out all session-related code to a separate handle-like class: Session. So the log-out event is now saved in Session's destructor.
There was a problem hiding this comment.
I like your idea with the new Session class, it's nice to move some session-related logic outside of the quite overcomplicated Context class.
src/Access/ContextAccess.h
Outdated
There was a problem hiding this comment.
Let's name those profiles current, not active.
Likewise how roles are named: we have current roles and enabled roles. So we would have current profiles, and enabled profiles (enabled profiles would include current profiles, plus their parents, plus parents of those parents, and so on). Well you don't need enabled profiles for your PR but I think we still should name those profiles as current for consistency.
src/Access/ContextAccess.cpp
Outdated
There was a problem hiding this comment.
SettingsProfileCache keeps a map named all_profiles, which can be used to get all those names. Searching in the map is faster than the call manager->readName().
There was a problem hiding this comment.
You can easily implement this by adding a new member variable of type const SettingsProfileCache & to EnabledSettings and also the function EnabledSettings::getCurrentProfileNames().
There was a problem hiding this comment.
done, in a bit different way
src/Access/EnabledSettings.h
Outdated
There was a problem hiding this comment.
Let's insert one more And to make the name like setSettingsAndConstraintsAndProfiles(). Because settings and settings constraints are different things. Or I think we can call it simply set() because there is no other set function in this class.
src/Interpreters/Context.cpp
Outdated
src/Interpreters/Context.cpp
Outdated
There was a problem hiding this comment.
Please don't forget to remove the dead code like this.
src/Access/EnabledSettings.cpp
Outdated
There was a problem hiding this comment.
AppliedProfiles -> CurrentProfiles ?
src/Access/EnabledSettings.cpp
Outdated
There was a problem hiding this comment.
applied_profiles -> current_profiles ?
src/Access/EnabledSettings.h
Outdated
There was a problem hiding this comment.
The function should not return const std::vector<UUID> &. It should return either std::shared_ptr<const std::vector<UUID>> or std::vector<UUID> instead. Because if it returns const std::vector<UUID> & it can be read outside the internal lock and be modified in setSettingsAndConstraintsAndProfiles() at the same time.
src/Access/SettingsProfilesCache.cpp
Outdated
There was a problem hiding this comment.
It seems there is no point for this function to handle multiple profiles at once. This logic can be implemented in the caller and so it would allow make this function more simple. I mean this function can be declared instead as std::optional<String> getProfileName(const UUID & profile_id) const and the caller will iterate through IDs if it wants.
src/Interpreters/Context.h
Outdated
There was a problem hiding this comment.
Somebody else, this is non-owning pointer. I had to poke this "hole" to remove MySQL-specific struct from context. So the MySQL-stream can access the sequence_id from MySQLSession (which was previously in Context::mysql)
There was a problem hiding this comment.
Added a comment to clarify it
src/Interpreters/Session.cpp
Outdated
There was a problem hiding this comment.
What is context_to_copy? Is this the global context?
There was a problem hiding this comment.
That depends, usually it is a server context or something similar.
src/Interpreters/Session.h
Outdated
There was a problem hiding this comment.
Why does the function getUserAuthentication() exist? I think we already have the function setUser() for checking password.
There was a problem hiding this comment.
There are some complex authentication scenarios, e.g. in PostgreSql or MySQL handlers:
ClickHouse/src/Server/MySQLHandler.cpp
Line 255 in b298047
src/Interpreters/Session.h
Outdated
There was a problem hiding this comment.
So now we have two separate classes: Session and NamedSession which look completely different. It doesn't look good to me. Can we somehow combine them? For example we could add all the fields from NamedSession into Session and make the class NamedSessions to keep Sessions instead of NamedSessions.
There was a problem hiding this comment.
Removed NamedSession from Context.cpp, and now we have a NamedSessionData in the Session.cpp.
Which logs all the info about LogIn, LogOut and LogIn Failure events. Additional info that is logged: - User name - event type (LogIn, LogOut, LoginFailure) - Event date\time\time with microseconds - authentication type (same as for IDENTIFIED BY of CREATE USER statement) - array of active settings profiles upon login - array of active roles upon login - array of changed settings with corresponding values - client address and port - interface (TCP\HTTP\MySQL\PostgreSQL, etc.) - client info (name, version info) - optional LoginFailure reason text message. Added some tests to verify that events are properly saved with all necessary info via following interfaces: - TCP - HTTP - MySQL Known limitations - Not tested against named HTTP sessions, PostgreSQL and gRPC, hence those are not guaranteed to work 100% properly.
|
|
|
Internal documentation ticket: DOCSUP-14784. |
I hereby agree to the terms of the CLA available at: https://yandex.ru/legal/cla/?lang=en
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):
Logging all successful and failed login, and logout events to a new
system.session_logtable....
Detailed description / Documentation draft:
New system.session_log table, which has all the info about LogIn, LogOut and LogIn Failure events.
Info that is logged:
implementation note: User 'sessions' are implicit and rely on the proper co-operation from Context users.
Added some tests to verify that events are properly saved with all necessary info via the following interfaces:
...