power-bi 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e5aaadec6cec57b22f4780dd2e1f4c64c55872d49f5a17a59326fcff099844dd
4
- data.tar.gz: 1a6bf8c48a1d17333f58519c5ee23ee9a85025c63110c6cc2f2fe9243566f7ca
3
+ metadata.gz: c120eeef2ac35b02afcfff32690f003efad7fbfd59c7f2bd714a6d0659b1ed44
4
+ data.tar.gz: ee077e4fa8f6faae52ad5c1107fd96f9cb8bae5e7c8f0ef47a83009e7c80ce0d
5
5
  SHA512:
6
- metadata.gz: 570af0ad49001778d237e4ce712a8120d879cc83d53a9b4a877d2d51ee6fcc3dde4e913cc8a3c42273ce5de45d31d7922308fee03c895fb6fcbce46332b06288
7
- data.tar.gz: 760813b41e3318fc52871f3adc61f4efcf518f9333f93fcf9295c15b9e61d094518cbd3adbbf7fec0c3c7921239d2082460099fc2adffcd4b60c15067e5946e1
6
+ metadata.gz: c9c742362808fb3fa08ad663ebbdaf34d916f932fa5bed8f66d657caf769c230a7694d82cd05555a642d41098ccb9fcf6eed9b6f89233ca344ff3f1c3d02b6fb
7
+ data.tar.gz: dc28a5e89d26365b7b48d5f6538bd1e8c12a9fe45c81b4f216ae63074ced3b728f31395c98751ee571a9e7234315693af5f6a7cffe4d86c398f6ec6683cfeafe
data/README.md CHANGED
@@ -10,6 +10,60 @@ The Power BI API does not handle the authorization part. It requires the user to
10
10
  pbi = PowerBI::Tenant.new(->{token = get_token(client, token) ; token.token})
