Power BI Embedded, Generate Embed Token only with View Access - powerbi

I have embedded the power bi report using service principal as given here.
I'm generating the embed token as below,
var generateTokenRequestParameters = new GenerateTokenRequest(accessLevel:"View",allowSaveAs:false,identities: new EffectiveIdentity[] { new EffectiveIdentity(username: serviceAccount, roles: new string[] { "Viewer" }, datasets: new string[] { report.DatasetId }) });
EmbedToken embedToken = client.Reports.GenerateTokenInGroup(groupId, reportId, generateTokenRequestParameters);
But when I embed the report using javascript sdk, if I configure the permission as below the user able to edit the reports. How can prevent the user from editing the report in the embed token?
permissions: models.Permissions.ReadWrite,
viewMode: models.ViewMode.Edit,

I have a C# MVC Web Application which embeds PowerBI reports.
Here is our embed config
var config = {
type: 'report',
id: embedReportId,
accessToken: accessToken,
tokenType: models.TokenType.Embed,
embedUrl: embedUrl,
permissions: models.Permissions.View,
settings: {
filterPaneEnabled: false,
navContentPaneEnabled: true,
background: models.BackgroundType.Transparent,
layoutType: models.LayoutType.Custom,
customLayout: {
displayOption: models.DisplayOption.FitToWidth
}
}
};
You mentioned the viewMode property in your question. As you can see we do not even set the viewMode property in our config. We only set the permissions property but as far as I can tell it doesn't do much. This property is rarely used or only controls minor things you can see on the UI.
If you generate the access token like you are for "View" and someone edits the embedding report viewer page to change the permission property from models.Permissions.View to models.Permissions.ReadWrite in the HTML/javascript any subequent calls the PowerBI JavaScript library attempts to make after that report viewer page edit would fail because the accesss token that was generated is for View and not Edit and the API calls to Microsoft/PowerBI would fail.
So you really just need to make sure your back end logic is generating the correct access token for whatever the viewing context is. If you were generating an access token with full permissions and using the PowerBI javascript library to hide things then users could potentially alter your page source and do more than you wanted.
Good luck

Related

How to bind multiple Power BI datasets to a single Power BI Report

I have a user name logging into the Web Application.
I am using HTML, CSS, JavaScript and ASP.NET webform to run the Web Application with Power BI embedded reports.
I am trying to code to pass Server Name and Database name based on the user logged in to Power BI embedded config.
How to pass server name and database name to Power BI Embedded config?
Tried to test the application flow with Import Data Sets using following things, but the Power BI Report is not getting loaded in HTML div element. Its giving "Power BI Loading logo" in the embed div and then showing message as "Power BI content is not available"
Application Flow:
Power BI workspace has One Report and Two Datasets
Following is the Token generation code in .NET
// Generate an embed token and populate embed variables
using (var client = new PowerBIClient(new Uri(Configurations.ApiUrl), Authentication.GetTokenCredentials()))
//using (var client = new PowerBIClient(new Uri(Configurations.ApiUrl), Authentication.m_tokenCredentials))
{
var report = client.Reports.GetReportInGroup(new Guid(Configurations.WorkspaceId), new Guid(ddlReport.SelectedValue));
var rls = new EffectiveIdentity(username: appLoginUserName, new List<string> { userDatasetId.ToString() });
// Effective Identity
var rolesList = new List<string>();
rolesList.Add("Tenant");
rls.Roles = rolesList;
// Create list of datasets
var v2DatasetID = new List<Guid>();
v2DatasetID.Add(userDatasetId);
// Create list of Effective Identities
var v2rls = new List<EffectiveIdentity>();
v2rls.Add(rls);
// Create a request for getting Embed token
// This method works only with new Power BI V2 workspace experience
var tokenRequest = new GenerateTokenRequestV2(
reports: new List<GenerateTokenRequestV2Report>() { new GenerateTokenRequestV2Report(report.Id) },
datasets: v2DatasetID.Select(datasetId => new GenerateTokenRequestV2Dataset(datasetId.ToString())).ToList(),
identities: v2rls,
targetWorkspaces: null
);
// Generate Embed token
var getToken = client.EmbedToken.GenerateToken(tokenRequest);
// Populate embed variables (to be passed client-side)
//embedToken = tokenResponse.Token;
embedToken = getToken.ToString();
embedUrl = report.EmbedUrl;
reportId = report.Id;
}
Following is the Power BI embed configuration with dynamic dataset:
<script>
// Read embed token
var embedToken = "<% =this.embedToken %>";
// Read embed URL
var embedUrl = "<% = this.embedUrl %>";
// Read report Id
var reportId = "<% = this.reportId %>";
// Read dataset Id
var userDatasetId = "<% = this.userDatasetId %>";
// Get models (models contains enums)
var models = window['powerbi-client'].models;
// Embed configuration is used to describe what and how to embed
// This object is used when calling powerbi.embed
// It can also includes settings and options such as filters
var config = {
type: 'report',
tokenType: models.TokenType.Embed,
accessToken: embedToken,
embedUrl: embedUrl,
id: reportId,
datasetId: userDatasetId, // The dataset id that you want the report to use
settings: {
filterPaneEnabled: true,
navContentPaneEnabled: true,
extensions: [
{
command: {
name: "cmdShowValue",
title: "Show Value in MessageBox",
selector: {
$schema: "http://powerbi.com/product/schema#visualSelector",
visualName: "VisualContainer7" // Sales and Avg Price by Month visual
},
extend: {
visualContextMenu: {
title: "Show Value in MessageBox"
}
}
}
}
]
}
};
// Embed the report within the div element
var report = powerbi.embed(embedDiv, config);
</script>
This is not possible. These are report wide settings and you are asking how two different users, which are viewing the same report simultaneously, to connect to a different data sources. You must either deploy as many report are needed and choose which one to embed, or build one report using all the data sources, and then filter the data based on the currently logged in used.
Otherwise, the direct answer to your question is to rebind the report or to change the datasource of the dataset.

