From dd4ccf273692775dd7fec5eec6eb88055416c3a1 Mon Sep 17 00:00:00 2001 From: Armand <4831c0@proton.me> Date: Mon, 15 Sep 2025 12:21:40 +0200 Subject: [PATCH] wearos: fix pairing --- .../ui/phone/pages/extras/main_wear_pair.dart | 1 + .../lib/helpers/api/client/kreta_client.dart | 4 +- .../db/models/app_settings_model.g.dart | 78 +++-- .../lib/helpers/db/models/token_model.dart | 35 +-- .../lib/helpers/db/models/token_model.g.dart | 273 +++++++++++++++--- .../ui/wear/screens/login/login_screen.dart | 1 + 6 files changed, 292 insertions(+), 100 deletions(-) diff --git a/firka/lib/ui/phone/pages/extras/main_wear_pair.dart b/firka/lib/ui/phone/pages/extras/main_wear_pair.dart index 9ffb5129..6f32bfc9 100644 --- a/firka/lib/ui/phone/pages/extras/main_wear_pair.dart +++ b/firka/lib/ui/phone/pages/extras/main_wear_pair.dart @@ -99,6 +99,7 @@ void showWearBottomSheet( // "timetable": timetableArray, "auth": { "studentId": data.client.model.studentId, + "studentIdNorm": data.client.model.studentIdNorm, "iss": data.client.model.iss, "idToken": data.client.model.idToken, "accessToken": data.client.model.accessToken, diff --git a/firka_wear/lib/helpers/api/client/kreta_client.dart b/firka_wear/lib/helpers/api/client/kreta_client.dart index 4be1063d..ca27ea11 100644 --- a/firka_wear/lib/helpers/api/client/kreta_client.dart +++ b/firka_wear/lib/helpers/api/client/kreta_client.dart @@ -108,7 +108,7 @@ class KretaClient { // it would be *ideal* to use xor and left shift here, however // binary operations seem to round the number down to // 32 bits for some reason??? - var cacheKey = model.studentId! + ((id.index + 1) * pow(10, 11)); + var cacheKey = model.studentIdNorm! + ((id.index + 1) * pow(10, 11)); var cache = await isar.genericCacheModels.get(cacheKey as int); dynamic resp; @@ -234,7 +234,7 @@ class KretaClient { DateTime? to, bool forceCache, Future Function(dynamic, int) storeCache) async { - var cacheKey = genCacheKey(from, model.studentId!); + var cacheKey = genCacheKey(from, model.studentIdNorm!); var cache = await cacheModel.get(cacheKey); var formatter = DateFormat('yyyy-MM-dd'); var fromStr = formatter.format(from); diff --git a/firka_wear/lib/helpers/db/models/app_settings_model.g.dart b/firka_wear/lib/helpers/db/models/app_settings_model.g.dart index 37b7cc38..da5fc478 100644 --- a/firka_wear/lib/helpers/db/models/app_settings_model.g.dart +++ b/firka_wear/lib/helpers/db/models/app_settings_model.g.dart @@ -32,7 +32,7 @@ const AppSettingsModelSchema = CollectionSchema( serialize: _appSettingsModelSerialize, deserialize: _appSettingsModelDeserialize, deserializeProp: _appSettingsModelDeserializeProp, - idName: r'ignored', + idName: r'id', indexes: {}, links: {}, embeddedSchemas: {}, @@ -111,7 +111,7 @@ void _appSettingsModelAttach( extension AppSettingsModelQueryWhereSort on QueryBuilder { - QueryBuilder anyIgnored() { + QueryBuilder anyId() { return QueryBuilder.apply(this, (query) { return query.addWhereClause(const IdWhereClause.any()); }); @@ -120,69 +120,68 @@ extension AppSettingsModelQueryWhereSort extension AppSettingsModelQueryWhere on QueryBuilder { - QueryBuilder - ignoredEqualTo(Id ignored) { + QueryBuilder idEqualTo( + Id id) { return QueryBuilder.apply(this, (query) { return query.addWhereClause(IdWhereClause.between( - lower: ignored, - upper: ignored, + lower: id, + upper: id, )); }); } QueryBuilder - ignoredNotEqualTo(Id ignored) { + idNotEqualTo(Id id) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query .addWhereClause( - IdWhereClause.lessThan(upper: ignored, includeUpper: false), + IdWhereClause.lessThan(upper: id, includeUpper: false), ) .addWhereClause( - IdWhereClause.greaterThan(lower: ignored, includeLower: false), + IdWhereClause.greaterThan(lower: id, includeLower: false), ); } else { return query .addWhereClause( - IdWhereClause.greaterThan(lower: ignored, includeLower: false), + IdWhereClause.greaterThan(lower: id, includeLower: false), ) .addWhereClause( - IdWhereClause.lessThan(upper: ignored, includeUpper: false), + IdWhereClause.lessThan(upper: id, includeUpper: false), ); } }); } QueryBuilder - ignoredGreaterThan(Id ignored, {bool include = false}) { + idGreaterThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( - IdWhereClause.greaterThan(lower: ignored, includeLower: include), + IdWhereClause.greaterThan(lower: id, includeLower: include), ); }); } QueryBuilder - ignoredLessThan(Id ignored, {bool include = false}) { + idLessThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( - IdWhereClause.lessThan(upper: ignored, includeUpper: include), + IdWhereClause.lessThan(upper: id, includeUpper: include), ); }); } - QueryBuilder - ignoredBetween( - Id lowerIgnored, - Id upperIgnored, { + QueryBuilder idBetween( + Id lowerId, + Id upperId, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause(IdWhereClause.between( - lower: lowerIgnored, + lower: lowerId, includeLower: includeLower, - upper: upperIgnored, + upper: upperId, includeUpper: includeUpper, )); }); @@ -346,63 +345,63 @@ extension AppSettingsModelQueryFilter } QueryBuilder - ignoredIsNull() { + idIsNull() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(const FilterCondition.isNull( - property: r'ignored', + property: r'id', )); }); } QueryBuilder - ignoredIsNotNull() { + idIsNotNull() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'ignored', + property: r'id', )); }); } QueryBuilder - ignoredEqualTo(Id? value) { + idEqualTo(Id? value) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( - property: r'ignored', + property: r'id', value: value, )); }); } QueryBuilder - ignoredGreaterThan( + idGreaterThan( Id? value, { bool include = false, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.greaterThan( include: include, - property: r'ignored', + property: r'id', value: value, )); }); } QueryBuilder - ignoredLessThan( + idLessThan( Id? value, { bool include = false, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.lessThan( include: include, - property: r'ignored', + property: r'id', value: value, )); }); } QueryBuilder - ignoredBetween( + idBetween( Id? lower, Id? upper, { bool includeLower = true, @@ -410,7 +409,7 @@ extension AppSettingsModelQueryFilter }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.between( - property: r'ignored', + property: r'id', lower: lower, includeLower: includeLower, upper: upper, @@ -501,17 +500,16 @@ extension AppSettingsModelQuerySortThenBy }); } - QueryBuilder - thenByIgnored() { + QueryBuilder thenById() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'ignored', Sort.asc); + return query.addSortBy(r'id', Sort.asc); }); } QueryBuilder - thenByIgnoredDesc() { + thenByIdDesc() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'ignored', Sort.desc); + return query.addSortBy(r'id', Sort.desc); }); } @@ -549,9 +547,9 @@ extension AppSettingsModelQueryWhereDistinct extension AppSettingsModelQueryProperty on QueryBuilder { - QueryBuilder ignoredProperty() { + QueryBuilder idProperty() { return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'ignored'); + return query.addPropertyName(r'id'); }); } diff --git a/firka_wear/lib/helpers/db/models/token_model.dart b/firka_wear/lib/helpers/db/models/token_model.dart index b404b14f..2061c3fb 100644 --- a/firka_wear/lib/helpers/db/models/token_model.dart +++ b/firka_wear/lib/helpers/db/models/token_model.dart @@ -1,32 +1,16 @@ -/* - Firka, alternative e-Kréta client. - Copyright (C) 2025 QwIT Development - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation, either version 3 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; import 'package:firka_wear/helpers/api/resp/token_grant.dart'; import 'package:isar/isar.dart'; +import '../../api/resp/token_grant.dart'; import '../../debug_helper.dart'; part 'token_model.g.dart'; @collection class TokenModel { - Id? studentId; // Custom unique student identifier + Id? studentIdNorm; // Custom unique student identifier with "G0" removed + String? studentId; // Custom unique student identifier String? iss; // Institution id for student String? idToken; // Unique identifier for the token if needed String? accessToken; // The main auth token @@ -35,10 +19,11 @@ class TokenModel { TokenModel(); - factory TokenModel.fromValues(Id studentId, String iss, String idToken, - String accessToken, String refreshToken, int expiryDate) { + factory TokenModel.fromValues(Id studentIdNorm, studentId, String iss, + String idToken, String accessToken, String refreshToken, int expiryDate) { var m = TokenModel(); + m.studentIdNorm = studentIdNorm; m.studentId = studentId; m.iss = iss; m.idToken = idToken; @@ -53,16 +38,16 @@ class TokenModel { var m = TokenModel(); final jwt = JWT.decode(resp.idToken); - // TODO: Add a proper model for jwt id - - m.studentId = int.parse(jwt.payload["kreta:user_name"]); + m.studentIdNorm = int.parse( + jwt.payload["kreta:user_name"].toString().replaceAll("G0", "")); + m.studentId = jwt.payload["kreta:user_name"]; m.iss = jwt.payload["kreta:institute_code"]; m.idToken = resp.idToken; m.accessToken = resp.accessToken; m.refreshToken = resp.refreshToken; m.expiryDate = timeNow() .add(Duration(seconds: resp.expiresIn)) - .subtract(Duration(minutes: 10)); // just to be safe + .subtract(Duration(minutes: 1)); // just to be safe return m; } diff --git a/firka_wear/lib/helpers/db/models/token_model.g.dart b/firka_wear/lib/helpers/db/models/token_model.g.dart index dbe50d69..9765a4ca 100644 --- a/firka_wear/lib/helpers/db/models/token_model.g.dart +++ b/firka_wear/lib/helpers/db/models/token_model.g.dart @@ -41,13 +41,18 @@ const TokenModelSchema = CollectionSchema( id: 4, name: r'refreshToken', type: IsarType.string, + ), + r'studentId': PropertySchema( + id: 5, + name: r'studentId', + type: IsarType.string, ) }, estimateSize: _tokenModelEstimateSize, serialize: _tokenModelSerialize, deserialize: _tokenModelDeserialize, deserializeProp: _tokenModelDeserializeProp, - idName: r'studentId', + idName: r'studentIdNorm', indexes: {}, links: {}, embeddedSchemas: {}, @@ -87,6 +92,12 @@ int _tokenModelEstimateSize( bytesCount += 3 + value.length * 3; } } + { + final value = object.studentId; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } return bytesCount; } @@ -101,6 +112,7 @@ void _tokenModelSerialize( writer.writeString(offsets[2], object.idToken); writer.writeString(offsets[3], object.iss); writer.writeString(offsets[4], object.refreshToken); + writer.writeString(offsets[5], object.studentId); } TokenModel _tokenModelDeserialize( @@ -115,7 +127,8 @@ TokenModel _tokenModelDeserialize( object.idToken = reader.readStringOrNull(offsets[2]); object.iss = reader.readStringOrNull(offsets[3]); object.refreshToken = reader.readStringOrNull(offsets[4]); - object.studentId = id; + object.studentId = reader.readStringOrNull(offsets[5]); + object.studentIdNorm = id; return object; } @@ -136,13 +149,15 @@ P _tokenModelDeserializeProp

( return (reader.readStringOrNull(offset)) as P; case 4: return (reader.readStringOrNull(offset)) as P; + case 5: + return (reader.readStringOrNull(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); } } Id _tokenModelGetId(TokenModel object) { - return object.studentId ?? Isar.autoIncrement; + return object.studentIdNorm ?? Isar.autoIncrement; } List> _tokenModelGetLinks(TokenModel object) { @@ -150,12 +165,12 @@ List> _tokenModelGetLinks(TokenModel object) { } void _tokenModelAttach(IsarCollection col, Id id, TokenModel object) { - object.studentId = id; + object.studentIdNorm = id; } extension TokenModelQueryWhereSort on QueryBuilder { - QueryBuilder anyStudentId() { + QueryBuilder anyStudentIdNorm() { return QueryBuilder.apply(this, (query) { return query.addWhereClause(const IdWhereClause.any()); }); @@ -164,70 +179,71 @@ extension TokenModelQueryWhereSort extension TokenModelQueryWhere on QueryBuilder { - QueryBuilder studentIdEqualTo( - Id studentId) { + QueryBuilder studentIdNormEqualTo( + Id studentIdNorm) { return QueryBuilder.apply(this, (query) { return query.addWhereClause(IdWhereClause.between( - lower: studentId, - upper: studentId, + lower: studentIdNorm, + upper: studentIdNorm, )); }); } - QueryBuilder studentIdNotEqualTo( - Id studentId) { + QueryBuilder + studentIdNormNotEqualTo(Id studentIdNorm) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query .addWhereClause( - IdWhereClause.lessThan(upper: studentId, includeUpper: false), + IdWhereClause.lessThan(upper: studentIdNorm, includeUpper: false), ) .addWhereClause( - IdWhereClause.greaterThan(lower: studentId, includeLower: false), + IdWhereClause.greaterThan( + lower: studentIdNorm, includeLower: false), ); } else { return query .addWhereClause( - IdWhereClause.greaterThan(lower: studentId, includeLower: false), + IdWhereClause.greaterThan( + lower: studentIdNorm, includeLower: false), ) .addWhereClause( - IdWhereClause.lessThan(upper: studentId, includeUpper: false), + IdWhereClause.lessThan(upper: studentIdNorm, includeUpper: false), ); } }); } - QueryBuilder studentIdGreaterThan( - Id studentId, - {bool include = false}) { + QueryBuilder + studentIdNormGreaterThan(Id studentIdNorm, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( - IdWhereClause.greaterThan(lower: studentId, includeLower: include), + IdWhereClause.greaterThan(lower: studentIdNorm, includeLower: include), ); }); } - QueryBuilder studentIdLessThan( - Id studentId, + QueryBuilder studentIdNormLessThan( + Id studentIdNorm, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( - IdWhereClause.lessThan(upper: studentId, includeUpper: include), + IdWhereClause.lessThan(upper: studentIdNorm, includeUpper: include), ); }); } - QueryBuilder studentIdBetween( - Id lowerStudentId, - Id upperStudentId, { + QueryBuilder studentIdNormBetween( + Id lowerStudentIdNorm, + Id upperStudentIdNorm, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause(IdWhereClause.between( - lower: lowerStudentId, + lower: lowerStudentIdNorm, includeLower: includeLower, - upper: upperStudentId, + upper: upperStudentIdNorm, includeUpper: includeUpper, )); }); @@ -931,43 +947,197 @@ extension TokenModelQueryFilter } QueryBuilder studentIdEqualTo( - Id? value) { + String? value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( property: r'studentId', value: value, + caseSensitive: caseSensitive, )); }); } QueryBuilder studentIdGreaterThan( - Id? value, { + String? value, { bool include = false, + bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.greaterThan( include: include, property: r'studentId', value: value, + caseSensitive: caseSensitive, )); }); } QueryBuilder studentIdLessThan( - Id? value, { + String? value, { bool include = false, + bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.lessThan( include: include, property: r'studentId', value: value, + caseSensitive: caseSensitive, )); }); } QueryBuilder studentIdBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'studentId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + studentIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'studentId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder studentIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'studentId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder studentIdContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'studentId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder studentIdMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'studentId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + studentIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'studentId', + value: '', + )); + }); + } + + QueryBuilder + studentIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'studentId', + value: '', + )); + }); + } + + QueryBuilder + studentIdNormIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'studentIdNorm', + )); + }); + } + + QueryBuilder + studentIdNormIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'studentIdNorm', + )); + }); + } + + QueryBuilder + studentIdNormEqualTo(Id? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'studentIdNorm', + value: value, + )); + }); + } + + QueryBuilder + studentIdNormGreaterThan( + Id? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'studentIdNorm', + value: value, + )); + }); + } + + QueryBuilder + studentIdNormLessThan( + Id? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'studentIdNorm', + value: value, + )); + }); + } + + QueryBuilder + studentIdNormBetween( Id? lower, Id? upper, { bool includeLower = true, @@ -975,7 +1145,7 @@ extension TokenModelQueryFilter }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.between( - property: r'studentId', + property: r'studentIdNorm', lower: lower, includeLower: includeLower, upper: upper, @@ -1052,6 +1222,18 @@ extension TokenModelQuerySortBy return query.addSortBy(r'refreshToken', Sort.desc); }); } + + QueryBuilder sortByStudentId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'studentId', Sort.asc); + }); + } + + QueryBuilder sortByStudentIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'studentId', Sort.desc); + }); + } } extension TokenModelQuerySortThenBy @@ -1127,6 +1309,18 @@ extension TokenModelQuerySortThenBy return query.addSortBy(r'studentId', Sort.desc); }); } + + QueryBuilder thenByStudentIdNorm() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'studentIdNorm', Sort.asc); + }); + } + + QueryBuilder thenByStudentIdNormDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'studentIdNorm', Sort.desc); + }); + } } extension TokenModelQueryWhereDistinct @@ -1164,13 +1358,20 @@ extension TokenModelQueryWhereDistinct return query.addDistinctBy(r'refreshToken', caseSensitive: caseSensitive); }); } + + QueryBuilder distinctByStudentId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'studentId', caseSensitive: caseSensitive); + }); + } } extension TokenModelQueryProperty on QueryBuilder { - QueryBuilder studentIdProperty() { + QueryBuilder studentIdNormProperty() { return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'studentId'); + return query.addPropertyName(r'studentIdNorm'); }); } @@ -1203,4 +1404,10 @@ extension TokenModelQueryProperty return query.addPropertyName(r'refreshToken'); }); } + + QueryBuilder studentIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'studentId'); + }); + } } diff --git a/firka_wear/lib/ui/wear/screens/login/login_screen.dart b/firka_wear/lib/ui/wear/screens/login/login_screen.dart index 1a06da46..2f222556 100644 --- a/firka_wear/lib/ui/wear/screens/login/login_screen.dart +++ b/firka_wear/lib/ui/wear/screens/login/login_screen.dart @@ -49,6 +49,7 @@ class _WearLoginScreen extends State { () async { var data = msg["auth"]; var tokenModel = TokenModel.fromValues( + data["studentIdNorm"], data["studentId"], data["iss"], data["idToken"],