11
11
  ```
12
12
 
13
+ ## Authentication & authorization towards the Power BI API
14
+
15
+ Currently (april 2024), there are basically 3 ways to authenticate to the Power BI API.
16
+
17
+ ### 1 - Master User
18
+
19
+ A master user is a classic Microsoft 365 user that you assign a Power BI Pro license. In order to allow the user to execute actions on the Power BI API you need to create an app registration in Azure AD. In the associated Enterprise application (gets created when you create the app registration), you need to add the permissions to use the Power BI service.
20
+
21
+ The resulting authentication looks like this:
22
+
23
+ ```
24
+ TENANT = "53c835d6-6841-4d58-948a-55117409e1d8"
25
+ CLIENT_ID = '6fc64675-bee3-49a7-70d8-b3301a51a88d'
26
+ CLIENT_SECRET = 'sI/@ncYe=eVt7.XfZ7tsPU1aPbxm0V_H'
27
+ USERNAME = 'company_0001@example.com'
28
+ PASSWORD = 'mLXv5A1jrIb8dHopur7y'
29
+
30
+ client = OAuth2::Client.new(CLIENT_ID, CLIENT_SECRET, site: 'https://login.microsoftonline.com', token_url: "#{TENANT}/oauth2/token")
31
+
32
+ token = client.password.get_token(USERNAME, PASSWORD, scope: 'openid', resource: 'https://analysis.windows.net/powerbi/api')
33
+ ```
34
+
35
+ Note that in this case the legacy (and slightly unsafe) Resource Owner Password Credentials (ROPC) OAuth flow is used.
36
+
37
+ ### 2 - Service principal
38
+
39
+ A service principal is a fancy word for a machine user with a secret (eg. id + key) in Azure AD. You create them by creating an app registration and adding a secret on it. You need to allow service principals in your Power BI admin settings. But once you allow that, the setup is very easy. No need to configure anything special in AD.
40
+
41
+ The resulting authentication looks like this:
42
+
43
+ ```
44
+ TENANT = "53c835d6-6841-4d58-948a-55117409e1d8"
45
+ CLIENT_ID = '6fc64675-bee3-49a7-70d8-b3301a51a88d'
46
+ CLIENT_SECRET = 'sI/@ncYe=eVt7.XfZ7tsPU1aPbxm0V_H'
47
+
48
+ client = OAuth2::Client.new(CLIENT_ID, CLIENT_SECRET, site: 'https://login.microsoftonline.com', token_url: "#{TENANT}/oauth2/token")
49
+
50
+ token = client.client_credentials.get_token(resource: 'https://analysis.windows.net/powerbi/api')
51
+ ```
52
+
53
+ Note that in this case the Client Credentials OAuth flow is used.
54
+
55
+ ### 3 - Service principal profiles
56
+
57
+ Service principal profiles is a Power-BI-only concept. Towards AD, it looks exactly the same as generic service principal. Hence the authenication looks exactly as in way 2.
58
+
59
+ Once authenticated, you can set profiles like this:
60
+
61
+ ```
62
+ pbi.profile = profile
63
+ ```
64
+
65
+ Every action executed after setting the profile, will be executed _through the eyes of the profile_. This way, you can create an isolated multi-tenant setup. Using profiles, simplifies the internal organization of Power BI and allows faster interaction with Power BI. This also lifts the 1000-workspaces limit that is imposed on Master Userss and Service Principals
66
+
13
67
  # Supported endpoints
14
68
 
15
69
  Note: where possible we use _lazy evaluation_: we only call the REST API endpoint when really needed. For examples `pbi.workspaces` won't trigger a call, while `pbi.workspaces.count` will trigger a call. And `pbi.workspace('123')` won't trigger a call, while `pbi.workspace('123').name` will trigger a call.
@@ -75,6 +129,13 @@ Note: Capacities are Azure creatures, you can't create them in Power BI.
75
129
  * List capacities: `pbi.capacities`
76
130
  * Get a capacity: `pbi.capacity(id)`
77
131
 
132
+ ## Profiles
133
+
134
+ * List profiles: `pbi.profiles`
135
+ * Get a profile: `pbi.profile(id)`
136
+ * Create a profile: `pbi.profiles.create`
137
+ * Delete a profile: `profile.delete`
138
+
78
139
  # Note about gateway credentials
79
140
 
80
141
  Power BI uses an obscure mechanism to encrypt credential exchange between the service and the gateway. The encryption must be done outside this module on a Windows machine based on th public key of the gateway. This is an example C# script:
@@ -0,0 +1,44 @@
1
+ module PowerBI
2
+ class Profile < Object
3
+
4
+ def initialize(tenant, parent, id = nil)
5
+ super(tenant, id)
6
+ end
7
+
8
+ def get_data(id)
9
+ @tenant.get("/profiles/#{id}")
10
+ end
11
+
12
+ def data_to_attributes(data)
13
+ {
14
+ id: data[:id],
15
+ display_name: data[:displayName],
16
+ }
17
+ end
18
+
19
+ def delete
20
+ @tenant.delete("/profiles/#{@id}")
21
+ @tenant.profiles.reload
22
+ true
23
+ end
24
+
25
+ end
26
+
27
+ class ProfileArray < Array
28
+ def self.get_class
29
+ Profile
30
+ end
31
+
32
+ def create(name)
33
+ data = @tenant.post("/profiles") do |req|
34
+ req.body = {displayName: name}.to_json
35
+ end
36
+ self.reload
37
+ Profile.instantiate_from_data(@tenant, nil, data)
38
+ end
39
+
40
+ def get_data
41
+ @tenant.get("/profiles")[:value]
42
+ end
43
+ end
44
+ end
@@ -1,13 +1,15 @@
1
1
  module PowerBI
2
2
  class Tenant
3
- attr_reader :workspaces, :gateways, :capacities
3
+ attr_reader :workspaces, :gateways, :capacities, :profiles, :profile
4
4
 
5
5
  def initialize(token_generator, retries: 5, logger: nil)
6
6
  @token_generator = token_generator
7
7
  @workspaces = WorkspaceArray.new(self)
8
8
  @gateways = GatewayArray.new(self)
9
9
  @capacities = CapacityArray.new(self)
10
+ @profiles = ProfileArray.new(self)
10
11
  @logger = logger
12
+ @profile = nil
11
13
 
12
14
  ## WHY RETRIES? ##
13
15
  # It is noticed that once in a while (~0.1% API calls), the Power BI server returns a 500 (internal server error) without apparent reason, just retrying works :-)
@@ -42,6 +44,11 @@ module PowerBI
42
44
  Capacity.new(self, nil, id)
43
45
  end
44
46
 
47
+ def profile=(profile)
48
+ @profile = profile
49
+ @workspaces.reload # we need to reload the workspaces because we look through the eyes of the profile
50
+ end
51
+
45
52
  def get(url, params = {})
46
53
  t0 = Time.now
47
54
  conn = Faraday.new do |f|
@@ -51,6 +58,9 @@ module PowerBI
51
58
  req.params = params
52
59
  req.headers['Accept'] = 'application/json'
53
60
  req.headers['authorization'] = "Bearer #{token}"
61
+ if @profile
62
+ req.headers['X-PowerBI-Profile-Id'] = @profile.id
63
+ end
54
64
  yield req if block_given?
55
65
  end
56
66
  if response.status == 400
@@ -73,6 +83,9 @@ module PowerBI
73
83
  response = conn.get(PowerBI::BASE_URL + url) do |req|
74
84
  req.params = params
75
85
  req.headers['authorization'] = "Bearer #{token}"
86
+ if @profile
87
+ req.headers['X-PowerBI-Profile-Id'] = @profile.id
88
+ end
76
89
  yield req if block_given?
77
90
  end
78
91
  log "Calling (GET - raw) #{response.env.url.to_s} - took #{((Time.now - t0) * 1000).to_i} ms"
@@ -92,6 +105,9 @@ module PowerBI
92
105
  req.headers['Accept'] = 'application/json'
93
106
  req.headers['Content-Type'] = 'application/json'
94
107
  req.headers['authorization'] = "Bearer #{token}"
108
+ if @profile
109
+ req.headers['X-PowerBI-Profile-Id'] = @profile.id
110
+ end
95
111
  yield req if block_given?
96
112
  end
97
113
  log "Calling (POST) #{response.env.url.to_s} - took #{((Time.now - t0) * 1000).to_i} ms"
@@ -113,6 +129,9 @@ module PowerBI
113
129
  req.headers['Accept'] = 'application/json'
114
130
  req.headers['Content-Type'] = 'application/json'
115
131
  req.headers['authorization'] = "Bearer #{token}"
132
+ if @profile
133
+ req.headers['X-PowerBI-Profile-Id'] = @profile.id
134
+ end
116
135
  yield req if block_given?
117
136
  end
118
137
  log "Calling (PATCH) #{response.env.url.to_s} - took #{((Time.now - t0) * 1000).to_i} ms"
@@ -133,6 +152,9 @@ module PowerBI
133
152
  req.params = params
134
153
  req.headers['Accept'] = 'application/json'
135
154
  req.headers['authorization'] = "Bearer #{token}"
155
+ if @profile
156
+ req.headers['X-PowerBI-Profile-Id'] = @profile.id
157
+ end
136
158
  yield req if block_given?
137
159
  end
138
160
  log "Calling (DELETE) #{response.env.url.to_s} - took #{((Time.now - t0) * 1000).to_i} ms"
@@ -158,6 +180,9 @@ module PowerBI
158
180
  req.headers['Accept'] = 'application/json'
159
181
  req.headers['Content-Type'] = 'multipart/form-data'
160
182
  req.headers['authorization'] = "Bearer #{token}"
183
+ if @profile
184
+ req.headers['X-PowerBI-Profile-Id'] = @profile.id
185
+ end
161
186
  req.body = {value: Faraday::UploadIO.new(file, 'application/octet-stream')}
162
187
  req.options.timeout = 120 # default is 60 seconds Net::ReadTimeout
163
188
  end
data/lib/power-bi.rb CHANGED
@@ -24,4 +24,5 @@ require_relative "power-bi/gateway"
24
24
  require_relative "power-bi/gateway_datasource"
25
25
  require_relative "power-bi/page"
26
26
  require_relative "power-bi/user"
27
- require_relative "power-bi/capacity"
27
+ require_relative "power-bi/capacity"
28
+ require_relative "power-bi/profile"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: power-bi
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lode Cools
@@ -98,6 +98,7 @@ files:
98
98
  - lib/power-bi/object.rb
99
99
  - lib/power-bi/page.rb
100
100
  - lib/power-bi/parameter.rb
101
+ - lib/power-bi/profile.rb
101
102
  - lib/power-bi/refresh.rb
102
103
  - lib/power-bi/report.rb
103
104
  - lib/power-bi/tenant.rb