What do I place as the report id in the PowerBIEmbed when creating a new report?

I'm trying to use the PowerBIEmbed React component to create and design a new report. Everything works great when I edit an existing report, but when I'm creating a new one (using an embed token generated with TokenAccessLevel.Create), I'm getting the following error:
Report id is required, but it was not found. You must provide an id either as part of embed configuration or as attribute 'powerbi-report-id'.
The id I'm passing in via the configuration is the empty GUID ("00000000-0000-0000-0000-000000000000"). I have also tried deleting that property from the configuration.
I have the permissions set to models.Permissions.All (which includes models.Permissions.Create), so that isn't the issue.
Client code
const config = {
permissions: models.Permissions.All,
tokenType: models.TokenType.Embed,
type: 'report',
embedURL: generatedURL,
accessToken: generatedToken,
viewMode: models.ViewMode.Edit,
};
return (
<PowerBIEmbed
embedConfig={config}
getEmbeddedComponent={report => this.setState({ report })}
/>
);
Server Code
var authToken = await PowerBIAuthentication.DoAuthentication(_Config);
using var client = new PowerBIClient(new Uri(_Config.ApiUrl), authToken);
var dataSets = await client.Datasets.GetDatasetsInGroupAsync(_Config.WorkspaceId, cancellationToken);
var dataSet = dataSets.Value.First(x => x.Name == "AppProtoModel");
var embedTokenParameters = new GenerateTokenRequest(TokenAccessLevel.Create, dataSet.Id);
var embedToken = await client.Reports.GenerateTokenForCreateInGroupAsync(
_Config.WorkspaceId,
embedTokenParameters,
cancellationToken: cancellationToken);
myDoc.PowerBISettings.EmbedToken = embedToken;
myDoc.PowerBISettings.EmbedUrl = dataSet.CreateReportEmbedURL;
return myDoc;
Currently, PowerBIEmbed component from powerbi-client-react library does not support create mode embedding for Power BI Report and, it can be achieved using Power BI JS SDK.
Refer below code snippets:
const embedConfiguration: IEmbedConfiguration = {
permissions: models.Permissions.All,
tokenType: models.TokenType.Embed,
type: "report",
embedUrl: createReportEmbedURL,
accessToken: createEmbedToken,
viewMode: models.ViewMode.Edit,
datasetId: datasetId,
};
const report = powerbi.createReport(reportContainer, embedConfiguration);
createReportEmbedURL mentioned in above snippet can be generated using Datasets - Get Dataset API.
createEmbedToken mentioned in above snippet can be generated using Embed Token - Report GenerateTokenForCreateInGroup API.
Note: The datasetId passed in the configuration should be the same which is used to generate createReportEmbedURL.
Refer following docs for more information:
Create and save embedded report

