1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-29 22:01:14 +03:00

Make the last activity timestamp and IP available through the API

This commit is contained in:
Quentin Gliech
2023-09-19 19:25:58 +02:00
parent b85655b944
commit 50558a7319
17 changed files with 2562 additions and 2329 deletions

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::net::IpAddr;
use chrono::{DateTime, Utc};
use serde::Serialize;
use ulid::Ulid;
@ -80,6 +82,8 @@ pub struct CompatSession {
pub device: Device,
pub created_at: DateTime<Utc>,
pub is_synapse_admin: bool,
pub last_active_at: Option<DateTime<Utc>>,
pub last_active_ip: Option<IpAddr>,
}
impl std::ops::Deref for CompatSession {

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::net::IpAddr;
use chrono::{DateTime, Utc};
use oauth2_types::scope::Scope;
use serde::Serialize;
@ -73,6 +74,8 @@ pub struct Session {
pub user_session_id: Option<Ulid>,
pub client_id: Ulid,
pub scope: Scope,
pub last_active_at: Option<DateTime<Utc>>,
pub last_active_ip: Option<IpAddr>,
}
impl std::ops::Deref for Session {

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::ops::Deref;
use std::{net::IpAddr, ops::Deref};
use chrono::{DateTime, Duration, Utc};
use rand::{Rng, SeedableRng};
@ -81,6 +81,8 @@ pub struct BrowserSession {
pub created_at: DateTime<Utc>,
pub finished_at: Option<DateTime<Utc>>,
pub user_agent: Option<String>,
pub last_active_at: Option<DateTime<Utc>>,
pub last_active_ip: Option<IpAddr>,
}
impl BrowserSession {
@ -101,6 +103,8 @@ impl BrowserSession {
created_at: now,
finished_at: None,
user_agent: Some("Mozilla/5.0".to_owned()),
last_active_at: Some(now),
last_active_ip: None,
})
.collect()
}

View File

@ -92,6 +92,16 @@ impl BrowserSession {
pub async fn user_agent(&self) -> Option<&str> {
self.0.user_agent.as_deref()
}
/// The last IP address used by the session.
pub async fn last_active_ip(&self) -> Option<String> {
self.0.last_active_ip.map(|ip| ip.to_string())
}
/// The last time the session was active.
pub async fn last_active_at(&self) -> Option<DateTime<Utc>> {
self.0.last_active_at
}
}
/// An authentication records when a user enter their credential in a browser

View File

@ -142,6 +142,16 @@ impl CompatSession {
mas_data_model::CompatSessionState::Finished { .. } => CompatSessionState::Finished,
}
}
/// The last IP address used by the session.
pub async fn last_active_ip(&self) -> Option<String> {
self.session.last_active_ip.map(|ip| ip.to_string())
}
/// The last time the session was active.
pub async fn last_active_at(&self) -> Option<DateTime<Utc>> {
self.session.last_active_at
}
}
/// A compat SSO login represents a login done through the legacy Matrix login

View File

