Verified Commit 46a57754 authored by Mark's avatar Mark
Browse files

Merge branch 'master' into 19-feature-link-to-the-applications-included-in-oas

parents ba209302 b65ce8ad
Pipeline #1851 passed with stage
in 2 minutes and 41 seconds
This diff is collapsed.
......@@ -175,7 +175,7 @@ class RemoveApplicationFromUser(graphene.Mutation):
ok = False
user = user_datastore.db.session().query(UserModel).filter_by(username=username).first()
application = user_datastore.db.session().query(ApplicationModel).filter_by(name=application).first()
if user is not None:
if user is not None and application is not None:
user.applications.remove(application)
user_datastore.commit()
ok = True
......@@ -191,7 +191,7 @@ class DeleteUser(graphene.Mutation):
def mutate(self, info, username):
ok = False
user = user_datastore.db.session().query(UserModel).filter_by(username=username).first()
if user:
if user is not None:
user_datastore.delete_user(user)
user_datastore.commit()
ok = True
......@@ -213,6 +213,9 @@ class Query(graphene.ObjectType):
def resolve_verify_password(root, info, username, password):
query = User.get_query(info)
user = query.filter(UserModel.username == username).first()
# verify_and_update_password returns True if the password is valid for the specified
# user. Additionally, the hashed password in the database is updated if the hashing
# algorithm happens to have changed.
return verify_and_update_password(password, user)
......
......@@ -172,15 +172,7 @@ class GraphQlTests(LiveServerTestCase):
role{{name}}
}}}}'''.format(username, role_name)
self.client.execute(querystring)
querystring = '''
mutation{{
removeRoleFromUser(
username: "{0}",
role: "{1}"){{ ok }}
}}'''.format(username, role_name)
result = loads(self.client.execute(querystring))
self.assertTrue(result["data"]["removeRoleFromUser"]["ok"])
querystring = '''
get_user_querystring = '''
{{
getUser(username: "{0}"){{
roles{{
......@@ -189,7 +181,17 @@ class GraphQlTests(LiveServerTestCase):
}}
}}
'''.format(username)
result = loads(self.client.execute(get_user_querystring))
self.assertEqual(1, len(result["data"]["getUser"]["roles"]["edges"]))
querystring = '''
mutation{{
removeRoleFromUser(
username: "{0}",
role: "{1}"){{ ok }}
}}'''.format(username, role_name)
result = loads(self.client.execute(querystring))
self.assertTrue(result["data"]["removeRoleFromUser"]["ok"])
result = loads(self.client.execute(get_user_querystring))
self.assertEqual(0, len(result["data"]["getUser"]["roles"]["edges"]))
......@@ -209,15 +211,7 @@ class GraphQlTests(LiveServerTestCase):
application{{name}}
}}}}'''.format(username, application_name)
self.client.execute(querystring)
querystring = '''
mutation{{
removeApplicationFromUser(
username: "{0}",
application: "{1}"){{ ok }}
}}'''.format(username, application_name)
result = loads(self.client.execute(querystring))
self.assertTrue(result["data"]["removeApplicationFromUser"]["ok"])
querystring = '''
get_user_querystring = '''
{{
getUser(username: "{0}"){{
applications{{
......@@ -226,7 +220,17 @@ class GraphQlTests(LiveServerTestCase):
}}
}}
'''.format(username)
result = loads(self.client.execute(get_user_querystring))
self.assertEqual(1, len(result["data"]["getUser"]["applications"]["edges"]))
querystring = '''
mutation{{
removeApplicationFromUser(
username: "{0}",
application: "{1}"){{ ok }}
}}'''.format(username, application_name)
result = loads(self.client.execute(querystring))
self.assertTrue(result["data"]["removeApplicationFromUser"]["ok"])
result = loads(self.client.execute(get_user_querystring))
self.assertEqual(0, len(result["data"]["getUser"]["applications"]["edges"]))
......
FROM node:13-alpine
WORKDIR /usr/src/app
expose 3000
EXPOSE 3000
COPY package*.json ./
......@@ -10,8 +10,8 @@ RUN npm install
COPY . .
ENV HOST sso.example.net
ENV BACKEND_API_URL http://oas.localhost:5002/graphql
ENV BASE_URL http://sso.example.net;3000
ENV BACKEND_API_URL http://sso.example.net:5002/graphql
ENV BASE_URL http://sso.example.net:3000
ENV HYDRA_BASE_URL http://oas.example.net:4444
ENV AUTHORIZE_URL http://oas.example.net:4444/oauth2/auth
ENV USERINFO_URL http://oas.example.net:4444/userinfo
......
### Installation
### Prerequisites
In order to run this application, npm (>= 6.9.0) needs to be installed. As the application
will authenticate its users using OpenID Connect, an OpenID Connect Provider and an OpenID Client
needs to be set up.
This application was developed to be used with [an OpenID Connect single sign on system](https://open.greenhost.net/openappstack/single-sign-on).
After installing `npm` run
### Configuration
Configuration has to be specified in `nuxt.config.js`. Setting the OpenID Connect parameters
correctly is essential for the login process to work. Note that there are also environment
variables that can be set instead of changing the defaults in `nuxt.config.js`.
In case you want to configure the application by using those variables you have to set and export them like this:
```
export HOST=sso.example.net
export BACKEND_API_URL=http://oas.localhost:5002/graphql
export BASE_URL=http://sso.example.net:3000
export HYDRA_BASE_URL=http://oas.example.net:4444
export AUTHORIZE_URL=http://oas.example.net:4444/oauth2/auth
export USERINFO_URL=http://oas.example.net:4444/userinfo
export ACCESS_TOKEN=http://oas.example.net:4444/oauth2/token
export REDIRECT_URL=http://sso.example.net:3000/callback
export OAUTH_CLIENT_ID=user-panel
export OAUTH_CLIENT_SECRET=secret_secret
```
Instead of using your own OpenID Provider, you can also use external providers. For details on how
to configure authentication for external providers like GitHub, please refer to [the nuxt-oauth documentation](https://auth.nuxtjs.org/schemes/oauth2.html)
### Installation
Installation is done with `npm`. Execute the following commands after changing to the user-panel/frontend
directory.
```
cd user-panel/frontend
npm install --production
```
......@@ -25,14 +55,6 @@ or
npm build-start
```
### Configuration
Configuration has to be specified in `nuxt.config.js`. Setting the OpenID Connect paramaters
correctly is essential for the login process to work.
For details on how to configure authentication for external providers like GitHub or Facebook
please refer to [the nuxt-oauth documentation](https://auth.nuxtjs.org/schemes/oauth2.html)
### Usage
......@@ -40,27 +62,52 @@ Navigate your browser to the `server` URL, that you specified in `nuxt.config.js
is 3000.
As an administrator you can access the user backend API by accessing the api endpoint
that is exposed by the userpanel at `/api/admin/graphql`. If you run your backend server in
that is exposed by the user-panel at `/api/admin/graphql`. If you run your backend server in
development mode, you can access the `GraphiQL` web interface of the backend by navigating
your browser to the URL.
Note that you need to manually provide a cookie containing the Baerer token in case you don't
Note that you need to manually provide a cookie containing the Bearer token in case you don't
access the api with a browser that automatically includes your cookie in the request.
Passing the token as a GET or POST variable is not supported.
### Run the tests
### Testing
Install the development dependencies with
The test folder includes end to end tests that you can run from your workstation to check if
your setup is working properly especially in case you want to make changes to the code.
#### Prerequisites
E2E tests are executed with chromedriver. Make sure that you have a supported version
of chromium installed. To downgrade the chromium version that is used in the tests, change
the version number in `package.json` [accordingly](https://www.npmjs.com/package/chromedriver).
In order to run the tests you also need to install additional node packages.
The development and testing dependencies can be installed with
```
npm install # without --production
```
Configure the application as described above.
After creating two regular users and an admin user by assigning the role `admin` to the admin
user and granting the admin and one of the test users access to the `user-panel` application,
configure the default values in `test/nightwatch_globals.js`. Don't forget to set the usernames
and passwords of the test users you created. After that, run `npm run test`.
The tests will use login credentials of 3 different users.
* Create a user that has the role `admin`and the application `user-panel` assigned to it.
* Create a second user and only assign the application `user-panel`.
* Create a third user and don't assign anything to it.
Note: E2E tests are executed with chromedriver. Make sure that you have a supported version
of chromium installed. To downgrade the chromium version that is used in the tests, change
the version number in `package.json` [accordingly](https://www.npmjs.com/package/chromedriver).
You can use the utility scripts provided in the backend folder of this repo to create the users
applications and roles. To do that execute:
```
bash ../backend/utils/create-application.bash user-panel
bash ../backend/utils/create-role.bash admin
bash ../backend/utils/create-user.bash admin adminadmin admin@example.net
bash ../backend/utils/create-user.bash testuser testtest testuser@example.net
bash ../backend/utils/create-user.bash testuser2 testtest testuser2@example.net
bash ../backend/utils/assign-role.bash admin admin
bash ../backend/utils/grant-access.bash admin user-panel
bash ../backend/utils/grant-access.bash testuser user-panel
```
After that configure the values in `test/nightwatch_globals.js` to match the usernames and
passwords.
#### Run the tests
To run the tests, execute `npm run test`
......@@ -30,22 +30,24 @@ const authenticateWithToken = function (req, res, next) {
token_type: "Bearer"
})
// We don't trust the token beacuse we received it via
// the request object which is create by the client.
// In order to veryfi the token, we query the userinfo
// the request object which is created by the client.
// In order to verify the token, we query the userinfo
// endpoint using the provided token. If the OpenID
// provider acceppts the token, we know it's valid and
// we will get the userinfo as at the same time
// provider accepts the token, we know it's valid and
// we will get the userinfo at the same time
openIdClient.userinfo(tokenSet).then(function(userinfo){
// Safe our validated userinfo in the request object
// for use in other express functions
// Save our validated userinfo in the request object
// for use in other express.js middleware functions
req.user = {
name: userinfo.name,
id: userinfo.sid,
sid: userinfo.sid,
roles: userinfo[nuxtConfig.auth.scopeKey]
}
if ( userinfo.hasOwnProperty('preferred_username')){
req.user.name = userinfo.preferred_username;
}
else {
req.user.name = userinfo.name
}
next()
}).catch(function(error){
res.status(403).send("Authentication failed: " + error)
......
......@@ -4,7 +4,7 @@ import Transport from 'lokka-transport-http';
export default class OpenAppStackModel {
constructor(graphQlUrl){
this.backendApiUrl = graphQlUrl || 'http://127.0.0.1:5000/graphql';
this.backendApiUrl = graphQlUrl;
this.graphQlClient = new Lokka({
transport: new Transport(this.backendApiUrl)
});
......@@ -121,15 +121,15 @@ export default class OpenAppStackModel {
}
deleteUser(username){
const vars = { username: username };
const deleteUser = `
mutation editUser($username: String!){
const deleteUserMutation = `
mutation deleteUser($username: String!){
deleteUser(
username: $username
){
ok
}
}`;
return this.graphQlClient.query(deleteUser, vars)
return this.graphQlClient.query(deleteUserMutation, vars)
.then( ok => {
return ok;
});
......
......@@ -3,8 +3,9 @@ module.exports = {
host: process.env.HOST || '0.0.0.0'
},
env: {
BACKEND_API_URL: process.env.BACKEND_API_URL || 'http://localhost:5002/graphql',
baseUrl: process.env.BASE_URL || 'http://sso.example.net:3000'},
BACKEND_API_URL: process.env.BACKEND_API_URL,
baseUrl: process.env.BASE_URL
},
serverMiddleware: [
'~/api/index.js'
],
......@@ -20,16 +21,16 @@ module.exports = {
strategies: {
oas: {
_scheme: 'oauth2',
base_url: process.env.HYDRA_BASE_URL || 'http://oas.example.net:4444',
authorization_endpoint: process.env.AUTHORIZE_URL || 'http://oas.example.net:4444/oauth2/auth',
userinfo_endpoint: process.env.USERINFO_URL || 'http://oas.example.net:4444/userinfo',
base_url: process.env.HYDRA_BASE_URL,
authorization_endpoint: process.env.AUTHORIZE_URL,
userinfo_endpoint: process.env.USERINFO_URL,
scope: ['openid', 'profile', 'email', 'openappstack_roles'],
access_token_endpoint: process.env.ACCESS_TOKEN_URL || 'http://oas.example.net:4444/oauth2/token',
access_token_endpoint: process.env.ACCESS_TOKEN_URL,
response_type: 'token',
token_type: 'Bearer',
redirect_uri: process.env.REDIRECT_URL || 'http://oas.example.net:3000/callback',
client_id: process.env.OAUTH_CLIENT_ID || 'user-panel',
client_secret: process.env.OAUTH_CLIENT_SECRET || 'secret_secret',
redirect_uri: process.env.REDIRECT_URL,
client_id: process.env.OAUTH_CLIENT_ID,
client_secret: process.env.OAUTH_CLIENT_SECRET,
}
},
scopeKey: "openappstack_roles"
......
{
"name": "user-panel",
"version": "1.2.0",
"description": "",
"description": "Management interface for an OpenID Connect identity provider backend",
"main": "nuxt.config.js",
"scripts": {
"dev": "nuxt",
......@@ -12,8 +12,8 @@
"test": "nightwatch --config ./test/nightwatch.json",
"build-start": "nuxt build; nuxt start"
},
"author": "",
"license": "ISC",
"homepage": "https://open.greenhost.net/openappstack/user-panel",
"license": "SEE LICENSE IN LICENSE.txt",
"dependencies": {
"@nuxtjs/auth": "^4.8.4",
"@nuxtjs/axios": "^5.6.0",
......@@ -21,7 +21,7 @@
"cookie-parser": "^1.4.4",
"lokka": "^1.7.0",
"lokka-transport-http": "^1.6.1",
"nuxt": "latest",
"nuxt": "^2.10.2",
"openid-client": "^3.7.3"
},
"devDependencies": {
......
......@@ -42,7 +42,7 @@
{{ newUsernameFeedback }}
</b-form-invalid-feedback>
<b-form-text id="input-new-username-help">
User name that new user will use to log in
Username that new user will use to log in
</b-form-text>
</div>
</b-form>
......
......@@ -15,7 +15,7 @@ module.exports = {
adminUsername: "admin",
adminPassword: "adminadmin",
userUsername: "testuser",
userPassword: "testtest"
userPassword: "testtest",
userWithoutAppPermissionUsername: "testuser2",
userWithoutAppPermossionPassword: "testtest"
},
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment