Skip to content

Commit

Permalink
Add search by matriculation number
Browse files Browse the repository at this point in the history
  • Loading branch information
flofriday committed Aug 1, 2021
1 parent 2bc9570 commit 58ed22a
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 147 deletions.
115 changes: 65 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# TU Wien Addressbook (unofficial)

An unofficial Android and iOS App to search [TU Wien](https://www.tuwien.at/en/) employees and students.

![Screenshot](screenshot.png)
Expand All @@ -9,129 +10,143 @@ An unofficial Android and iOS App to search [TU Wien](https://www.tuwien.at/en/)
API, the app itself is not official!

## Features
* Search [TU Wien](https://www.tuwien.at/en/) employees and students wihout opening TISS
* Filter search results
* Optional login (needed to get student information)
* Dark Mode

- Search [TU Wien](https://www.tuwien.at/en/) employees and students wihout opening TISS
- Search by matriculation number, or name
- Filter search results
- Optional login (needed to get student information)
- Dark Mode

## Future Features

Things I should implement, but propably won't.

* Find students by their mattriculation number
* Support english
* Indicate if the user is logged in while searching
* Indicate that there are no student information without logging in
* Improve Settings UI
- Support english
- Indicate if the user is logged in while searching
- Indicate that there are no student information without logging in
- Improve Settings UI

## Build it yourself
1) [Install Flutter](https://flutter.dev/docs/get-started/install)
2) Clone this repository or download it.
3) Connect your phone to your computer.
4) Open the repository in your terminal.
5) For Android run: `flutter build apk --release && flutter install`<br>
For iOS you need to sign the App yourself to use it. Here is a
[Guide](https://medium.com/front-end-weekly/how-to-test-your-flutter-ios-app-on-your-ios-device-75924bfd75a8)
on how to do this.

1. [Install Flutter](https://flutter.dev/docs/get-started/install)
2. Clone this repository or download it.
3. Connect your phone to your computer.
4. Open the repository in your terminal.
5. For Android run: `flutter build apk --release && flutter install`<br>
For iOS you need to sign the App yourself to use it. Here is a
[Guide](https://medium.com/front-end-weekly/how-to-test-your-flutter-ios-app-on-your-ios-device-75924bfd75a8)
on how to do this.

## Frequently asked Question

### Why is the App in German?
Many of the TISS API responses are in German, so in the spirit of consistency

Many of the TISS API responses are in German, so in the spirit of consistency
I just designed the whole App in German.

### The search results are bad!

I know :pensive:<br>
There is just nothing I can do about it, because the App gets them from the TISS
There is just nothing I can do about it, because the App gets them from the TISS
API.

### Why is the search so slow?
Again, there is nothing I can do about it as the API response just take a long
time. However, I figured out, the more specific you are, the faster the

Again, there is nothing I can do about it as the API response just take a long
time. However, I figured out, the more specific you are, the faster the
responses are.

### Where do you get the data from?
Actually, there is an official
[TISS REST API](https://tiss.tuwien.ac.at/api/dokumentation). While the API is
public (or semi-public, but I will come back to this later), the documentation

Actually, there is an official
[TISS REST API](https://tiss.tuwien.ac.at/api/dokumentation). While the API is
public (or semi-public, but I will come back to this later), the documentation
is not, so you need to be able to login to [TISS](https://tiss.tuwien.ac.at/).

However, if you don't have access to [TISS](https://tiss.tuwien.ac.at/), don't worry as
many aspects of the API are not documented anyway. For example, there is no
documentation about what the response json looks like or that you can set
However, if you don't have access to [TISS](https://tiss.tuwien.ac.at/), don't worry as
many aspects of the API are not documented anyway. For example, there is no
documentation about what the response json looks like or that you can set
the language via a query parameter or that you will only receive student
information if you are logged in.

To search people on the API, I use the `/api/person/v22/psuche` endpoint.
Here is a full example:

```
https://tiss.tuwien.ac.at/api/person/v22/psuche?q=Panholz&max_treffer=50
```

Here is a list of all query parameters:
| Name | Type | Datatype | Description |
| Name | Type | Datatype | Description |
|-------------|:-----:|---------:|----------------------------------------------------------------------------------------------------------------|
| q | path | string | Searchterm |
| intern | query | bool | If internal persons(like students) should be included. This only works if you are logged in. Default is false. |
| max_treffer | query | int | Upper limit of results. Limit is 100, Default is 15. |
| locale | query | string | The language of (some?) fields of the result. It uses 2 letter codes like "de" or "en". Default is ???? |
| q | path | string | Searchterm |
| intern | query | bool | If internal persons(like students) should be included. This only works if you are logged in. Default is false. |
| max_treffer | query | int | Upper limit of results. Limit is 100, Default is 15. |
| locale | query | string | The language of (some?) fields of the result. It uses 2 letter codes like "de" or "en". Default is ???? |

### How does the app log in, to get student information?
So most sane developers, would design the authentication process of an API with an
API-token or maybe just let you send the username and password with every

So most sane developers, would design the authentication process of an API with an
API-token or maybe just let you send the username and password with every
request. (API-token is the way better solution)

Unfortunately, I couldn't find anything like that, so we will do it like the
Webinterface with cookies, parsing HTML, parsing URLs, following redirects and
Unfortunately, I couldn't find anything like that, so we will do it like the
Webinterface with cookies, parsing HTML, parsing URLs, following redirects and
many more cookies.

Sounds fun? I guarantee you **IT IS NOT**!

#### STEP 0: Requirements
Many requests will set you a cookie, so just save them and if you do a request

Many requests will set you a cookie, so just save them and if you do a request
in the future to the same domain just send them with the request.

#### STEP 1: Create a session and collect the AuthState
First start with a `GET` request to

First start with a `GET` request to
`https://tiss.tuwien.ac.at/admin/authentifizierung`. The server will answer with
the status 302 (Redirect). Now you follow multiple redirects (like 5 or 6)
the status 302 (Redirect). Now you follow multiple redirects (like 5 or 6)
until the server finally answers with 200 (OK).

Now parse the URL on which you finally landed on, because in the URL should be
a query parameter called `AuthState` which we need to save for the next request.

#### STEP 2: Get the first login cookie and parsing HTML
Next, we need to make a `POST` request to

Next, we need to make a `POST` request to
`https://idp.zid.tuwien.ac.at/simplesaml/module.php/core/loginuserpass.php`.

The data we send with that request is form-encoded and there are the following
fields:
|Name|Content|
|--|--|
| username | *your TU Wien username* |
| password | *your TU Wien password* |
| totp | *empty (This field is for Two Factor Authentication*|
| AuthState | *the AuthState we collected in Step 1* |
| username | _your TU Wien username_ |
| password | _your TU Wien password_ |
| totp | _empty (This field is for Two Factor Authentication_|
| AuthState | _the AuthState we collected in Step 1_ |

The server should now respond with HTML. In this HTML is a Form with two hidden
fields: `SAMLResponse` and `RelayState`. You now need to parse the HTML and get
the content of both fields and save them.

#### Step 3: Get the second login cookie

Make a `POST` request to `https://login.tuwien.ac.at/auth/postResponse` with
the following form-encoded data:
|Name|Content|
|--|--|
| SAMLResponse | *the SAMLResponse from Step 2* |
| RelayState | *the RelayState from Step 2* |
| SAMLResponse | _the SAMLResponse from Step 2_ |
| RelayState | _the RelayState from Step 2_ |

The server will respond with status 303 (Redirect), save that url.

#### Step 4: Get the final TISS cookie
With `GET` requests follow the redirects until the server responds with

With `GET` requests follow the redirects until the server responds with
200 (OK), starting with the location we got at the end of Step 3.

While following these redirects the server will eventually set you cookie called
`TISS_AUTH`. This is the one you ~wanted~ needed all along. Now, all you have
to do is make requests to the TISS API and send this cookie with them.
While following these redirects the server will eventually set you cookie called
`TISS_AUTH`. This is the one you ~wanted~ needed all along. Now, all you have
to do is make requests to the TISS API and send this cookie with them.

🥳 🎉
123 changes: 60 additions & 63 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class App extends StatelessWidget {
title: 'TU Addressbuch',
locale: Locale('de', 'AT'),
theme: ThemeData(
brightness: Brightness.light,
accentColor: Colors.indigo[400],
primarySwatch: Colors.blueGrey,
scaffoldBackgroundColor: Colors.blueGrey[50],
Expand Down Expand Up @@ -94,73 +95,69 @@ class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
//Padding(padding: EdgeInsets.only(top: 100)),
Expanded(child: Container()),
Center(
child: Text("Suche", style: Theme.of(context).textTheme.headline3),
),
Padding(
padding: EdgeInsets.all(16),
child: ElevatedButton(
/*
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100.0),
),
color: Theme.of(context).cardColor,
*/
onPressed: () {
showSearch(context: context, delegate: PersonSearch());
},
child: Padding(
padding: EdgeInsets.all(8),
child: Row(
children: [
Icon(
Icons.search,
size: 24,
),
Padding(
padding: EdgeInsets.only(left: 10),
),
Text(
"Studierende, Angestellte",
style: TextStyle(fontSize: 20),
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
//Padding(padding: EdgeInsets.only(top: 100)),
Expanded(child: Container()),
Center(
child: Text("Suche", style: Theme.of(context).textTheme.headline3),
),
Padding(
padding: EdgeInsets.all(16),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0),
)),
onPressed: () {
showSearch(context: context, delegate: PersonSearch());
},
child: Padding(
padding: EdgeInsets.all(8),
child: Row(
children: [
Icon(
Icons.search,
size: 24,
),
Padding(
padding: EdgeInsets.only(left: 10),
),
Text(
"Studierende, Angestellte",
style: TextStyle(fontSize: 20),
),
],
),
),
),
Expanded(
child: Container(),
flex: 2,
),
Padding(
padding: EdgeInsets.all(8),
child: RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style.copyWith(
fontSize: Theme.of(context).textTheme.caption!.fontSize),
children: [
TextSpan(
text: "Made with ❤️ by ",
),
TextSpan(
text: "flofriday",
//style: TextStyle(decoration: TextDecoration.underline),
recognizer: TapGestureRecognizer()
..onTap = () {
print("tap");
launchInBrowser("https://github.com/flofriday");
}),
]),
),
),

Expanded(
child: Container(),
flex: 2,
),
Padding(
padding: EdgeInsets.all(8),
child: RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style.copyWith(
fontSize: Theme.of(context).textTheme.caption!.fontSize),
children: [
TextSpan(
text: "Made with ❤️ by ",
),
TextSpan(
text: "flofriday",
//style: TextStyle(decoration: TextDecoration.underline),
recognizer: TapGestureRecognizer()
..onTap = () {
print("tap");
launchInBrowser("https://github.com/flofriday");
}),
]),
),
],
),
),
]),
);
}
}
Loading

0 comments on commit 58ed22a

Please sign in to comment.