This is an automated email from the ASF dual-hosted git repository.
pan3793 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git
The following commit(s) were added to refs/heads/master by this push:
new e887d7c414 [ZEPPELIN-6528] Handle forbidden interpreter responses in
classic UI
e887d7c414 is described below
commit e887d7c41403d4dcd16114aa361f2097058e7bcf
Author: Jongyoul Lee <[email protected]>
AuthorDate: Wed Aug 12 16:10:01 2026 +0900
[ZEPPELIN-6528] Handle forbidden interpreter responses in classic UI
### What is this PR for?
Apache Shiro 2.0.6 returns HTTP 403 for an authenticated user who lacks a
required role. Shiro 1.13.0 returned HTTP 401 for the same authorization denial.
The classic interpreter page only handled HTTP 401, so a 403 response did
not show the permission error toast or redirect the user. This also caused
`AuthenticationIT.testAnyOfRolesUser` to fail with `Expected ngToast not found`.
This PR handles both 401 and 403 responses and adds controller tests
covering both statuses.
### What type of PR is it?
Bug Fix
### Todos
* [x] Handle HTTP 403 authorization denials in the classic interpreter page
* [x] Preserve the existing HTTP 401 behavior
* [x] Add regression tests for both response statuses
### What is the Jira issue?
* https://issues.apache.org/jira/browse/ZEPPELIN-6528
### How should this be tested?
```bash
cd zeppelin-web
npm run karma-test -- --single-run
```
Local validation:
* ESLint passed for the changed controller and test.
* `git diff --check` passed.
* Karma compiled the application and test bundle successfully, but the
browser run could not start locally because Firefox is not installed in the
worktree environment.
### Screenshots
N/A
### Questions
* Does the license files need to update? No
* Is there breaking changes for older versions? No
* Does this need documentation? No
Closes #5416 from jongyoul/codex/ZEPPELIN-6528-handle-shiro-403.
Signed-off-by: Cheng Pan <[email protected]>
---
.../src/app/interpreter/interpreter.controller.js | 2 +-
.../app/interpreter/interpreter.controller.test.js | 68 ++++++++++++++++++++++
2 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/zeppelin-web/src/app/interpreter/interpreter.controller.js
b/zeppelin-web/src/app/interpreter/interpreter.controller.js
index dddae0022c..63db4d1525 100644
--- a/zeppelin-web/src/app/interpreter/interpreter.controller.js
+++ b/zeppelin-web/src/app/interpreter/interpreter.controller.js
@@ -114,7 +114,7 @@ function InterpreterCtrl($rootScope, $scope, $http,
baseUrlSrv, ngToast, $timeou
$scope.interpreterSettings = res.data.body;
checkDownloadingDependencies();
}).catch(function(res) {
- if (res.status === 401) {
+ if (res.status === 401 || res.status === 403) {
ngToast.danger({
content: 'You don\'t have permission on this page',
verticalPosition: 'bottom',
diff --git a/zeppelin-web/src/app/interpreter/interpreter.controller.test.js
b/zeppelin-web/src/app/interpreter/interpreter.controller.test.js
new file mode 100644
index 0000000000..0ceb6afda3
--- /dev/null
+++ b/zeppelin-web/src/app/interpreter/interpreter.controller.test.js
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+describe('Controller: Interpreter', function() {
+ beforeEach(angular.mock.module('zeppelinWebApp'));
+
+ const baseUrlSrvMock = {
+ getBase: () => '/',
+ getRestApiBase: () => '',
+ };
+
+ let $controller;
+ let $httpBackend;
+ let $rootScope;
+ let ngToast;
+
+ beforeEach(inject((_$controller_, _$httpBackend_, _$rootScope_, _ngToast_)
=> {
+ $controller = _$controller_;
+ $httpBackend = _$httpBackend_;
+ $rootScope = _$rootScope_;
+ ngToast = _ngToast_;
+ }));
+
+ afterEach(function() {
+ $httpBackend.verifyNoOutstandingExpectation();
+ $httpBackend.verifyNoOutstandingRequest();
+ });
+
+ [401, 403].forEach((status) => {
+ it(`should display an authorization error for HTTP ${status}`, function() {
+ spyOn(ngToast, 'danger');
+ spyOn(window, 'setTimeout');
+
+ $httpBackend.expectGET('/interpreter/property/types').respond(200,
{body: []});
+ $httpBackend.expectGET('/interpreter/setting').respond(status, {});
+ $httpBackend.expectGET('/interpreter').respond(200, {body: []});
+ $httpBackend.expectGET('/interpreter/repository').respond(200, {body:
[]});
+
+ $controller('InterpreterCtrl', {
+ $scope: $rootScope.$new(),
+ baseUrlSrv: baseUrlSrvMock,
+ $route: {current: {$$route: {originalPath: '/interpreter'}}},
+ });
+ $httpBackend.flush();
+
+ expect(ngToast.danger).toHaveBeenCalledWith({
+ content: 'You don\'t have permission on this page',
+ verticalPosition: 'bottom',
+ timeout: '3000',
+ });
+ expect(window.setTimeout).toHaveBeenCalledWith(jasmine.any(Function),
3000);
+ });
+ });
+});