You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-08-06 06:02:40 +03:00
Save which user session created a compat session
This also exposes the user session in the GraphQL API, and allow filtering on browser session ID on the app session list.
This commit is contained in:
@@ -184,7 +184,7 @@ impl Options {
|
|||||||
|
|
||||||
let compat_session = repo
|
let compat_session = repo
|
||||||
.compat_session()
|
.compat_session()
|
||||||
.add(&mut rng, &clock, &user, device, admin)
|
.add(&mut rng, &clock, &user, device, None, admin)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let token = TokenType::CompatAccessToken.generate(&mut rng);
|
let token = TokenType::CompatAccessToken.generate(&mut rng);
|
||||||
|
@@ -80,6 +80,7 @@ pub struct CompatSession {
|
|||||||
pub state: CompatSessionState,
|
pub state: CompatSessionState,
|
||||||
pub user_id: Ulid,
|
pub user_id: Ulid,
|
||||||
pub device: Device,
|
pub device: Device,
|
||||||
|
pub user_session_id: Option<Ulid>,
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
pub is_synapse_admin: bool,
|
pub is_synapse_admin: bool,
|
||||||
pub last_active_at: Option<DateTime<Utc>>,
|
pub last_active_at: Option<DateTime<Utc>>,
|
||||||
|
@@ -12,11 +12,20 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use async_graphql::{Context, Description, Object, ID};
|
use async_graphql::{
|
||||||
|
connection::{query, Connection, Edge, OpaqueCursor},
|
||||||
|
Context, Description, Object, ID,
|
||||||
|
};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use mas_storage::{user::BrowserSessionRepository, RepositoryAccess};
|
use mas_data_model::Device;
|
||||||
|
use mas_storage::{
|
||||||
|
app_session::AppSessionFilter, user::BrowserSessionRepository, Pagination, RepositoryAccess,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{NodeType, SessionState, User};
|
use super::{
|
||||||
|
AppSession, CompatSession, Cursor, NodeCursor, NodeType, OAuth2Session, PreloadedTotalCount,
|
||||||
|
SessionState, User,
|
||||||
|
};
|
||||||
use crate::state::ContextExt;
|
use crate::state::ContextExt;
|
||||||
|
|
||||||
/// A browser session represents a logged in user in a browser.
|
/// A browser session represents a logged in user in a browser.
|
||||||
@@ -92,6 +101,97 @@ impl BrowserSession {
|
|||||||
pub async fn last_active_at(&self) -> Option<DateTime<Utc>> {
|
pub async fn last_active_at(&self) -> Option<DateTime<Utc>> {
|
||||||
self.0.last_active_at
|
self.0.last_active_at
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the list of both compat and OAuth 2.0 sessions started by this
|
||||||
|
/// browser session, chronologically sorted
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
async fn app_sessions(
|
||||||
|
&self,
|
||||||
|
ctx: &Context<'_>,
|
||||||
|
|
||||||
|
#[graphql(name = "state", desc = "List only sessions in the given state.")]
|
||||||
|
state_param: Option<SessionState>,
|
||||||
|
|
||||||
|
#[graphql(name = "device", desc = "List only sessions for the given device.")]
|
||||||
|
device_param: Option<String>,
|
||||||
|
|
||||||
|
#[graphql(desc = "Returns the elements in the list that come after the cursor.")]
|
||||||
|
after: Option<String>,
|
||||||
|
#[graphql(desc = "Returns the elements in the list that come before the cursor.")]
|
||||||
|
before: Option<String>,
|
||||||
|
#[graphql(desc = "Returns the first *n* elements from the list.")] first: Option<i32>,
|
||||||
|
#[graphql(desc = "Returns the last *n* elements from the list.")] last: Option<i32>,
|
||||||
|
) -> Result<Connection<Cursor, AppSession, PreloadedTotalCount>, async_graphql::Error> {
|
||||||
|
let state = ctx.state();
|
||||||
|
let mut repo = state.repository().await?;
|
||||||
|
|
||||||
|
query(
|
||||||
|
after,
|
||||||
|
before,
|
||||||
|
first,
|
||||||
|
last,
|
||||||
|
|after, before, first, last| async move {
|
||||||
|
let after_id = after
|
||||||
|
.map(|x: OpaqueCursor<NodeCursor>| {
|
||||||
|
x.extract_for_types(&[NodeType::OAuth2Session, NodeType::CompatSession])
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
let before_id = before
|
||||||
|
.map(|x: OpaqueCursor<NodeCursor>| {
|
||||||
|
x.extract_for_types(&[NodeType::OAuth2Session, NodeType::CompatSession])
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
let pagination = Pagination::try_new(before_id, after_id, first, last)?;
|
||||||
|
|
||||||
|
let device_param = device_param.map(Device::try_from).transpose()?;
|
||||||
|
|
||||||
|
let filter = AppSessionFilter::new().for_browser_session(&self.0);
|
||||||
|
|
||||||
|
let filter = match state_param {
|
||||||
|
Some(SessionState::Active) => filter.active_only(),
|
||||||
|
Some(SessionState::Finished) => filter.finished_only(),
|
||||||
|
None => filter,
|
||||||
|
};
|
||||||
|
|
||||||
|
let filter = match device_param.as_ref() {
|
||||||
|
Some(device) => filter.for_device(device),
|
||||||
|
None => filter,
|
||||||
|
};
|
||||||
|
|
||||||
|
let page = repo.app_session().list(filter, pagination).await?;
|
||||||
|
|
||||||
|
let count = if ctx.look_ahead().field("totalCount").exists() {
|
||||||
|
Some(repo.app_session().count(filter).await?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
repo.cancel().await?;
|
||||||
|
|
||||||
|
let mut connection = Connection::with_additional_fields(
|
||||||
|
page.has_previous_page,
|
||||||
|
page.has_next_page,
|
||||||
|
PreloadedTotalCount(count),
|
||||||
|
);
|
||||||
|
|
||||||
|
connection
|
||||||
|
.edges
|
||||||
|
.extend(page.edges.into_iter().map(|s| match s {
|
||||||
|
mas_storage::app_session::AppSession::Compat(session) => Edge::new(
|
||||||
|
OpaqueCursor(NodeCursor(NodeType::CompatSession, session.id)),
|
||||||
|
AppSession::CompatSession(Box::new(CompatSession::new(*session))),
|
||||||
|
),
|
||||||
|
mas_storage::app_session::AppSession::OAuth2(session) => Edge::new(
|
||||||
|
OpaqueCursor(NodeCursor(NodeType::OAuth2Session, session.id)),
|
||||||
|
AppSession::OAuth2Session(Box::new(OAuth2Session(*session))),
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
|
Ok::<_, async_graphql::Error>(connection)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An authentication records when a user enter their credential in a browser
|
/// An authentication records when a user enter their credential in a browser
|
||||||
|
@@ -18,7 +18,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use mas_storage::{compat::CompatSessionRepository, user::UserRepository};
|
use mas_storage::{compat::CompatSessionRepository, user::UserRepository};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use super::{NodeType, SessionState, User};
|
use super::{BrowserSession, NodeType, SessionState, User};
|
||||||
use crate::state::ContextExt;
|
use crate::state::ContextExt;
|
||||||
|
|
||||||
/// Lazy-loaded reverse reference.
|
/// Lazy-loaded reverse reference.
|
||||||
@@ -125,6 +125,27 @@ impl CompatSession {
|
|||||||
Ok(sso_login.map(CompatSsoLogin))
|
Ok(sso_login.map(CompatSsoLogin))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The browser session which started this session, if any.
|
||||||
|
pub async fn browser_session(
|
||||||
|
&self,
|
||||||
|
ctx: &Context<'_>,
|
||||||
|
) -> Result<Option<BrowserSession>, async_graphql::Error> {
|
||||||
|
let Some(user_session_id) = self.session.user_session_id else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let state = ctx.state();
|
||||||
|
let mut repo = state.repository().await?;
|
||||||
|
let browser_session = repo
|
||||||
|
.browser_session()
|
||||||
|
.lookup(user_session_id)
|
||||||
|
.await?
|
||||||
|
.context("Could not load browser session")?;
|
||||||
|
repo.cancel().await?;
|
||||||
|
|
||||||
|
Ok(Some(BrowserSession(browser_session)))
|
||||||
|
}
|
||||||
|
|
||||||
/// The state of the session.
|
/// The state of the session.
|
||||||
pub async fn state(&self) -> SessionState {
|
pub async fn state(&self) -> SessionState {
|
||||||
match &self.session.state {
|
match &self.session.state {
|
||||||
|
@@ -32,7 +32,7 @@ pub use self::{
|
|||||||
node::{Node, NodeType},
|
node::{Node, NodeType},
|
||||||
oauth::{OAuth2Client, OAuth2Session},
|
oauth::{OAuth2Client, OAuth2Session},
|
||||||
upstream_oauth::{UpstreamOAuth2Link, UpstreamOAuth2Provider},
|
upstream_oauth::{UpstreamOAuth2Link, UpstreamOAuth2Provider},
|
||||||
users::{User, UserEmail},
|
users::{AppSession, User, UserEmail},
|
||||||
viewer::{Anonymous, Viewer, ViewerSession},
|
viewer::{Anonymous, Viewer, ViewerSession},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use anyhow::Context as _;
|
||||||
use async_graphql::{
|
use async_graphql::{
|
||||||
connection::{query, Connection, Edge, OpaqueCursor},
|
connection::{query, Connection, Edge, OpaqueCursor},
|
||||||
Context, Description, Enum, Object, Union, ID,
|
Context, Description, Enum, Object, Union, ID,
|
||||||
@@ -544,6 +545,12 @@ impl User {
|
|||||||
#[graphql(name = "device", desc = "List only sessions for the given device.")]
|
#[graphql(name = "device", desc = "List only sessions for the given device.")]
|
||||||
device_param: Option<String>,
|
device_param: Option<String>,
|
||||||
|
|
||||||
|
#[graphql(
|
||||||
|
name = "browserSession",
|
||||||
|
desc = "List only sessions for the given session."
|
||||||
|
)]
|
||||||
|
browser_session_param: Option<ID>,
|
||||||
|
|
||||||
#[graphql(desc = "Returns the elements in the list that come after the cursor.")]
|
#[graphql(desc = "Returns the elements in the list that come after the cursor.")]
|
||||||
after: Option<String>,
|
after: Option<String>,
|
||||||
#[graphql(desc = "Returns the elements in the list that come before the cursor.")]
|
#[graphql(desc = "Returns the elements in the list that come before the cursor.")]
|
||||||
@@ -552,6 +559,7 @@ impl User {
|
|||||||
#[graphql(desc = "Returns the last *n* elements from the list.")] last: Option<i32>,
|
#[graphql(desc = "Returns the last *n* elements from the list.")] last: Option<i32>,
|
||||||
) -> Result<Connection<Cursor, AppSession, PreloadedTotalCount>, async_graphql::Error> {
|
) -> Result<Connection<Cursor, AppSession, PreloadedTotalCount>, async_graphql::Error> {
|
||||||
let state = ctx.state();
|
let state = ctx.state();
|
||||||
|
let requester = ctx.requester();
|
||||||
let mut repo = state.repository().await?;
|
let mut repo = state.repository().await?;
|
||||||
|
|
||||||
query(
|
query(
|
||||||
@@ -587,6 +595,38 @@ impl User {
|
|||||||
None => filter,
|
None => filter,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let maybe_session = match browser_session_param {
|
||||||
|
Some(id) => {
|
||||||
|
// This might fail, but we're probably alright with it
|
||||||
|
let id = NodeType::BrowserSession
|
||||||
|
.extract_ulid(&id)
|
||||||
|
.context("Invalid browser_session parameter")?;
|
||||||
|
|
||||||
|
let Some(session) = repo
|
||||||
|
.browser_session()
|
||||||
|
.lookup(id)
|
||||||
|
.await?
|
||||||
|
.filter(|u| requester.is_owner_or_admin(u))
|
||||||
|
else {
|
||||||
|
// If we couldn't find the session or if the requester can't access it,
|
||||||
|
// return an empty list
|
||||||
|
return Ok(Connection::with_additional_fields(
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
PreloadedTotalCount(Some(0)),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(session)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let filter = match maybe_session {
|
||||||
|
Some(ref session) => filter.for_browser_session(session),
|
||||||
|
None => filter,
|
||||||
|
};
|
||||||
|
|
||||||
let page = repo.app_session().list(filter, pagination).await?;
|
let page = repo.app_session().list(filter, pagination).await?;
|
||||||
|
|
||||||
let count = if ctx.look_ahead().field("totalCount").exists() {
|
let count = if ctx.look_ahead().field("totalCount").exists() {
|
||||||
@@ -625,7 +665,7 @@ impl User {
|
|||||||
|
|
||||||
/// A session in an application, either a compatibility or an OAuth 2.0 one
|
/// A session in an application, either a compatibility or an OAuth 2.0 one
|
||||||
#[derive(Union)]
|
#[derive(Union)]
|
||||||
enum AppSession {
|
pub enum AppSession {
|
||||||
CompatSession(Box<CompatSession>),
|
CompatSession(Box<CompatSession>),
|
||||||
OAuth2Session(Box<OAuth2Session>),
|
OAuth2Session(Box<OAuth2Session>),
|
||||||
}
|
}
|
||||||
|
@@ -411,7 +411,7 @@ async fn user_password_login(
|
|||||||
|
|
||||||
let session = repo
|
let session = repo
|
||||||
.compat_session()
|
.compat_session()
|
||||||
.add(&mut rng, clock, &user, device, false)
|
.add(&mut rng, clock, &user, device, None, false)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok((session, user))
|
Ok((session, user))
|
||||||
@@ -738,7 +738,14 @@ mod tests {
|
|||||||
// Complete the flow by fulfilling it with a session
|
// Complete the flow by fulfilling it with a session
|
||||||
let compat_session = repo
|
let compat_session = repo
|
||||||
.compat_session()
|
.compat_session()
|
||||||
.add(&mut state.rng(), &state.clock, user, device.clone(), false)
|
.add(
|
||||||
|
&mut state.rng(),
|
||||||
|
&state.clock,
|
||||||
|
user,
|
||||||
|
device.clone(),
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@@ -208,7 +208,14 @@ pub async fn post(
|
|||||||
|
|
||||||
let compat_session = repo
|
let compat_session = repo
|
||||||
.compat_session()
|
.compat_session()
|
||||||
.add(&mut rng, &clock, &session.user, device, false)
|
.add(
|
||||||
|
&mut rng,
|
||||||
|
&clock,
|
||||||
|
&session.user,
|
||||||
|
device,
|
||||||
|
Some(&session),
|
||||||
|
false,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
repo.compat_sso_login()
|
repo.compat_sso_login()
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"db_name": "PostgreSQL",
|
"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 , last_active_at\n , last_active_ip as \"last_active_ip: IpAddr\"\n FROM compat_sessions\n WHERE compat_session_id = $1\n ",
|
"query": "\n SELECT compat_session_id\n , device_id\n , user_id\n , user_session_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": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
@@ -20,26 +20,31 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 3,
|
"ordinal": 3,
|
||||||
|
"name": "user_session_id",
|
||||||
|
"type_info": "Uuid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
"name": "created_at",
|
"name": "created_at",
|
||||||
"type_info": "Timestamptz"
|
"type_info": "Timestamptz"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 4,
|
"ordinal": 5,
|
||||||
"name": "finished_at",
|
"name": "finished_at",
|
||||||
"type_info": "Timestamptz"
|
"type_info": "Timestamptz"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 5,
|
"ordinal": 6,
|
||||||
"name": "is_synapse_admin",
|
"name": "is_synapse_admin",
|
||||||
"type_info": "Bool"
|
"type_info": "Bool"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 6,
|
"ordinal": 7,
|
||||||
"name": "last_active_at",
|
"name": "last_active_at",
|
||||||
"type_info": "Timestamptz"
|
"type_info": "Timestamptz"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 7,
|
"ordinal": 8,
|
||||||
"name": "last_active_ip: IpAddr",
|
"name": "last_active_ip: IpAddr",
|
||||||
"type_info": "Inet"
|
"type_info": "Inet"
|
||||||
}
|
}
|
||||||
@@ -53,6 +58,7 @@
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
true,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
@@ -60,5 +66,5 @@
|
|||||||
true
|
true
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hash": "23c03635d6433099a4353ba0c80b88737724edb16315832891550e29088d02bf"
|
"hash": "04e25c9267bf2eb143a6445345229081e7b386743a93b3833ef8ad9d09972f3b"
|
||||||
}
|
}
|
19
crates/storage-pg/.sqlx/query-4070549b235e059eaeccc4751b480ccb30ad5b62d933b4efb03491124a9361ad.json
generated
Normal file
19
crates/storage-pg/.sqlx/query-4070549b235e059eaeccc4751b480ccb30ad5b62d933b4efb03491124a9361ad.json
generated
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n INSERT INTO compat_sessions \n (compat_session_id, user_id, device_id,\n user_session_id, created_at, is_synapse_admin)\n VALUES ($1, $2, $3, $4, $5, $6)\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Uuid",
|
||||||
|
"Uuid",
|
||||||
|
"Text",
|
||||||
|
"Uuid",
|
||||||
|
"Timestamptz",
|
||||||
|
"Bool"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "4070549b235e059eaeccc4751b480ccb30ad5b62d933b4efb03491124a9361ad"
|
||||||
|
}
|
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"db_name": "PostgreSQL",
|
|
||||||
"query": "\n INSERT INTO compat_sessions (compat_session_id, user_id, device_id, created_at, is_synapse_admin)\n VALUES ($1, $2, $3, $4, $5)\n ",
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Uuid",
|
|
||||||
"Uuid",
|
|
||||||
"Text",
|
|
||||||
"Timestamptz",
|
|
||||||
"Bool"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nullable": []
|
|
||||||
},
|
|
||||||
"hash": "cff3ac0fff62ffdc5640fce08c2ffabc1d89202561b736c5d03b501dfcd8d886"
|
|
||||||
}
|
|
@@ -0,0 +1,19 @@
|
|||||||
|
-- Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
--
|
||||||
|
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
-- you may not use this file except in compliance with the License.
|
||||||
|
-- You may obtain a copy of the License at
|
||||||
|
--
|
||||||
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
--
|
||||||
|
-- Unless required by applicable law or agreed to in writing, software
|
||||||
|
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
-- See the License for the specific language governing permissions and
|
||||||
|
-- limitations under the License.
|
||||||
|
|
||||||
|
-- Adds an optional link between the compatibility sessions and the user sessions
|
||||||
|
ALTER TABLE compat_sessions
|
||||||
|
ADD COLUMN user_session_id UUID
|
||||||
|
REFERENCES user_sessions (user_session_id)
|
||||||
|
ON DELETE SET NULL;
|
@@ -102,11 +102,12 @@ impl TryFrom<AppSessionLookup> for AppSession {
|
|||||||
last_active_ip,
|
last_active_ip,
|
||||||
} = value;
|
} = value;
|
||||||
|
|
||||||
|
let user_session_id = user_session_id.map(Ulid::from);
|
||||||
|
|
||||||
match (
|
match (
|
||||||
compat_session_id,
|
compat_session_id,
|
||||||
oauth2_session_id,
|
oauth2_session_id,
|
||||||
oauth2_client_id,
|
oauth2_client_id,
|
||||||
user_session_id,
|
|
||||||
user_id,
|
user_id,
|
||||||
scope_list,
|
scope_list,
|
||||||
device_id,
|
device_id,
|
||||||
@@ -116,7 +117,6 @@ impl TryFrom<AppSessionLookup> for AppSession {
|
|||||||
Some(compat_session_id),
|
Some(compat_session_id),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
Some(user_id),
|
Some(user_id),
|
||||||
None,
|
None,
|
||||||
Some(device_id),
|
Some(device_id),
|
||||||
@@ -140,6 +140,7 @@ impl TryFrom<AppSessionLookup> for AppSession {
|
|||||||
state,
|
state,
|
||||||
user_id: user_id.into(),
|
user_id: user_id.into(),
|
||||||
device,
|
device,
|
||||||
|
user_session_id,
|
||||||
created_at,
|
created_at,
|
||||||
is_synapse_admin,
|
is_synapse_admin,
|
||||||
last_active_at,
|
last_active_at,
|
||||||
@@ -153,7 +154,6 @@ impl TryFrom<AppSessionLookup> for AppSession {
|
|||||||
None,
|
None,
|
||||||
Some(oauth2_session_id),
|
Some(oauth2_session_id),
|
||||||
Some(oauth2_client_id),
|
Some(oauth2_client_id),
|
||||||
user_session_id,
|
|
||||||
user_id,
|
user_id,
|
||||||
Some(scope_list),
|
Some(scope_list),
|
||||||
None,
|
None,
|
||||||
@@ -180,7 +180,7 @@ impl TryFrom<AppSessionLookup> for AppSession {
|
|||||||
created_at,
|
created_at,
|
||||||
client_id: oauth2_client_id.into(),
|
client_id: oauth2_client_id.into(),
|
||||||
user_id: user_id.map(Ulid::from),
|
user_id: user_id.map(Ulid::from),
|
||||||
user_session_id: user_session_id.map(Ulid::from),
|
user_session_id,
|
||||||
scope,
|
scope,
|
||||||
last_active_at,
|
last_active_at,
|
||||||
last_active_ip,
|
last_active_ip,
|
||||||
@@ -269,6 +269,10 @@ impl<'c> AppSessionRepository for PgAppSessionRepository<'c> {
|
|||||||
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::FinishedAt)).is_not_null()
|
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::FinishedAt)).is_not_null()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
.and_where_option(filter.browser_session().map(|browser_session| {
|
||||||
|
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::UserSessionId))
|
||||||
|
.eq(Uuid::from(browser_session.id))
|
||||||
|
}))
|
||||||
.and_where_option(filter.device().map(|device| {
|
.and_where_option(filter.device().map(|device| {
|
||||||
Expr::val(device.to_scope_token().to_string()).eq(PgFunc::any(Expr::col((
|
Expr::val(device.to_scope_token().to_string()).eq(PgFunc::any(Expr::col((
|
||||||
OAuth2Sessions::Table,
|
OAuth2Sessions::Table,
|
||||||
@@ -288,7 +292,10 @@ impl<'c> AppSessionRepository for PgAppSessionRepository<'c> {
|
|||||||
)
|
)
|
||||||
.expr_as(Expr::cust("NULL"), AppSessionLookupIden::Oauth2SessionId)
|
.expr_as(Expr::cust("NULL"), AppSessionLookupIden::Oauth2SessionId)
|
||||||
.expr_as(Expr::cust("NULL"), AppSessionLookupIden::Oauth2ClientId)
|
.expr_as(Expr::cust("NULL"), AppSessionLookupIden::Oauth2ClientId)
|
||||||
.expr_as(Expr::cust("NULL"), AppSessionLookupIden::UserSessionId)
|
.expr_as(
|
||||||
|
Expr::col((CompatSessions::Table, CompatSessions::UserSessionId)),
|
||||||
|
AppSessionLookupIden::UserSessionId,
|
||||||
|
)
|
||||||
.expr_as(
|
.expr_as(
|
||||||
Expr::col((CompatSessions::Table, CompatSessions::UserId)),
|
Expr::col((CompatSessions::Table, CompatSessions::UserId)),
|
||||||
AppSessionLookupIden::UserId,
|
AppSessionLookupIden::UserId,
|
||||||
@@ -329,6 +336,10 @@ impl<'c> AppSessionRepository for PgAppSessionRepository<'c> {
|
|||||||
Expr::col((CompatSessions::Table, CompatSessions::FinishedAt)).is_not_null()
|
Expr::col((CompatSessions::Table, CompatSessions::FinishedAt)).is_not_null()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
.and_where_option(filter.browser_session().map(|browser_session| {
|
||||||
|
Expr::col((CompatSessions::Table, CompatSessions::UserSessionId))
|
||||||
|
.eq(Uuid::from(browser_session.id))
|
||||||
|
}))
|
||||||
.and_where_option(filter.device().map(|device| {
|
.and_where_option(filter.device().map(|device| {
|
||||||
Expr::col((CompatSessions::Table, CompatSessions::DeviceId)).eq(device.to_string())
|
Expr::col((CompatSessions::Table, CompatSessions::DeviceId)).eq(device.to_string())
|
||||||
}))
|
}))
|
||||||
@@ -385,6 +396,10 @@ impl<'c> AppSessionRepository for PgAppSessionRepository<'c> {
|
|||||||
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::FinishedAt)).is_not_null()
|
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::FinishedAt)).is_not_null()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
.and_where_option(filter.browser_session().map(|browser_session| {
|
||||||
|
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::UserSessionId))
|
||||||
|
.eq(Uuid::from(browser_session.id))
|
||||||
|
}))
|
||||||
.and_where_option(filter.device().map(|device| {
|
.and_where_option(filter.device().map(|device| {
|
||||||
Expr::val(device.to_scope_token().to_string()).eq(PgFunc::any(Expr::col((
|
Expr::val(device.to_scope_token().to_string()).eq(PgFunc::any(Expr::col((
|
||||||
OAuth2Sessions::Table,
|
OAuth2Sessions::Table,
|
||||||
@@ -406,6 +421,10 @@ impl<'c> AppSessionRepository for PgAppSessionRepository<'c> {
|
|||||||
Expr::col((CompatSessions::Table, CompatSessions::FinishedAt)).is_not_null()
|
Expr::col((CompatSessions::Table, CompatSessions::FinishedAt)).is_not_null()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
.and_where_option(filter.browser_session().map(|browser_session| {
|
||||||
|
Expr::col((CompatSessions::Table, CompatSessions::UserSessionId))
|
||||||
|
.eq(Uuid::from(browser_session.id))
|
||||||
|
}))
|
||||||
.and_where_option(filter.device().map(|device| {
|
.and_where_option(filter.device().map(|device| {
|
||||||
Expr::col((CompatSessions::Table, CompatSessions::DeviceId)).eq(device.to_string())
|
Expr::col((CompatSessions::Table, CompatSessions::DeviceId)).eq(device.to_string())
|
||||||
}))
|
}))
|
||||||
@@ -493,7 +512,7 @@ mod tests {
|
|||||||
let device = Device::generate(&mut rng);
|
let device = Device::generate(&mut rng);
|
||||||
let compat_session = repo
|
let compat_session = repo
|
||||||
.compat_session()
|
.compat_session()
|
||||||
.add(&mut rng, &clock, &user, device.clone(), false)
|
.add(&mut rng, &clock, &user, device.clone(), None, false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@@ -87,7 +87,7 @@ mod tests {
|
|||||||
let device_str = device.as_str().to_owned();
|
let device_str = device.as_str().to_owned();
|
||||||
let session = repo
|
let session = repo
|
||||||
.compat_session()
|
.compat_session()
|
||||||
.add(&mut rng, &clock, &user, device.clone(), false)
|
.add(&mut rng, &clock, &user, device.clone(), None, false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(session.user_id, user.id);
|
assert_eq!(session.user_id, user.id);
|
||||||
@@ -203,7 +203,7 @@ mod tests {
|
|||||||
let device = Device::generate(&mut rng);
|
let device = Device::generate(&mut rng);
|
||||||
let sso_login_session = repo
|
let sso_login_session = repo
|
||||||
.compat_session()
|
.compat_session()
|
||||||
.add(&mut rng, &clock, &user, device, false)
|
.add(&mut rng, &clock, &user, device, None, false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -291,7 +291,7 @@ mod tests {
|
|||||||
let device = Device::generate(&mut rng);
|
let device = Device::generate(&mut rng);
|
||||||
let session = repo
|
let session = repo
|
||||||
.compat_session()
|
.compat_session()
|
||||||
.add(&mut rng, &clock, &user, device, false)
|
.add(&mut rng, &clock, &user, device, None, false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -411,7 +411,7 @@ mod tests {
|
|||||||
let device = Device::generate(&mut rng);
|
let device = Device::generate(&mut rng);
|
||||||
let session = repo
|
let session = repo
|
||||||
.compat_session()
|
.compat_session()
|
||||||
.add(&mut rng, &clock, &user, device, false)
|
.add(&mut rng, &clock, &user, device, None, false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -584,7 +584,7 @@ mod tests {
|
|||||||
let device = Device::generate(&mut rng);
|
let device = Device::generate(&mut rng);
|
||||||
let session = repo
|
let session = repo
|
||||||
.compat_session()
|
.compat_session()
|
||||||
.add(&mut rng, &clock, &user, device, false)
|
.add(&mut rng, &clock, &user, device, None, false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@@ -17,7 +17,8 @@ use std::net::IpAddr;
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use mas_data_model::{
|
use mas_data_model::{
|
||||||
CompatSession, CompatSessionState, CompatSsoLogin, CompatSsoLoginState, Device, User,
|
BrowserSession, CompatSession, CompatSessionState, CompatSsoLogin, CompatSsoLoginState, Device,
|
||||||
|
User,
|
||||||
};
|
};
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
compat::{CompatSessionFilter, CompatSessionRepository},
|
compat::{CompatSessionFilter, CompatSessionRepository},
|
||||||
@@ -55,6 +56,7 @@ struct CompatSessionLookup {
|
|||||||
compat_session_id: Uuid,
|
compat_session_id: Uuid,
|
||||||
device_id: String,
|
device_id: String,
|
||||||
user_id: Uuid,
|
user_id: Uuid,
|
||||||
|
user_session_id: Option<Uuid>,
|
||||||
created_at: DateTime<Utc>,
|
created_at: DateTime<Utc>,
|
||||||
finished_at: Option<DateTime<Utc>>,
|
finished_at: Option<DateTime<Utc>>,
|
||||||
is_synapse_admin: bool,
|
is_synapse_admin: bool,
|
||||||
@@ -83,6 +85,7 @@ impl TryFrom<CompatSessionLookup> for CompatSession {
|
|||||||
id,
|
id,
|
||||||
state,
|
state,
|
||||||
user_id: value.user_id.into(),
|
user_id: value.user_id.into(),
|
||||||
|
user_session_id: value.user_session_id.map(Ulid::from),
|
||||||
device,
|
device,
|
||||||
created_at: value.created_at,
|
created_at: value.created_at,
|
||||||
is_synapse_admin: value.is_synapse_admin,
|
is_synapse_admin: value.is_synapse_admin,
|
||||||
@@ -100,6 +103,7 @@ struct CompatSessionAndSsoLoginLookup {
|
|||||||
compat_session_id: Uuid,
|
compat_session_id: Uuid,
|
||||||
device_id: String,
|
device_id: String,
|
||||||
user_id: Uuid,
|
user_id: Uuid,
|
||||||
|
user_session_id: Option<Uuid>,
|
||||||
created_at: DateTime<Utc>,
|
created_at: DateTime<Utc>,
|
||||||
finished_at: Option<DateTime<Utc>>,
|
finished_at: Option<DateTime<Utc>>,
|
||||||
is_synapse_admin: bool,
|
is_synapse_admin: bool,
|
||||||
@@ -135,6 +139,7 @@ impl TryFrom<CompatSessionAndSsoLoginLookup> for (CompatSession, Option<CompatSs
|
|||||||
state,
|
state,
|
||||||
user_id: value.user_id.into(),
|
user_id: value.user_id.into(),
|
||||||
device,
|
device,
|
||||||
|
user_session_id: value.user_session_id.map(Ulid::from),
|
||||||
created_at: value.created_at,
|
created_at: value.created_at,
|
||||||
is_synapse_admin: value.is_synapse_admin,
|
is_synapse_admin: value.is_synapse_admin,
|
||||||
last_active_at: value.last_active_at,
|
last_active_at: value.last_active_at,
|
||||||
@@ -214,6 +219,7 @@ impl<'c> CompatSessionRepository for PgCompatSessionRepository<'c> {
|
|||||||
SELECT compat_session_id
|
SELECT compat_session_id
|
||||||
, device_id
|
, device_id
|
||||||
, user_id
|
, user_id
|
||||||
|
, user_session_id
|
||||||
, created_at
|
, created_at
|
||||||
, finished_at
|
, finished_at
|
||||||
, is_synapse_admin
|
, is_synapse_admin
|
||||||
@@ -251,6 +257,7 @@ impl<'c> CompatSessionRepository for PgCompatSessionRepository<'c> {
|
|||||||
clock: &dyn Clock,
|
clock: &dyn Clock,
|
||||||
user: &User,
|
user: &User,
|
||||||
device: Device,
|
device: Device,
|
||||||
|
browser_session: Option<&BrowserSession>,
|
||||||
is_synapse_admin: bool,
|
is_synapse_admin: bool,
|
||||||
) -> Result<CompatSession, Self::Error> {
|
) -> Result<CompatSession, Self::Error> {
|
||||||
let created_at = clock.now();
|
let created_at = clock.now();
|
||||||
@@ -259,12 +266,15 @@ impl<'c> CompatSessionRepository for PgCompatSessionRepository<'c> {
|
|||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO compat_sessions (compat_session_id, user_id, device_id, created_at, is_synapse_admin)
|
INSERT INTO compat_sessions
|
||||||
VALUES ($1, $2, $3, $4, $5)
|
(compat_session_id, user_id, device_id,
|
||||||
|
user_session_id, created_at, is_synapse_admin)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6)
|
||||||
"#,
|
"#,
|
||||||
Uuid::from(id),
|
Uuid::from(id),
|
||||||
Uuid::from(user.id),
|
Uuid::from(user.id),
|
||||||
device.as_str(),
|
device.as_str(),
|
||||||
|
browser_session.map(|s| Uuid::from(s.id)),
|
||||||
created_at,
|
created_at,
|
||||||
is_synapse_admin,
|
is_synapse_admin,
|
||||||
)
|
)
|
||||||
@@ -277,6 +287,7 @@ impl<'c> CompatSessionRepository for PgCompatSessionRepository<'c> {
|
|||||||
state: CompatSessionState::default(),
|
state: CompatSessionState::default(),
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
device,
|
device,
|
||||||
|
user_session_id: browser_session.map(|s| s.id),
|
||||||
created_at,
|
created_at,
|
||||||
is_synapse_admin,
|
is_synapse_admin,
|
||||||
last_active_at: None,
|
last_active_at: None,
|
||||||
@@ -350,6 +361,10 @@ impl<'c> CompatSessionRepository for PgCompatSessionRepository<'c> {
|
|||||||
Expr::col((CompatSessions::Table, CompatSessions::UserId)),
|
Expr::col((CompatSessions::Table, CompatSessions::UserId)),
|
||||||
CompatSessionAndSsoLoginLookupIden::UserId,
|
CompatSessionAndSsoLoginLookupIden::UserId,
|
||||||
)
|
)
|
||||||
|
.expr_as(
|
||||||
|
Expr::col((CompatSessions::Table, CompatSessions::UserSessionId)),
|
||||||
|
CompatSessionAndSsoLoginLookupIden::UserSessionId,
|
||||||
|
)
|
||||||
.expr_as(
|
.expr_as(
|
||||||
Expr::col((CompatSessions::Table, CompatSessions::CreatedAt)),
|
Expr::col((CompatSessions::Table, CompatSessions::CreatedAt)),
|
||||||
CompatSessionAndSsoLoginLookupIden::CreatedAt,
|
CompatSessionAndSsoLoginLookupIden::CreatedAt,
|
||||||
|
@@ -53,6 +53,7 @@ pub enum CompatSessions {
|
|||||||
CompatSessionId,
|
CompatSessionId,
|
||||||
UserId,
|
UserId,
|
||||||
DeviceId,
|
DeviceId,
|
||||||
|
UserSessionId,
|
||||||
CreatedAt,
|
CreatedAt,
|
||||||
FinishedAt,
|
FinishedAt,
|
||||||
IsSynapseAdmin,
|
IsSynapseAdmin,
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
//! Repositories to interact with all kinds of sessions
|
//! Repositories to interact with all kinds of sessions
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use mas_data_model::{CompatSession, Device, Session, User};
|
use mas_data_model::{BrowserSession, CompatSession, Device, Session, User};
|
||||||
|
|
||||||
use crate::{repository_impl, Page, Pagination};
|
use crate::{repository_impl, Page, Pagination};
|
||||||
|
|
||||||
@@ -56,6 +56,7 @@ pub enum AppSession {
|
|||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||||
pub struct AppSessionFilter<'a> {
|
pub struct AppSessionFilter<'a> {
|
||||||
user: Option<&'a User>,
|
user: Option<&'a User>,
|
||||||
|
browser_session: Option<&'a BrowserSession>,
|
||||||
state: Option<AppSessionState>,
|
state: Option<AppSessionState>,
|
||||||
device_id: Option<&'a Device>,
|
device_id: Option<&'a Device>,
|
||||||
}
|
}
|
||||||
@@ -67,7 +68,7 @@ impl<'a> AppSessionFilter<'a> {
|
|||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the user who owns the compatibility sessions
|
/// Set the user who owns the sessions
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn for_user(mut self, user: &'a User) -> Self {
|
pub fn for_user(mut self, user: &'a User) -> Self {
|
||||||
self.user = Some(user);
|
self.user = Some(user);
|
||||||
@@ -80,6 +81,19 @@ impl<'a> AppSessionFilter<'a> {
|
|||||||
self.user
|
self.user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the browser session filter
|
||||||
|
#[must_use]
|
||||||
|
pub fn for_browser_session(mut self, browser_session: &'a BrowserSession) -> Self {
|
||||||
|
self.browser_session = Some(browser_session);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the browser session filter
|
||||||
|
#[must_use]
|
||||||
|
pub fn browser_session(&self) -> Option<&BrowserSession> {
|
||||||
|
self.browser_session
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the device ID filter
|
/// Set the device ID filter
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn for_device(mut self, device_id: &'a Device) -> Self {
|
pub fn for_device(mut self, device_id: &'a Device) -> Self {
|
||||||
|
@@ -16,7 +16,7 @@ use std::net::IpAddr;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use mas_data_model::{CompatSession, CompatSsoLogin, Device, User};
|
use mas_data_model::{BrowserSession, CompatSession, CompatSsoLogin, Device, User};
|
||||||
use rand_core::RngCore;
|
use rand_core::RngCore;
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
|
|
||||||
@@ -175,6 +175,7 @@ pub trait CompatSessionRepository: Send + Sync {
|
|||||||
/// * `clock`: The clock used to generate timestamps
|
/// * `clock`: The clock used to generate timestamps
|
||||||
/// * `user`: The user to create the compat session for
|
/// * `user`: The user to create the compat session for
|
||||||
/// * `device`: The device ID of this session
|
/// * `device`: The device ID of this session
|
||||||
|
/// * `browser_session`: The browser session which created this session
|
||||||
/// * `is_synapse_admin`: Whether the session is a synapse admin session
|
/// * `is_synapse_admin`: Whether the session is a synapse admin session
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
@@ -186,6 +187,7 @@ pub trait CompatSessionRepository: Send + Sync {
|
|||||||
clock: &dyn Clock,
|
clock: &dyn Clock,
|
||||||
user: &User,
|
user: &User,
|
||||||
device: Device,
|
device: Device,
|
||||||
|
browser_session: Option<&BrowserSession>,
|
||||||
is_synapse_admin: bool,
|
is_synapse_admin: bool,
|
||||||
) -> Result<CompatSession, Self::Error>;
|
) -> Result<CompatSession, Self::Error>;
|
||||||
|
|
||||||
@@ -261,6 +263,7 @@ repository_impl!(CompatSessionRepository:
|
|||||||
clock: &dyn Clock,
|
clock: &dyn Clock,
|
||||||
user: &User,
|
user: &User,
|
||||||
device: Device,
|
device: Device,
|
||||||
|
browser_session: Option<&BrowserSession>,
|
||||||
is_synapse_admin: bool,
|
is_synapse_admin: bool,
|
||||||
) -> Result<CompatSession, Self::Error>;
|
) -> Result<CompatSession, Self::Error>;
|
||||||
|
|
||||||
|
@@ -223,6 +223,36 @@ type BrowserSession implements Node & CreationEvent {
|
|||||||
The last time the session was active.
|
The last time the session was active.
|
||||||
"""
|
"""
|
||||||
lastActiveAt: DateTime
|
lastActiveAt: DateTime
|
||||||
|
"""
|
||||||
|
Get the list of both compat and OAuth 2.0 sessions started by this
|
||||||
|
browser session, chronologically sorted
|
||||||
|
"""
|
||||||
|
appSessions(
|
||||||
|
"""
|
||||||
|
List only sessions in the given state.
|
||||||
|
"""
|
||||||
|
state: SessionState
|
||||||
|
"""
|
||||||
|
List only sessions for the given device.
|
||||||
|
"""
|
||||||
|
device: String
|
||||||
|
"""
|
||||||
|
Returns the elements in the list that come after the cursor.
|
||||||
|
"""
|
||||||
|
after: String
|
||||||
|
"""
|
||||||
|
Returns the elements in the list that come before the cursor.
|
||||||
|
"""
|
||||||
|
before: String
|
||||||
|
"""
|
||||||
|
Returns the first *n* elements from the list.
|
||||||
|
"""
|
||||||
|
first: Int
|
||||||
|
"""
|
||||||
|
Returns the last *n* elements from the list.
|
||||||
|
"""
|
||||||
|
last: Int
|
||||||
|
): AppSessionConnection!
|
||||||
}
|
}
|
||||||
|
|
||||||
type BrowserSessionConnection {
|
type BrowserSessionConnection {
|
||||||
@@ -288,6 +318,10 @@ type CompatSession implements Node & CreationEvent {
|
|||||||
"""
|
"""
|
||||||
ssoLogin: CompatSsoLogin
|
ssoLogin: CompatSsoLogin
|
||||||
"""
|
"""
|
||||||
|
The browser session which started this session, if any.
|
||||||
|
"""
|
||||||
|
browserSession: BrowserSession
|
||||||
|
"""
|
||||||
The state of the session.
|
The state of the session.
|
||||||
"""
|
"""
|
||||||
state: SessionState!
|
state: SessionState!
|
||||||
@@ -1473,6 +1507,10 @@ type User implements Node {
|
|||||||
"""
|
"""
|
||||||
device: String
|
device: String
|
||||||
"""
|
"""
|
||||||
|
List only sessions for the given session.
|
||||||
|
"""
|
||||||
|
browserSession: ID
|
||||||
|
"""
|
||||||
Returns the elements in the list that come after the cursor.
|
Returns the elements in the list that come after the cursor.
|
||||||
"""
|
"""
|
||||||
after: String
|
after: String
|
||||||
|
@@ -158,6 +158,11 @@ export type Authentication = CreationEvent &
|
|||||||
export type BrowserSession = CreationEvent &
|
export type BrowserSession = CreationEvent &
|
||||||
Node & {
|
Node & {
|
||||||
__typename?: "BrowserSession";
|
__typename?: "BrowserSession";
|
||||||
|
/**
|
||||||
|
* Get the list of both compat and OAuth 2.0 sessions started by this
|
||||||
|
* browser session, chronologically sorted
|
||||||
|
*/
|
||||||
|
appSessions: AppSessionConnection;
|
||||||
/** When the object was created. */
|
/** When the object was created. */
|
||||||
createdAt: Scalars["DateTime"]["output"];
|
createdAt: Scalars["DateTime"]["output"];
|
||||||
/** When the session was finished. */
|
/** When the session was finished. */
|
||||||
@@ -178,6 +183,16 @@ export type BrowserSession = CreationEvent &
|
|||||||
userAgent?: Maybe<Scalars["String"]["output"]>;
|
userAgent?: Maybe<Scalars["String"]["output"]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** A browser session represents a logged in user in a browser. */
|
||||||
|
export type BrowserSessionAppSessionsArgs = {
|
||||||
|
after?: InputMaybe<Scalars["String"]["input"]>;
|
||||||
|
before?: InputMaybe<Scalars["String"]["input"]>;
|
||||||
|
device?: InputMaybe<Scalars["String"]["input"]>;
|
||||||
|
first?: InputMaybe<Scalars["Int"]["input"]>;
|
||||||
|
last?: InputMaybe<Scalars["Int"]["input"]>;
|
||||||
|
state?: InputMaybe<SessionState>;
|
||||||
|
};
|
||||||
|
|
||||||
export type BrowserSessionConnection = {
|
export type BrowserSessionConnection = {
|
||||||
__typename?: "BrowserSessionConnection";
|
__typename?: "BrowserSessionConnection";
|
||||||
/** A list of edges. */
|
/** A list of edges. */
|
||||||
@@ -206,6 +221,8 @@ export type BrowserSessionEdge = {
|
|||||||
export type CompatSession = CreationEvent &
|
export type CompatSession = CreationEvent &
|
||||||
Node & {
|
Node & {
|
||||||
__typename?: "CompatSession";
|
__typename?: "CompatSession";
|
||||||
|
/** The browser session which started this session, if any. */
|
||||||
|
browserSession?: Maybe<BrowserSession>;
|
||||||
/** When the object was created. */
|
/** When the object was created. */
|
||||||
createdAt: Scalars["DateTime"]["output"];
|
createdAt: Scalars["DateTime"]["output"];
|
||||||
/** The Matrix Device ID of this session. */
|
/** The Matrix Device ID of this session. */
|
||||||
@@ -980,6 +997,7 @@ export type User = Node & {
|
|||||||
export type UserAppSessionsArgs = {
|
export type UserAppSessionsArgs = {
|
||||||
after?: InputMaybe<Scalars["String"]["input"]>;
|
after?: InputMaybe<Scalars["String"]["input"]>;
|
||||||
before?: InputMaybe<Scalars["String"]["input"]>;
|
before?: InputMaybe<Scalars["String"]["input"]>;
|
||||||
|
browserSession?: InputMaybe<Scalars["ID"]["input"]>;
|
||||||
device?: InputMaybe<Scalars["String"]["input"]>;
|
device?: InputMaybe<Scalars["String"]["input"]>;
|
||||||
first?: InputMaybe<Scalars["Int"]["input"]>;
|
first?: InputMaybe<Scalars["Int"]["input"]>;
|
||||||
last?: InputMaybe<Scalars["Int"]["input"]>;
|
last?: InputMaybe<Scalars["Int"]["input"]>;
|
||||||
|
@@ -277,6 +277,61 @@ export default {
|
|||||||
kind: "OBJECT",
|
kind: "OBJECT",
|
||||||
name: "BrowserSession",
|
name: "BrowserSession",
|
||||||
fields: [
|
fields: [
|
||||||
|
{
|
||||||
|
name: "appSessions",
|
||||||
|
type: {
|
||||||
|
kind: "NON_NULL",
|
||||||
|
ofType: {
|
||||||
|
kind: "OBJECT",
|
||||||
|
name: "AppSessionConnection",
|
||||||
|
ofType: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "after",
|
||||||
|
type: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "before",
|
||||||
|
type: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "device",
|
||||||
|
type: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "first",
|
||||||
|
type: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "last",
|
||||||
|
type: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "state",
|
||||||
|
type: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "createdAt",
|
name: "createdAt",
|
||||||
type: {
|
type: {
|
||||||
@@ -475,6 +530,15 @@ export default {
|
|||||||
kind: "OBJECT",
|
kind: "OBJECT",
|
||||||
name: "CompatSession",
|
name: "CompatSession",
|
||||||
fields: [
|
fields: [
|
||||||
|
{
|
||||||
|
name: "browserSession",
|
||||||
|
type: {
|
||||||
|
kind: "OBJECT",
|
||||||
|
name: "BrowserSession",
|
||||||
|
ofType: null,
|
||||||
|
},
|
||||||
|
args: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "createdAt",
|
name: "createdAt",
|
||||||
type: {
|
type: {
|
||||||
@@ -2663,6 +2727,13 @@ export default {
|
|||||||
name: "Any",
|
name: "Any",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "browserSession",
|
||||||
|
type: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "device",
|
name: "device",
|
||||||
type: {
|
type: {
|
||||||
|
Reference in New Issue
Block a user