powerbi global object not found in typescript

I am trying to use this power bi below code where powerbi object not found error is getting in my typescript code:
// Read embed application token from textbox
var txtAccessToken = $('#txtAccessToken').val();
// Read embed URL from textbox
var txtEmbedUrl = $('#txtReportEmbed').val();
// Read report Id from textbox
var txtEmbedReportId = $('#txtEmbedReportId').val();
// Read embed type from radio
var tokenType = $('input:radio[name=tokenType]:checked').val();
// Get models. models contains enums that can be used.
var models = window['powerbi-client'].models;
// We give All permissions to demonstrate switching between View and Edit mode and saving report.
var permissions = models.Permissions.All;
// Embed configuration used to describe the what and how to embed.
// This object is used when calling powerbi.embed.
// This also includes settings and options such as filters.
// You can find more information at https://github.com/Microsoft/PowerBI-JavaScript/wiki/Embed-Configuration-Details.
var config= {
type: 'report',
tokenType: tokenType == '0' ? models.TokenType.Aad : models.TokenType.Embed,
accessToken: txtAccessToken,
embedUrl: txtEmbedUrl,
id: txtEmbedReportId,
permissions: permissions,
settings: {
filterPaneEnabled: true,
navContentPaneEnabled: true
}
};
// Get a reference to the embedded report HTML element
var embedContainer = $('#embedContainer')[0];
// Embed the report and display it within the div container.
var report = powerbi.embed(embedContainer, config);
// Report.off removes a given event handler if it exists.
report.off("loaded");
// Report.on will add an event handler which prints to Log window.
report.on("loaded", function() {
Log.logText("Loaded");
});
report.on("error", function(event) {
Log.log(event.detail);
report.off("error");
});
report.off("saved");
report.on("saved", function(event) {
Log.log(event.detail);
if(event.detail.saveAs) {
Log.logText('In order to interact with the new report, create a new token and load the new report');
}
});
in the above code the powerbi object shows not found in my typescript code: powerbi.embed(embedContainer, config);
I tried to use window['powerbi'] or window.powerbi but doesn't work. What should be the solution then?
I faced a similar issue a few weeks back (probably exactly the same). For me it seems that what works is using window.powerbi.embed() for the embed action, whereas the import import * as powerbi from "powerbi-client"; is used for all other Power BI objects.
I had the same problem, found this question through a google search. I wasn't able to figure out why it wasn't on the window, but as a work around you can initialize it yourself like this:
import * as pbi from "powerbi-client";
const powerbi = new pbi.service.Service(
pbi.factories.hpmFactory,
pbi.factories.wpmpFactory,
pbi.factories.routerFactory
);
const container = document.getElementById("report-container");
powerbi.embed(container, embedConfiguration);

Switch between mobile and desktop view in power bi embedded

