Skip to content

Commit

Permalink
Handle auth token with canEditResource access
Browse files Browse the repository at this point in the history
  • Loading branch information
burnpiro committed Jul 19, 2022
1 parent 8c49513 commit 9e8a72d
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 19 deletions.
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,27 @@ After starting the server you should be able to access [localhost/zhiva/app/expl
>
> You might be prompted with the message about invalid SSL certificate. This is caused by using OpenSSL to generate certificate for `localhost` and that certificate has no 3rd party that confirms its authenticity. It's fine for local network but remember to use proper certificate if the server is accessible from outside your network.
### User accounts

For every user in your system you have to generate new user account. To do this, you should access [https://localhost/auth/token](https://localhost/auth/token) and enter required fields. __Admin Password__ is the password you've entered in [Generate server settings](#generate-server-settings). At the end you'll be presented with one-off __AuthToken__.

__Auth Token__ is used to sign-in user in zhiva application. You can do it by selecting __zhiva Auth__ as __Authentication Type__ in __Settings -> Servers__ inside the application. You'll be presented with two options to authenticate user.

The first is __Token__ and this is the place when you can paste your token. After than you can validate your token.

The second option is to use __Login with zhiva__ button. You have to provide likt to authentication server sign-in website. Paste `https://localhost/auth/sign-in` into the input field and click on the login button. You will be prompted for __username__ and __password__ (you've created the user a second ago). After login you'll be redirected back to the application with server already configured.

### Access to orthanc explorer

Because this version of the server works with tokens you have to generate one in order to access local explorer. To generate token you have to go through the proces from [https://localhost/auth/token](https://localhost/auth/token) again. At the end just copy the token and access explorer by adding this token as query parameter. Your URI should look like this:
```bash
https://localhost/zhiva/app/explorer.html?token=YOUR_TOKEN_HERE
```

Remember to give your token __"Can edit PACS resources?"__ access, otherwise you won't be able to modify anything on the server.

This token expires in 24h or when the user uses it to login.

### Access from within internal network

If you have more than one computer inside your network (or VPN connection), then you can share the server settings with them. To check the server address please run the following command:
Expand All @@ -109,7 +130,7 @@ Windows
ipconfig
```

and look for the setting with the `inet` value that starts with `192.168.`. That should by your address in the local network. You should be able to access the upload page from `192.168.x.x:8042/app/explorer.html`.
and look for the setting with the `inet` value that starts with `192.168.`. That should by your address in the local network. You should be able to access the upload page from `192.168.x.x:8042/app/explorer.html?token=XYZ`.

#### Access control settings

Expand Down
45 changes: 30 additions & 15 deletions auth-server/pages/generateTokenPage.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,28 @@
</div>
<div class="flex mb-2 mt-4">
<div class="flex items-center h-5">
<input id="canEditResource" name="canEditResource" aria-describedby="canEditResource-checkbox-helper" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<input
id="canEditResource"
name="canEditResource"
value="true"
aria-describedby="canEditResource-checkbox-helper"
type="checkbox"
class="w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
/>
</div>
<div class="ml-2 text-sm">
<label for="canEditResource" class="font-medium text-blue-900 dark:text-blue-300">Can edit PACS resources?</label>
<p id="canEditResource-checkbox-helper" class="text-xs font-normal text-blue-500 dark:text-blue-300">Enables user to edit data on PACS. If not checked user will have readOnly access.</p>
<label
for="canEditResource"
class="font-medium text-blue-900 dark:text-blue-300"
>Can edit PACS resources?</label
>
<p
id="canEditResource-checkbox-helper"
class="text-xs font-normal text-blue-500 dark:text-blue-300"
>
Enables user to edit data on PACS. If not checked user will have
readOnly access.
</p>
</div>
</div>
<div class="mb-2">
Expand Down Expand Up @@ -150,9 +167,7 @@
>
Please enter new password.
</p>
<ul
class="list-disc text-blue-700 text-sm mt-2 pl-4"
>
<ul class="list-disc text-blue-700 text-sm mt-2 pl-4">
<li>At least 8 characters long</li>
<li>At least one uppercase letter</li>
<li>At least one lowercase letter</li>
Expand Down Expand Up @@ -199,7 +214,9 @@
</div>
</div>
<script>
let strongPassword = new RegExp('(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9])(?=.{8,})');
let strongPassword = new RegExp(
"(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9])(?=.{8,})"
);
const formElem = document.getElementById("loginForm");
const formErr = document.getElementById("formError");
const loadingElem = document.getElementById("loading");
Expand All @@ -221,8 +238,8 @@
});

const isPasswordStrong = (password) => {
return strongPassword.test(password)
}
return strongPassword.test(password);
};

const handleError = (error) => {
if (error === "User already exists") {
Expand All @@ -246,9 +263,7 @@

const sendRequest = (data, recreateToken) => {
// submit the data via XHR
const areValuesValid = Object.values(data).every(
(el) => el.length > 0
);
const areValuesValid = Object.values(data).every((el) => el !== '');

if (recreateToken) {
data["createUserIfNotExists"] = true;
Expand Down Expand Up @@ -299,7 +314,7 @@
formElem.onformdata = (e) => {
currFormData = Array.from(e.formData.entries()).reduce(
(acc, [key, val]) => {
acc[key] = val;
acc[key] = val === "true" ? true : val;
return acc;
},
{}
Expand All @@ -321,9 +336,9 @@
userPasswordInput.onkeyup = (e) => {
currFormData["userPassword"] = e.target.value;
if (!isPasswordStrong(currFormData["userPassword"])) {
confirmButton.setAttribute('disabled', '');
confirmButton.setAttribute("disabled", "");
} else {
confirmButton.removeAttribute('disabled');
confirmButton.removeAttribute("disabled");
}
};
</script>
Expand Down
23 changes: 20 additions & 3 deletions auth-server/services/pacsAuthService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Request, Response } from "express";
import { TOKEN_ERROR_MESSAGES, trimJWT, validateToken } from "../utils/jwt";
import { User } from "../db/schemas/user";
import { Token } from "../db/schemas/token";

type AccessObj = {
granted: boolean;
Expand All @@ -18,7 +19,7 @@ type OrthancRequestBody = {
};

async function validateAccessToken(
authorizationHeader: string
authorizationHeader: string, requestBody: OrthancRequestBody
): Promise<AccessObj> {
let token = trimJWT(authorizationHeader);

Expand All @@ -29,9 +30,25 @@ async function validateAccessToken(
throw Error(TOKEN_ERROR_MESSAGES.EXPIRED_TOKEN);
}

if(!tokenPayload.canEditResource && requestBody.method !== 'get') {
throw Error(TOKEN_ERROR_MESSAGES.UNAUTHORIZED_TOKEN);
}

let selectedToken = await Token.findOne({
type: "access",
token: token,
});

let selectedUser = await User.findOne({
username: tokenPayload.username,
accessToken: token,
$or: [
{
authToken: token,
},
{
accessTokens: [selectedToken ? selectedToken._id.valueOf() : null],
},
],
});

// Check if auth token exists in DB
Expand All @@ -50,7 +67,7 @@ export function pacsAuthService(
response: Response<AccessObj>
) {
if (request.method == "POST") {
validateAccessToken(request.body["token-value"])
validateAccessToken(request.body["token-value"], request.body)
.then((accessObj) => {
response.status(200);
response.send(accessObj);
Expand Down
1 change: 1 addition & 0 deletions auth-server/utils/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export enum TOKEN_ERROR_MESSAGES {
INVALID_TOKEN = "Invalid Token",
MISSING_TOKEN = "Missing Token",
EXPIRED_TOKEN = "Token Expired",
UNAUTHORIZED_TOKEN = "Unauthorized",
OTHER_ERROR = "Unexpected error",
}

Expand Down

0 comments on commit 9e8a72d

Please sign in to comment.