@ -128,6 +128,16 @@ impl OAuth2Session {
Ok(Some(User(user)))
}
/// The last IP address used by the session.
pub async fn last_active_ip(&self) -> Option<String> {
self.0.last_active_ip.map(|ip| ip.to_string())
}
/// The last time the session was active.
pub async fn last_active_at(&self) -> Option<DateTime<Utc>> {
self.0.last_active_at
}
}
/// The application type advertised by the client.

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT compat_session_id\n , device_id\n , user_id\n , created_at\n , finished_at\n , is_synapse_admin\n FROM compat_sessions\n WHERE compat_session_id = $1\n ",
"query": "\n SELECT compat_session_id\n , device_id\n , user_id\n , created_at\n , finished_at\n , is_synapse_admin\n , last_active_at\n , last_active_ip as \"last_active_ip: IpAddr\"\n FROM compat_sessions\n WHERE compat_session_id = $1\n ",
"describe": {
"columns": [
{
@ -32,6 +32,16 @@
"ordinal": 5,
"name": "is_synapse_admin",
"type_info": "Bool"
},
{
"ordinal": 6,
"name": "last_active_at",
"type_info": "Timestamptz"
},
{
"ordinal": 7,
"name": "last_active_ip: IpAddr",
"type_info": "Inet"
}
],
"parameters": {
@ -45,8 +55,10 @@
false,
false,
true,
false
false,
true,
true
]
},
"hash": "0469c1d3ad11fd96febacad33302709c870ead848d6920cdfdb18912d543488e"
"hash": "23c03635d6433099a4353ba0c80b88737724edb16315832891550e29088d02bf"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT s.user_session_id\n , s.created_at AS \"user_session_created_at\"\n , s.finished_at AS \"user_session_finished_at\"\n , s.user_agent AS \"user_session_user_agent\"\n , u.user_id\n , u.username AS \"user_username\"\n , u.primary_user_email_id AS \"user_primary_user_email_id\"\n , u.created_at AS \"user_created_at\"\n , u.locked_at AS \"user_locked_at\"\n FROM user_sessions s\n INNER JOIN users u\n USING (user_id)\n WHERE s.user_session_id = $1\n ",
"query": "\n SELECT s.user_session_id\n , s.created_at AS \"user_session_created_at\"\n , s.finished_at AS \"user_session_finished_at\"\n , s.user_agent AS \"user_session_user_agent\"\n , s.last_active_at AS \"user_session_last_active_at\"\n , s.last_active_ip AS \"user_session_last_active_ip: IpAddr\"\n , u.user_id\n , u.username AS \"user_username\"\n , u.primary_user_email_id AS \"user_primary_user_email_id\"\n , u.created_at AS \"user_created_at\"\n , u.locked_at AS \"user_locked_at\"\n FROM user_sessions s\n INNER JOIN users u\n USING (user_id)\n WHERE s.user_session_id = $1\n ",
"describe": {
"columns": [
{
@ -25,26 +25,36 @@
},
{
"ordinal": 4,
"name": "user_session_last_active_at",
"type_info": "Timestamptz"
},
{
"ordinal": 5,
"name": "user_session_last_active_ip: IpAddr",
"type_info": "Inet"
},
{
"ordinal": 6,
"name": "user_id",
"type_info": "Uuid"
},
{
"ordinal": 5,
"ordinal": 7,
"name": "user_username",
"type_info": "Text"
},
{
"ordinal": 6,
"ordinal": 8,
"name": "user_primary_user_email_id",
"type_info": "Uuid"
},
{
"ordinal": 7,
"ordinal": 9,
"name": "user_created_at",
"type_info": "Timestamptz"
},
{
"ordinal": 8,
"ordinal": 10,
"name": "user_locked_at",
"type_info": "Timestamptz"
}
@ -59,6 +69,8 @@
false,
true,
true,
true,
true,
false,
false,
true,
@ -66,5 +78,5 @@
true
]
},
"hash": "b98052065469a71643bb332cbeb07e0e43c620ffa1a592eb45ab326e0064efa8"
"hash": "2b0d54c284dc4d946faae4190568bf597c04b40f010132dd7bf68462c47f9eac"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT oauth2_session_id\n , user_id\n , user_session_id\n , oauth2_client_id\n , scope_list\n , created_at\n , finished_at\n FROM oauth2_sessions\n\n WHERE oauth2_session_id = $1\n ",
"query": "\n SELECT oauth2_session_id\n , user_id\n , user_session_id\n , oauth2_client_id\n , scope_list\n , created_at\n , finished_at\n , last_active_at\n , last_active_ip as \"last_active_ip: IpAddr\"\n FROM oauth2_sessions\n\n WHERE oauth2_session_id = $1\n ",
"describe": {
"columns": [
{
@ -37,6 +37,16 @@
"ordinal": 6,
"name": "finished_at",
"type_info": "Timestamptz"
},
{
"ordinal": 7,
"name": "last_active_at",
"type_info": "Timestamptz"
},
{
"ordinal": 8,
"name": "last_active_ip: IpAddr",
"type_info": "Inet"
}
],
"parameters": {
@ -51,8 +61,10 @@
false,
false,
false,
true,
true,
true
]
},
"hash": "31b7910705963c1a0fa1d55614299aea1a2fa782d40ec53e93c03fe0d73ab0f4"
"hash": "31aace373b20b5dbf65fa51d8663da7571d85b6a7d2d544d69e7d04260cdffc9"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT compat_session_id\n , device_id\n , user_id\n , created_at\n , finished_at\n , is_synapse_admin\n FROM compat_sessions\n WHERE user_id = $1\n AND device_id = $2\n ",
"query": "\n SELECT compat_session_id\n , device_id\n , user_id\n , created_at\n , finished_at\n , is_synapse_admin\n , last_active_at\n , last_active_ip as \"last_active_ip: IpAddr\"\n FROM compat_sessions\n WHERE user_id = $1\n AND device_id = $2\n ",
"describe": {
"columns": [
{
@ -32,6 +32,16 @@
"ordinal": 5,
"name": "is_synapse_admin",
"type_info": "Bool"
},
{
"ordinal": 6,
"name": "last_active_at",
"type_info": "Timestamptz"
},
{
"ordinal": 7,
"name": "last_active_ip: IpAddr",
"type_info": "Inet"
}
],
"parameters": {
@ -46,8 +56,10 @@
false,
false,
true,
false
false,
true,
true
]
},
"hash": "89e8bb889b8e604d1b4669e904d3233e70b44277e7fe2ce4317c9d821512d86b"
"hash": "662ff8972c0cbccb9ba45b1d724c7b6e87656beabe702603cfd4b5a92263b5ab"
}