I have a power bi report with both desktop and mobile views. I'd like the browser to switch between these views as it resizes. The only way I can currently achieve this is to embed two instances of the report into the browser, one mobile the other desktop, and hide and show them depending on the browser size.
The problem with this is that if I set some filter values when in the desktop view then, narrow the browser so that the mobile view is shown, then the filter values are not same, this obviously being because there are in reality two separate reports.
The other downside of this approach is that I am presumably also incurring the performance cost on my database of generating two reports.
What can I do to only embed a single report that can dynamically switch between mobile and desktop views?
UPDATE Following response below, test code to toggle layout between mobile and custom layout
angular.element($window).on('resize', function () {
if (vm.report === null)
return;
var models = window['powerbi-client'].models;
var newLayout = models.LayoutType.Custom;
if (window.innerWidth < 768) {
newLayout = models.LayoutType.MobilePortrait;
}
if (vm.report.config.settings.layoutType !== newLayout) {
const newSettings = { layoutType: newLayout };
vm.report.updateSettings(newSettings);
}}
UPDATE 2, Added code to show how the report is generated
// report config
var models = window['powerbi-client'].models;
var config = {
type: 'report',
tokenType: models.TokenType.Embed,
accessToken: result.accessToken,
embedUrl: result.embedUrl,
id: result.reportId,
permissions: models.Permissions.View,
viewMode: models.ViewMode.Read,
settings: {
filterPaneEnabled: false,
navContentPaneEnabled: false,
background: models.BackgroundType.Transparent,
layoutType: models.LayoutType.Custom,
customLayout: {
displayOption: models.DisplayOption.FitToPage
}
}
};
// get elements and embed them
var desktopReportContainer = $('.reportContainer')[0];
vm.report = powerbi.embed(desktopReportContainer, config);
Instead of embedding two instances of a report you can do:
Change the layout type by updating settings like here: change-layout-example.
The downside of this approach is that your user's cross filters will not be saved when changing layout.
Before changing the layout type, save a bookmark and then after changing the layout type apply the saved bookmark:
function changeLayout(layoutType) {
report.bookmarksManager.capture()
.then(function (capturedBookmark) {
var bookmarkState = capturedBookmark.state;
var config = {
layoutType: layoutType
};
report.updateSettings(config).then(function () {
report.bookmarksManager.applyState(bookmarkState);
})
})
}
Please note that you will have to add error handling code to the sample above.
Use Custom layout instead of mobile layout like here: Dynamic report layout.
The downside of this approach is that you will have to write code that sets the layout dynamically.
Power BI embed Javascript library has direct support for your case.
First you will need to create a report with mobile layout using Power BI desktop. After you created the report you can embed it using JavaScript SDK. In order to decide in which layout to embed, use the layoutType property of settings in embed configuration.
There are two layout types dedicated to mobile devices:
MobilePortrait - Optimized for portrait view (This is the mobile
layout you created on Power BI desktop)
MobileLandscape - Optimized
for landscape view. This layout looks like the regular report layout.
Load a report in mobile layout Example:
// Get models. models contains enums that can be used.
var models = window['powerbi-client'].models;
var embedConfiguration = {
type: 'report',
id: '5dac7a4a-4452-46b3-99f6-a25915e0fe55',
embedUrl: 'https://app.powerbi.com/reportEmbed',
tokenType: models.TokenType.Embed,
accessToken: 'h4...rf',
settings: {
layoutType: models.LayoutType.MobilePortrait
}
};
Here is the detail guide: https://github.com/Microsoft/PowerBI-JavaScript/wiki/Embed-For-Mobile

Access Liferay Web services using JSON API

Hi i created a plugin portlet. In the JSP i am accessing all countries list by using JSON API. It is working fine for Logged in users. But for the Guest users i am unable to access the web service. I am working on Liferay 6.0.6. The following is my code.
Liferay.Service.Portal.Country.getCountries(
{},
function(result) {
for(var count=0;count< result.length;count++){
alert(result[count].name);
var option = document.createElement("option");
}
}
);
Assuming that you are using Liferay 6.1, you can achieve it by adding a property to portal-ext.properties file
json.service.public.methods=getCountries
If you need to check the whole flow checkout
JSONServiceAction
I think you need to pass the serviceContext with permissions to the Service.
Can you try by setting the communityPermissions and guestPermissions as VIEW ?
Liferay.Service.Portal.Country.getCountries(
{
serviceContext: jQuery.toJSON(
{
communityPermissions: communityPermission,
guestPermissions: guestPermission,
scopeGroupId: themeDisplay.getScopeGroupId()
}
)
},
function(result) {
for(var count=0;count< result.length;count++){
alert(result[count].name);
var option = document.createElement("option");
}
}
);
I found a work around for the above problem. I am unable to access JSON API because Liferay is using A.io.request for AJAX Calls which is available for Logged in Users only. So I have prepared the following code.
jQuery.ajax({
type: "POST",
url: '<%=themeDisplay.getURLPortal() %>'+'/tunnel-web/json?serviceClassName=com.liferay.portal.service.CountryServiceUtil&serviceMethodName=getCountries',
dataType: 'json',
success: function(countriesList) {
alert(countriesList);
alert(countriesList[0].countryId);
}
}
});