Impersonation with webapps#
Concepts#
Webapps can be accessed by any user with the necessary permissions, such as “Read dashboards” or “Read project content” after successfully logging in. You can refer to the Public webapps page if you require further information.
Each time a user uses a webapp, the code is executed on behalf of a user, defined by the “Run backend as” parameter configured in the webapp settings, as illustrated in Figure 1. Therefore, the actions performed by the user are attributed to the user mentioned in the “Run backend as” parameter.
For example, if user U1 runs a webapp with the “Run backend as” parameter set to A1, the actions taken by U1 will be identified as actions taken by the user A1. If you need the actions being identified as actions taken by user U1, you must make an impersonation call.
It is essential to consider the security and permission aspects of impersonation while using webapps. You can refer to this documentation to learn more about these aspects.
Impersonation usage#
Impersonation calls work on any web framework so that you can use your preferred web framework. Creating a simple webapp allows you to test, evaluate, and understand how impersonation works. When a job starts, Dataiku logs it. Then, you can observe impersonation by examining a Dataiku project’s interface and jobs section.
To test impersonation, you will create a web application that builds a dataset (i.e., starts a job). You can observe impersonation by examining a Dataiku project’s interface or the jobs section. To carry out this task, you will use the Dataiku TShirts project. To do so:
On the top left corner, click + New project > Sample project > Dataiku TShirts.
On the top navigation bar, navigate to the </> > Webapp section.
Click on + New Webapp and select Code Webapp.
Select the library with which you want to build your webapp.
The webapp aims to build a dataset present in the project; for example,
you will build the web_history_prepared
dataset.
Implementation#
Codes presented show two ways of getting user information, but there is only one way to do impersonation. You can also get user information by using the impersonation mode.
1<h1>Impersonation Demo</h1>
2
3<h2>Welcome: <span id="identified_user"></span></h2>
4
5<div class="build_dataset">
6 <form id="form-dataset" novalidate>
7 <button type="button" class="main-blue-button" id="build-button">Build the dataset</button>
8 </form>
9</div>
1/*
2 * For more information, refer to the "Javascript API" documentation:
3 * https://doc.dataiku.com/dss/latest/api/js/index.html
4 */
5
6let buildButton = document.getElementById('build-button');
7let identifiedUser = document.getElementById('identified_user')
8
9buildButton.addEventListener('click', function (event) {
10 datasetToBuild = "web_history_prepared"
11 $.get(getWebAppBackendUrl("/buil_dataset"), {datasetToBuild: datasetToBuild});
12});
13
14// When loading, get the user information
15$.getJSON(getWebAppBackendUrl('/get_user_name'), function (data) {
16 identifiedUser.textContent = data;
17});
1import dataiku
2import pandas as pd
3from flask import request, jsonify
4
5import logging
6
7logger = logging.getLogger(__name__)
8
9
10# Example:
11# As the Python webapp backend is a Flask app, refer to the Flask
12# documentation for more information about how to adapt this
13# example to your needs.
14# From JavaScript, you can access the defined endpoints using
15# getWebAppBackendUrl('first_api_call')
16
17@app.route('/get_user_name')
18def get_user_name():
19 logger.info("In it")
20 logger.info(request)
21 # Get user information from the request (can be done with impersonation)
22 headers = dict(request.headers)
23 auth_info = dataiku.api_client().get_auth_info_from_browser_headers(headers)
24 return (json.dumps(auth_info.get("associatedDSSUser")))
25
26 # Example of impersonation usage
27
28
29# with dataiku.WebappImpersonationContext() as ctx:
30# logger.info('impersonation')
31# # Using this context, your actions here will be impersonated.
32# client = dataiku.api_client()
33# user = client.get_own_user()
34# settings = user.get_settings()
35# logger.info(settings.get_raw())
36# return json.dumps(settings.get_raw().get('displayName'))
37
38
39@app.route('/buil_dataset')
40def build_dataset():
41 dataset = request.args.get('datasetToBuild')
42 logger.info("Impersonation begins...")
43 with dataiku.WebappImpersonationContext() as context:
44 # Each time your need to do impersonation, you need to obtain a client.
45 # Dash cannot store objects that are not in JSON format.
46 local_client = dataiku.api_client()
47 project = local_client.get_default_project()
48 outdataset = project.get_dataset(dataset)
49 outdataset.build()
50
51 logger.info("Impersonation ends...")
52 resp = jsonify(success=True)
53 return resp
1from dash import html, Input, Output
2from dash.exceptions import PreventUpdate
3from flask import request
4import logging
5import dataiku
6
7logger = logging.getLogger(__name__)
8
9dataset_to_build = "web_history_prepared"
10
11# build your Dash app
12app.layout = html.Div([
13 html.H1("Impersonation Demo", style={'text-align': 'center'}),
14 html.H3([
15 html.Span("Welcome: "),
16 html.Span(id="identified_user")
17 ]),
18 html.Button('Build the dataset', id='submit-val', n_clicks=0)
19])
20
21
22@app.callback(
23 Output('identified_user', 'children'),
24 Input('identified_user', 'children')
25)
26def load_user_credentials(children):
27 if children:
28 raise PreventUpdate
29 else:
30 # Get user info from request (can be done with impersonation)
31 request_headers = dict(request.headers)
32 auth_info = dataiku.api_client().get_auth_info_from_browser_headers(request_headers)
33 return auth_info.get("associatedDSSUser")
34
35 # with dataiku.WebappImpersonationContext() as ctx:
36 # # Using this context, your actions here will be impersonated.
37 # client = dataiku.api_client()
38 # user = client.get_own_user()
39 # settings = user.get_settings()
40 # return settings.get_raw().get('displayName')
41
42
43@app.callback(
44 Output('submit-val', 'n_clicks'),
45 Input('submit-val', 'n_clicks'),
46 prevent_initial_call=True
47)
48def update_output(n_clicks):
49 logger.info("Impersonation begins...")
50 with dataiku.WebappImpersonationContext() as context:
51 # Each time your need to do impersonation, you need to obtain a client.
52 # Dash cannot store objects that are not in JSON format.
53 local_client = dataiku.api_client()
54 project = local_client.get_default_project()
55 outdataset = project.get_dataset(dataset_to_build)
56 outdataset.build()
57
58 logger.info("Impersonation ends...")
59 raise PreventUpdate
1import dataiku
2
3from functools import partial
4
5from bokeh.io import curdoc
6from bokeh.models import Div, Button
7from bokeh.layouts import layout
8from dataiku.webapps.run_bokeh import get_session_headers
9
10import logging
11
12logger = logging.getLogger(__name__)
13
14dataset_to_build = "web_history_prepared"
15
16
17def build_dataset(dataset):
18 logger.info('impersonation')
19 with dataiku.WebappImpersonationContext() as ctx:
20 local_client = dataiku.api_client()
21 project = local_client.get_default_project()
22 outdataset = project.get_dataset(dataset)
23 outdataset.build()
24
25
26def application():
27 title = Div(text="""<h1>Impersonation Demo</h1>""")
28
29 # Get user form http request (can be done with user impersonation)
30 headers = get_session_headers(curdoc().session_context.id)
31 auth_info = dataiku.api_client().get_auth_info_from_browser_headers(headers)
32 user = auth_info.get('associatedDSSUser')
33 subtitle = Div(text=f"""<h2>Welcome: <span id="identified_user">{user}</span></h2>""")
34
35 button = Button(label="Build the dataset")
36 button.on_event('button_click', partial(build_dataset, dataset=dataset_to_build))
37
38 app = layout([title, subtitle, button])
39 curdoc().add_root(app)
40
41 curdoc().title = "Impersonation"
42
43
44application()
1import streamlit as st
2import pandas as pd
3import numpy as np
4import altair as alt
5import json
6
7import dataiku
8import logging
9
10logger = logging.getLogger(__name__)
11
12from streamlit.scriptrunner.script_run_context import get_script_run_ctx
13from streamlit.server.server import Server
14
15dataset_to_build = "web_history_prepared"
16
17
18def get_headers():
19 # Get headers
20 session_id = get_script_run_ctx().session_id
21 server = Server.get_current()
22 session_info = server._get_session_info(session_id)
23 if session_info.ws is None:
24 # At first page load, this is None (at least until #4099 is fixed)
25 st.markdown("Unable to get session websocket. Please refresh the page.")
26 st.stop()
27 headers = dict(session_info.ws.request.headers)
28 logger.info(headers)
29 return headers
30
31
32def build_dataset(dataset):
33 user = dataiku.api_client().get_user(auth_info.get('authIdentifier'))
34 client_as_user = user.get_client_as()
35 project = client_as_user.get_default_project()
36 outdataset = project.get_dataset(dataset_to_build)
37 outdataset.build()
38
39
40st.title('Impersonation Demo')
41request_headers = get_headers()
42auth_info = dataiku.api_client().get_auth_info_from_browser_headers(request_headers)
43st.subheader(f"Welcome: {auth_info.get('authIdentifier')}")
44
45st.button("Build the dataset", on_click=build_dataset, args=[dataset_to_build])
Wrapping Up#
Congratulations! You know how to use and implement impersonation for web applications. Permissions and impersonation are critical points for web application security.