You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-08-07 17:03:01 +03:00
Allow more characters in device IDs
This commit is contained in:
@@ -70,13 +70,35 @@ impl Device {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn valid_device_chars(c: char) -> bool {
|
||||||
|
// This matches the regex in the policy
|
||||||
|
c.is_ascii_alphanumeric()
|
||||||
|
|| c == '.'
|
||||||
|
|| c == '_'
|
||||||
|
|| c == '~'
|
||||||
|
|| c == '!'
|
||||||
|
|| c == '$'
|
||||||
|
|| c == '&'
|
||||||
|
|| c == '\''
|
||||||
|
|| c == '('
|
||||||
|
|| c == ')'
|
||||||
|
|| c == '*'
|
||||||
|
|| c == '+'
|
||||||
|
|| c == ','
|
||||||
|
|| c == ';'
|
||||||
|
|| c == '='
|
||||||
|
|| c == ':'
|
||||||
|
|| c == '@'
|
||||||
|
|| c == '/'
|
||||||
|
|| c == '-'
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<String> for Device {
|
impl TryFrom<String> for Device {
|
||||||
type Error = InvalidDeviceID;
|
type Error = InvalidDeviceID;
|
||||||
|
|
||||||
/// Create a [`Device`] out of an ID, validating the ID has the right shape
|
/// Create a [`Device`] out of an ID, validating the ID has the right shape
|
||||||
fn try_from(id: String) -> Result<Self, Self::Error> {
|
fn try_from(id: String) -> Result<Self, Self::Error> {
|
||||||
// This matches the regex in the policy
|
if !id.chars().all(valid_device_chars) {
|
||||||
if !id.chars().all(|c| c.is_ascii_alphanumeric() || c == '-') {
|
|
||||||
return Err(InvalidDeviceID::InvalidCharacters);
|
return Err(InvalidDeviceID::InvalidCharacters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@ import { Route as rootRoute } from './routes/__root'
|
|||||||
import { Route as ResetCrossSigningImport } from './routes/reset-cross-signing'
|
import { Route as ResetCrossSigningImport } from './routes/reset-cross-signing'
|
||||||
import { Route as AccountImport } from './routes/_account'
|
import { Route as AccountImport } from './routes/_account'
|
||||||
import { Route as AccountIndexImport } from './routes/_account.index'
|
import { Route as AccountIndexImport } from './routes/_account.index'
|
||||||
import { Route as DevicesIdImport } from './routes/devices.$id'
|
import { Route as DevicesSplatImport } from './routes/devices.$'
|
||||||
import { Route as ClientsIdImport } from './routes/clients.$id'
|
import { Route as ClientsIdImport } from './routes/clients.$id'
|
||||||
import { Route as AccountSessionsIndexImport } from './routes/_account.sessions.index'
|
import { Route as AccountSessionsIndexImport } from './routes/_account.sessions.index'
|
||||||
import { Route as EmailsIdVerifyImport } from './routes/emails.$id.verify'
|
import { Route as EmailsIdVerifyImport } from './routes/emails.$id.verify'
|
||||||
@@ -38,8 +38,8 @@ const AccountIndexRoute = AccountIndexImport.update({
|
|||||||
getParentRoute: () => AccountRoute,
|
getParentRoute: () => AccountRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
const DevicesIdRoute = DevicesIdImport.update({
|
const DevicesSplatRoute = DevicesSplatImport.update({
|
||||||
path: '/devices/$id',
|
path: '/devices/$',
|
||||||
getParentRoute: () => rootRoute,
|
getParentRoute: () => rootRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
@@ -84,8 +84,8 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof ClientsIdImport
|
preLoaderRoute: typeof ClientsIdImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
}
|
}
|
||||||
'/devices/$id': {
|
'/devices/$': {
|
||||||
preLoaderRoute: typeof DevicesIdImport
|
preLoaderRoute: typeof DevicesSplatImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
}
|
}
|
||||||
'/_account/': {
|
'/_account/': {
|
||||||
@@ -122,7 +122,7 @@ export const routeTree = rootRoute.addChildren([
|
|||||||
]),
|
]),
|
||||||
ResetCrossSigningRoute,
|
ResetCrossSigningRoute,
|
||||||
ClientsIdRoute,
|
ClientsIdRoute,
|
||||||
DevicesIdRoute,
|
DevicesSplatRoute,
|
||||||
EmailsIdVerifyRoute,
|
EmailsIdVerifyRoute,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@@ -71,8 +71,8 @@ export const Route = createRootRouteWithContext<{
|
|||||||
case "org.matrix.session_view":
|
case "org.matrix.session_view":
|
||||||
if (search.device_id)
|
if (search.device_id)
|
||||||
throw redirect({
|
throw redirect({
|
||||||
to: "/devices/$id",
|
to: "/devices/$",
|
||||||
params: { id: search.device_id },
|
params: { _splat: search.device_id },
|
||||||
});
|
});
|
||||||
throw redirect({ to: "/sessions", search: { last: PAGE_SIZE } });
|
throw redirect({ to: "/sessions", search: { last: PAGE_SIZE } });
|
||||||
|
|
||||||
@@ -80,8 +80,8 @@ export const Route = createRootRouteWithContext<{
|
|||||||
case "org.matrix.session_end":
|
case "org.matrix.session_end":
|
||||||
if (search.device_id)
|
if (search.device_id)
|
||||||
throw redirect({
|
throw redirect({
|
||||||
to: "/devices/$id",
|
to: "/devices/$",
|
||||||
params: { id: search.device_id },
|
params: { _splat: search.device_id },
|
||||||
});
|
});
|
||||||
throw redirect({ to: "/sessions", search: { last: PAGE_SIZE } });
|
throw redirect({ to: "/sessions", search: { last: PAGE_SIZE } });
|
||||||
|
|
||||||
|
@@ -42,7 +42,7 @@ const QUERY = graphql(/* GraphQL */ `
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
export const Route = createFileRoute("/devices/$id")({
|
export const Route = createFileRoute("/devices/$")({
|
||||||
async loader({ context, params, abortController: { signal } }) {
|
async loader({ context, params, abortController: { signal } }) {
|
||||||
const viewer = await context.client.query(
|
const viewer = await context.client.query(
|
||||||
CURRENT_VIEWER_QUERY,
|
CURRENT_VIEWER_QUERY,
|
||||||
@@ -57,7 +57,7 @@ export const Route = createFileRoute("/devices/$id")({
|
|||||||
const result = await context.client.query(
|
const result = await context.client.query(
|
||||||
QUERY,
|
QUERY,
|
||||||
{
|
{
|
||||||
deviceId: params.id,
|
deviceId: params._splat,
|
||||||
userId: viewer.data.viewer.id,
|
userId: viewer.data.viewer.id,
|
||||||
},
|
},
|
||||||
{ fetchOptions: { signal } },
|
{ fetchOptions: { signal } },
|
||||||
@@ -78,7 +78,7 @@ export const Route = createFileRoute("/devices/$id")({
|
|||||||
|
|
||||||
function NotFound(): React.ReactElement {
|
function NotFound(): React.ReactElement {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { id: deviceId } = Route.useParams();
|
const { _splat: deviceId } = Route.useParams();
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Alert
|
<Alert
|
@@ -62,7 +62,7 @@ allowed_scope("urn:mas:admin") {
|
|||||||
allowed_scope(scope) {
|
allowed_scope(scope) {
|
||||||
# Grant access to the C-S API only if there is a user
|
# Grant access to the C-S API only if there is a user
|
||||||
interactive_grant_type(input.grant_type)
|
interactive_grant_type(input.grant_type)
|
||||||
regex.match("urn:matrix:org.matrix.msc2967.client:device:[A-Za-z0-9-]{10,}", scope)
|
regex.match("^urn:matrix:org.matrix.msc2967.client:device:[A-Za-z0-9._~!$&'()*+,;=:@/-]{10,}$", scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
allowed_scope("urn:matrix:org.matrix.msc2967.client:api:*") {
|
allowed_scope("urn:matrix:org.matrix.msc2967.client:api:*") {
|
||||||
|
Reference in New Issue
Block a user