-
Notifications
You must be signed in to change notification settings - Fork 231
Fix wrong 'archiveLink' for operations user #3060
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix wrong 'archiveLink' for operations user #3060
Conversation
app/scripts/directives/logViewer.js
Outdated
|
||
var canI = $filter('canI'); | ||
var podsLogVersion = APIService.getPreferredVersion('pods/log'); | ||
var canViewOperationsLogs = canI(podsLogVersion, 'view'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is checking that you can view the log you're already viewing... @jcantrill Is this really what we want?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This specific issue is that we changed the available index patterns if you are an operations user. You used to get a list of every project in the cluster which was exceedingly long and we modified the seeding to provide only one which matched them all with 'project.*'. The complementary change was not captured in the web console. Operations users are being redirected to Kibana to use an index-pattern which does not exist.
Here we are trying to determine if the use is an operations user by using the same SAR we use in the multi-tenant plugin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we are trying to determine if the use is an operations user by using the same SAR we use in the multi-tenant plugin.
What SAR specifically do you have?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only operations users should be able to view pods/log from the logging project, so:
oc auth can-i view pods/log -n openshift-logging
app/scripts/services/logLinks.js
Outdated
var archiveUri = function(opts, prefix) { | ||
var archiveUri = function(opts, prefix, canViewOperationsLogs) { | ||
if(canViewOperationsLogs) { | ||
if(!prefix || (prefix && prefix.startsWith('project.'))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two lines can be collapsed into a single if
condition
app/scripts/directives/logViewer.js
Outdated
|
||
var canI = $filter('canI'); | ||
var podsLogVersion = APIService.getPreferredVersion('pods/log'); | ||
var canViewOperationsLogs = canI(podsLogVersion, 'view'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spadgett This function is confusing me - the third parameter, project
, makes the function behave in a way that I don't understand.
When I run the following code while browsing any project other than openshift-logging
, logged in as cluster-admin
canI(podsLogVersion, 'view', 'openshift-logging'');
the function evaluates to false
. But when I browse pods/log from openshift-logging
project, the function evaluates to true
. I'm trying to write something like:
oc adm policy who-can view pods/log -n openshift-logging
and weirdly enough, I get that functionality when I don't specify the project in the function call.
I have to take in consideration that the user is either operations user (who can view project & operations logs) or a regular user.
Any idea how to translate the above oc adm command to a API call? Thank you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you need to check a specific permission in another project, you should use SelfSubjectAccessReview
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what the canI
filter does under the hood, isn't it?
I'm trying to use its projectName
argument, I assume that
AuthorizationService.canI(resource, verb, projectName)
will issue a request against the Authorization API and SSAR is used on the server, or not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that's not what it does... You should run a separate SelfSubjectAccessReview request to check access in another project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some support for it in the code base?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, but it should be pretty easy to call DataService.create with a SelfSubjectAccessReview object:
https://kubernetes.io/docs/reference/access-authn-authz/authorization/#checking-api-access
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the hint I was hoping for, thanks a lot!
app/scripts/directives/logViewer.js
Outdated
containername: $scope.options.container, | ||
backlink: URI.encode($window.location.href) | ||
}, $filter('annotation')($scope.context.project,'loggingDataPrefix'))) | ||
// var ssarVersion = APIService.getPreferredVersion('selfsubjectaccessreviews'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to add SSAR to preferredVersions but somehow this change didn't take effect, even after grunt build.
@spadgett can you advice how to add it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't worry about adding a preferred version. You'd need to update the origin-web-common repo and cut a release. I don't think it's worth it here.
app/scripts/directives/logViewer.js
Outdated
return data.status.allowed; | ||
}, function() { | ||
return false; | ||
}).then(function(canViewOperationsLogs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spadgett @jcantrill I'm struggling with the async nature of promise
s. As this function is not called externally (click on a button etc), at this point log view is blocked. Can you help me how to make sure this promise gets evaluated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had an issue with my VM, all log files disappeared from the file system. That's why I was seeing weird behavior in the console. The code should be fine.
app/scripts/services/logLinks.js
Outdated
} | ||
prefix = prefix || 'project.' + opts.namespace + '.' + opts.namespaceUid; | ||
opts.index = prefix + '.*'; | ||
console.log('link: ' + opts.index); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will delete once the fix is ready.
app/scripts/directives/logViewer.js
Outdated
} | ||
} | ||
}; | ||
var promise = DataService.create('selfsubjectaccessreviews', null, ssar, {namespace: 'openshift-logging'}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure the API version in the request is the same as the endpoint you're calling. I believe it would be
DataService.create({ group: 'authorization.k8s.io', version: 'v1', resource: 'selfsubjectaccessreviews'}, ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be caching this response somewhere so we only check it once per session. You probably want to move this into an Angular service.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if I remove role from a user? That could be a possible danger it that user is logged in a session and has dirty cash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disregard, we'll do another round of auth on our side.
/cc @jcantrill Jeff should review as well |
Depends on openshift/origin-web-common#342 |
@jcantrill there are still a few issues that need to be resolved
|
If you guys have switched to the OAuth proxy, I don't think we should be sending the token from the console at all anymore, correct? cc @jwforres |
the original discussion was the switch to using the oauth proxy was supposed to be transparent to console users and that the existing token mechanism was going to behave the same the discussion about changing the behavior to stop passing token had been a converged console discussion |
@jwforres @spadgett I incorrectly was under the impression the oauth-proxy was able to function in the same manor as the original proxy. Pending investigation from @josefkarasek about if it its possible to function in the previous capacity, we may need to revert the proxy being used in order to not have a functional regression. We discussed within the logging team regarding the 4.0 release not having a link from console to kibana which means this work would be uneeded. It means however we need to continue to release the old proxy which I was hoping to avoid |
6002f80
to
71ccb32
Compare
@jcantrill @spadgett please let me know what you think. I will then update the PR so we can finish this. Thank you |
@josefkarasek @spadgett this #3067 will resolve the form post/get issue |
71ccb32
to
3d5fa60
Compare
b1ab31e
to
b2e3cdd
Compare
app/scripts/directives/logViewer.js
Outdated
DataService, | ||
HTMLService, | ||
ModalsService, | ||
AggregatedLoggingService, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not defined in this repo. Please move to this repo per @spadgett request
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's in vendor.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
app/scripts/directives/logViewer.js
Outdated
}, $filter('annotation')($scope.context.project,'loggingDataPrefix'))) | ||
|
||
var currentUser = AuthService.UserStore().getUser().metadata.name; | ||
AggregatedLoggingService.isOperationsUser(currentUser).then(function(canViewOperationsLogs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this needs to be encapsulated this way but I defer to @spadgett. You should be able to simply call:
AggregatedLoggingService.isOperationsUser(currentUser)
and assign it to a var. and then pass it in as a param to archiveURI
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has to be a promise since it makes a selfsubjectaccessreview request to the server, which is asynchronous
app/scripts/directives/logViewer.js
Outdated
'DataService', | ||
'HTMLService', | ||
'ModalsService', | ||
'AggregatedLoggingService', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We try to keep these alphabetized
// Create SelfSubjectAccessReview request againt authorization API to check whether | ||
// current user can view pods/log from 'openshift-logging' project. | ||
// Users who can view such logs are 'operations' users | ||
var isOperationsUser = function(userName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not need to take in a user name since it uses self subject access review. (The cached data will be cleared on logout because the page is reloaded, so that's not a problem here.)
|
||
var cachedUserPermissions = $cacheFactory('operationsUsersCache', { | ||
number: 10 | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like overkill since we are only ever storing one value (if the current user is an operations user)
number: 10 | ||
}); | ||
|
||
var inFlightPermissionsRequest = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is needed since you should never have multiple in flight requests for this specific permission
// Using cached data. | ||
Logger.log("AggregatedLoggingService, using cached user " + userName); | ||
if ((_.now() - userAllowed.cacheTimestamp) >= 600000) { | ||
userAllowed.forceRefresh = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't worry about forcing refresh... This permission does not change often. Let's just always use the cached value.
if ((_.now() - userAllowed.cacheTimestamp) >= 600000) { | ||
userAllowed.forceRefresh = true; | ||
} | ||
return $q.when(!!_.get(userAllowed, ['allowed'])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just...
return $q.when(userAllowed.allowed);
</button> | ||
</form> | ||
<div ng-if="kibanaArchiveUrl"> | ||
<a href="{{kibanaArchiveUrl}}" class="btn btn-primary btn-lg">View Archive</a> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ng-href
Why does this have button styling? It should just be a normal link.
app/scripts/services/logLinks.js
Outdated
|
||
var archiveUri = function(opts, prefix) { | ||
var archiveUri = function(opts, prefix, canViewOperationsLogs) { | ||
if((canViewOperationsLogs && !prefix) || (canViewOperationsLogs && prefix && prefix.startsWith('project.'))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (canViewOperationsLogs && (!prefix || prefix.startsWith('project.')) {
27f7837
to
fc1a1aa
Compare
@spadgett are you happy with the latest changes? |
null, ssar, {namespace: 'default'}).then( | ||
function(data) { | ||
userAllowed = data.status.allowed; | ||
return Promise.resolve(userAllowed); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return userAllowed;
userAllowed = data.status.allowed; | ||
return Promise.resolve(userAllowed); | ||
}, function() { | ||
return Promise.resolve(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return false;
} | ||
} | ||
}; | ||
authPromise = DataService.create({ group: 'authorization.k8s.io', version: 'v1', resource: 'selfsubjectaccessreviews'}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to create a Promise yourself. Just return the Promise from DataService.create
return DataService.create(...).then(...);
app/scripts/services/logLinks.js
Outdated
|
||
var archiveUri = function(opts, prefix) { | ||
var archiveUri = function(opts, prefix, canViewOperationsLogs) { | ||
if((canViewOperationsLogs) && (!prefix || prefix.startsWith('project.'))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style nit: i think the extra parens around canViewOperationsLogs actually make it harder to read
var isOperationsUser = function() { | ||
var authPromise = null; | ||
|
||
if (userAllowed === undefined) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd switch this to an early return if we have a cached value. It's easier to reason about.
if (userAllowed !== undefined) {
return $q.when(userAllowed);
}
var ssar = { ... };
return DataService.create(...).then(...);
cb82403
to
043ed71
Compare
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM but there seems to be a dist mismatch
app/scripts/directives/logViewer.js
Outdated
$scope.$watchGroup(['context.project.metadata.name', 'options.container', 'name'], function() { | ||
angular.extend($scope, { | ||
// The archive URL violates angular's built in same origin policy. | ||
// Need to explicitly tell it to trust this location or it will throw errors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this is still needed now that it's a link instead of a form. You can probably get rid of the trustAsResourceUrl
call
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment is out of date with the code now
return userAllowed; | ||
}, function() { | ||
return false; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 thanks, this looks much cleaner
043ed71
to
ef6e11c
Compare
@spadgett Thank you for your help and feedback. If there's still something to be done please let me know. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, except please remove the incorrect comment
app/scripts/directives/logViewer.js
Outdated
$scope.$watchGroup(['context.project.metadata.name', 'options.container', 'name'], function() { | ||
angular.extend($scope, { | ||
// The archive URL violates angular's built in same origin policy. | ||
// Need to explicitly tell it to trust this location or it will throw errors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment is out of date with the code now
app/scripts/directives/logViewer.js
Outdated
// The archive URL violates angular's built in same origin policy. | ||
// Need to explicitly tell it to trust this location or it will throw errors. | ||
kibanaArchiveUrl: logLinks.archiveUri({ | ||
baseURL: url, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: indentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure what's wrong with indentation here - a new block of code - indented by 2 spaces against its parent block
ef6e11c
to
40e9714
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/lgtm
Thanks @josefkarasek 👍
/cherrypick release-3.10 |
@jcantrill: #3060 failed to apply on top of branch "release-3.10":
In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
@josefkarasek please backport to 3.10 and 3.9 to fix relevent issues |
Note that this will need some rework if we aren't using oauth proxy in 3.10 and 3.9 |
Opening this PR mostly to get feedback. I'll open a new PR against 3.9 and 3.10 ones the fix is ready, with proper
grunt build
andtest
.fixes https://bugzilla.redhat.com/show_bug.cgi?id=1587807