View File

@ -58,6 +58,8 @@ struct CompatSessionLookup {
created_at: DateTime<Utc>,
finished_at: Option<DateTime<Utc>>,
is_synapse_admin: bool,
last_active_at: Option<DateTime<Utc>>,
last_active_ip: Option<IpAddr>,
}
impl TryFrom<CompatSessionLookup> for CompatSession {
@ -84,6 +86,8 @@ impl TryFrom<CompatSessionLookup> for CompatSession {
device,
created_at: value.created_at,
is_synapse_admin: value.is_synapse_admin,
last_active_at: value.last_active_at,
last_active_ip: value.last_active_ip,
};
Ok(session)
@ -99,6 +103,8 @@ struct CompatSessionAndSsoLoginLookup {
created_at: DateTime<Utc>,
finished_at: Option<DateTime<Utc>>,
is_synapse_admin: bool,
last_active_at: Option<DateTime<Utc>>,
last_active_ip: Option<IpAddr>,
compat_sso_login_id: Option<Uuid>,
compat_sso_login_token: Option<String>,
compat_sso_login_redirect_uri: Option<String>,
@ -131,6 +137,8 @@ impl TryFrom<CompatSessionAndSsoLoginLookup> for (CompatSession, Option<CompatSs
device,
created_at: value.created_at,
is_synapse_admin: value.is_synapse_admin,
last_active_at: value.last_active_at,
last_active_ip: value.last_active_ip,
};
match (
@ -209,6 +217,8 @@ impl<'c> CompatSessionRepository for PgCompatSessionRepository<'c> {
, created_at
, finished_at
, is_synapse_admin
, last_active_at
, last_active_ip as "last_active_ip: IpAddr"
FROM compat_sessions
WHERE compat_session_id = $1
"#,
@ -247,6 +257,8 @@ impl<'c> CompatSessionRepository for PgCompatSessionRepository<'c> {
, created_at
, finished_at
, is_synapse_admin
, last_active_at
, last_active_ip as "last_active_ip: IpAddr"
FROM compat_sessions
WHERE user_id = $1
AND device_id = $2
@ -309,6 +321,8 @@ impl<'c> CompatSessionRepository for PgCompatSessionRepository<'c> {
device,
created_at,
is_synapse_admin,
last_active_at: None,
last_active_ip: None,
})
}
@ -390,6 +404,14 @@ impl<'c> CompatSessionRepository for PgCompatSessionRepository<'c> {
Expr::col((CompatSessions::Table, CompatSessions::IsSynapseAdmin)),
CompatSessionAndSsoLoginLookupIden::IsSynapseAdmin,
)
.expr_as(
Expr::col((CompatSessions::Table, CompatSessions::LastActiveAt)),
CompatSessionAndSsoLoginLookupIden::LastActiveAt,
)
.expr_as(
Expr::col((CompatSessions::Table, CompatSessions::LastActiveIp)),
CompatSessionAndSsoLoginLookupIden::LastActiveIp,
)
.expr_as(
Expr::col((CompatSsoLogins::Table, CompatSsoLogins::CompatSsoLoginId)),
CompatSessionAndSsoLoginLookupIden::CompatSsoLoginId,

View File

@ -22,6 +22,8 @@ pub enum UserSessions {
CreatedAt,
FinishedAt,
UserAgent,
LastActiveAt,
LastActiveIp,
}
#[derive(sea_query::Iden)]
@ -53,6 +55,8 @@ pub enum CompatSessions {
CreatedAt,
FinishedAt,
IsSynapseAdmin,
LastActiveAt,
LastActiveIp,
}
#[derive(sea_query::Iden)]
@ -80,6 +84,8 @@ pub enum OAuth2Sessions {
ScopeList,
CreatedAt,
FinishedAt,
LastActiveAt,
LastActiveIp,
}
#[derive(sea_query::Iden)]

View File

@ -59,6 +59,8 @@ struct OAuthSessionLookup {
scope_list: Vec<String>,
created_at: DateTime<Utc>,
finished_at: Option<DateTime<Utc>>,
last_active_at: Option<DateTime<Utc>>,
last_active_ip: Option<IpAddr>,
}
impl TryFrom<OAuthSessionLookup> for Session {
@ -91,6 +93,8 @@ impl TryFrom<OAuthSessionLookup> for Session {
user_id: value.user_id.map(Ulid::from),
user_session_id: value.user_session_id.map(Ulid::from),
scope,
last_active_at: value.last_active_at,
last_active_ip: value.last_active_ip,
})
}
}
@ -119,6 +123,8 @@ impl<'c> OAuth2SessionRepository for PgOAuth2SessionRepository<'c> {
, scope_list
, created_at
, finished_at
, last_active_at
, last_active_ip as "last_active_ip: IpAddr"
FROM oauth2_sessions
WHERE oauth2_session_id = $1
@ -191,6 +197,8 @@ impl<'c> OAuth2SessionRepository for PgOAuth2SessionRepository<'c> {
user_session_id: user_session.map(|s| s.id),
client_id: client.id,
scope,
last_active_at: None,
last_active_ip: None,
})
}
@ -273,6 +281,14 @@ impl<'c> OAuth2SessionRepository for PgOAuth2SessionRepository<'c> {
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::FinishedAt)),
OAuthSessionLookupIden::FinishedAt,
)
.expr_as(
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::LastActiveAt)),
OAuthSessionLookupIden::LastActiveAt,
)
.expr_as(
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::LastActiveIp)),
OAuthSessionLookupIden::LastActiveIp,
)
.from(OAuth2Sessions::Table)
.and_where_option(filter.user().map(|user| {
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::UserId)).eq(Uuid::from(user.id))

View File

@ -56,6 +56,8 @@ struct SessionLookup {
user_session_created_at: DateTime<Utc>,
user_session_finished_at: Option<DateTime<Utc>>,
user_session_user_agent: Option<String>,
user_session_last_active_at: Option<DateTime<Utc>>,
user_session_last_active_ip: Option<IpAddr>,
user_id: Uuid,
user_username: String,
user_primary_user_email_id: Option<Uuid>,
@ -83,6 +85,8 @@ impl TryFrom<SessionLookup> for BrowserSession {
created_at: value.user_session_created_at,
finished_at: value.user_session_finished_at,
user_agent: value.user_session_user_agent,
last_active_at: value.user_session_last_active_at,
last_active_ip: value.user_session_last_active_ip,
})
}
}
@ -144,6 +148,8 @@ impl<'c> BrowserSessionRepository for PgBrowserSessionRepository<'c> {
, s.created_at AS "user_session_created_at"
, s.finished_at AS "user_session_finished_at"
, s.user_agent AS "user_session_user_agent"
, s.last_active_at AS "user_session_last_active_at"
, s.last_active_ip AS "user_session_last_active_ip: IpAddr"
, u.user_id
, u.username AS "user_username"
, u.primary_user_email_id AS "user_primary_user_email_id"
@ -207,6 +213,8 @@ impl<'c> BrowserSessionRepository for PgBrowserSessionRepository<'c> {
created_at,
finished_at: None,
user_agent,
last_active_at: None,
last_active_ip: None,
};
Ok(session)
@ -277,6 +285,14 @@ impl<'c> BrowserSessionRepository for PgBrowserSessionRepository<'c> {
Expr::col((UserSessions::Table, UserSessions::UserAgent)),
SessionLookupIden::UserSessionUserAgent,
)
.expr_as(
Expr::col((UserSessions::Table, UserSessions::LastActiveAt)),
SessionLookupIden::UserSessionLastActiveAt,
)
.expr_as(
Expr::col((UserSessions::Table, UserSessions::LastActiveIp)),
SessionLookupIden::UserSessionLastActiveIp,
)
.expr_as(
Expr::col((Users::Table, Users::UserId)),
SessionLookupIden::UserId,

View File

@ -157,6 +157,14 @@ type BrowserSession implements Node & CreationEvent {
The user-agent string with which the session was created.
"""
userAgent: String
"""
The last IP address used by the session.
"""
lastActiveIp: String
"""
The last time the session was active.
"""
lastActiveAt: DateTime
}
type BrowserSessionConnection {
@ -239,6 +247,14 @@ type CompatSession implements Node & CreationEvent {
The state of the session.
"""
state: CompatSessionState!
"""
The last IP address used by the session.
"""
lastActiveIp: String
"""
The last time the session was active.
"""
lastActiveAt: DateTime
}
type CompatSessionConnection {
@ -740,6 +756,14 @@ type Oauth2Session implements Node & CreationEvent {
User authorized for this session.
"""
user: User
"""
The last IP address used by the session.
"""
lastActiveIp: String
"""
The last time the session was active.
"""
lastActiveAt: DateTime
}
type Oauth2SessionConnection {

View File

@ -127,6 +127,10 @@ export type BrowserSession = CreationEvent &
finishedAt?: Maybe<Scalars["DateTime"]["output"]>;
/** ID of the object. */
id: Scalars["ID"]["output"];
/** The last time the session was active. */
lastActiveAt?: Maybe<Scalars["DateTime"]["output"]>;
/** The last IP address used by the session. */
lastActiveIp?: Maybe<Scalars["String"]["output"]>;
/** The most recent authentication of this session. */
lastAuthentication?: Maybe<Authentication>;
/** The state of the session. */
@ -181,6 +185,10 @@ export type CompatSession = CreationEvent &
finishedAt?: Maybe<Scalars["DateTime"]["output"]>;
/** ID of the object. */
id: Scalars["ID"]["output"];
/** The last time the session was active. */
lastActiveAt?: Maybe<Scalars["DateTime"]["output"]>;
/** The last IP address used by the session. */
lastActiveIp?: Maybe<Scalars["String"]["output"]>;
/** The associated SSO login, if any. */
ssoLogin?: Maybe<CompatSsoLogin>;
/** The state of the session. */
@ -544,6 +552,10 @@ export type Oauth2Session = CreationEvent &
finishedAt?: Maybe<Scalars["DateTime"]["output"]>;
/** ID of the object. */
id: Scalars["ID"]["output"];
/** The last time the session was active. */
lastActiveAt?: Maybe<Scalars["DateTime"]["output"]>;
/** The last IP address used by the session. */
lastActiveIp?: Maybe<Scalars["String"]["output"]>;
/** Scope granted for this session. */
scope: Scalars["String"]["output"];
/** The state of the session. */

File diff suppressed because it is too large Load Diff