-
Notifications
You must be signed in to change notification settings - Fork 740
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
feat(web): Expose experimental account storage API #1494
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,12 +21,22 @@ import com.fasterxml.jackson.databind.ObjectMapper | |
import com.netflix.spinnaker.gate.security.AllowedAccountsSupport | ||
import com.netflix.spinnaker.gate.security.SpinnakerUser | ||
import com.netflix.spinnaker.gate.services.AccountLookupService | ||
import com.netflix.spinnaker.gate.services.internal.ClouddriverService | ||
import com.netflix.spinnaker.gate.services.internal.ClouddriverService.Account | ||
import com.netflix.spinnaker.gate.services.internal.ClouddriverService.AccountDetails | ||
import com.netflix.spinnaker.kork.annotations.Alpha | ||
import com.netflix.spinnaker.security.User | ||
import io.swagger.annotations.ApiOperation | ||
import io.swagger.annotations.ApiParam | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.security.access.prepost.PostFilter | ||
import org.springframework.security.access.prepost.PreAuthorize | ||
import org.springframework.web.bind.annotation.DeleteMapping | ||
import org.springframework.web.bind.annotation.GetMapping | ||
import org.springframework.web.bind.annotation.PathVariable | ||
import org.springframework.web.bind.annotation.PostMapping | ||
import org.springframework.web.bind.annotation.PutMapping | ||
import org.springframework.web.bind.annotation.RequestBody | ||
import org.springframework.web.bind.annotation.RequestHeader | ||
import org.springframework.web.bind.annotation.RequestMapping | ||
import org.springframework.web.bind.annotation.RequestMethod | ||
|
@@ -43,6 +53,9 @@ class CredentialsController { | |
@Autowired | ||
AllowedAccountsSupport allowedAccountsSupport | ||
|
||
@Autowired | ||
ClouddriverService clouddriverService | ||
|
||
@Autowired | ||
ObjectMapper objectMapper | ||
|
||
|
@@ -78,4 +91,53 @@ class CredentialsController { | |
@RequestHeader(value = "X-RateLimit-App", required = false) String sourceApp) { | ||
return getAccountDetailsWithAuthorizedFlag(user).find { it.name == account } | ||
} | ||
|
||
@GetMapping('/type/{accountType}') | ||
@ApiOperation('Looks up account definitions by type.') | ||
@PostFilter("hasPermission(filterObject.name, 'ACCOUNT', 'WRITE')") | ||
@Alpha | ||
List<ClouddriverService.AccountDefinition> getAccountsByType( | ||
@ApiParam(value = 'Value of the "@type" key for accounts to search for.', example = 'kubernetes') | ||
@PathVariable String accountType, | ||
@ApiParam('Maximum number of entries to return in results. Used for pagination.') | ||
@RequestParam OptionalInt limit, | ||
@ApiParam('Account name to start account definition listing from. Used for pagination.') | ||
@RequestParam Optional<String> startingAccountName | ||
) { | ||
clouddriverService.getAccountDefinitionsByType(accountType, limit.isPresent() ? limit.getAsInt() : null, startingAccountName.orElse(null)) | ||
} | ||
|
||
@PostMapping | ||
@ApiOperation('Creates a new account definition.') | ||
@PreAuthorize('isAuthenticated()') | ||
@Alpha | ||
ClouddriverService.AccountDefinition createAccount( | ||
@ApiParam('Account definition body including a discriminator field named "@type" with the account type.') | ||
@RequestBody ClouddriverService.AccountDefinition accountDefinition | ||
) { | ||
clouddriverService.createAccountDefinition(accountDefinition) | ||
} | ||
|
||
@PutMapping | ||
@ApiOperation('Updates an existing account definition.') | ||
@PreAuthorize("hasPermission(#definition.name, 'ACCOUNT', 'WRITE')") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was also updated to allow users with WRITE permissions on the account to update it. |
||
@Alpha | ||
ClouddriverService.AccountDefinition updateAccount( | ||
@ApiParam('Account definition body including a discriminator field named "@type" with the account type.') | ||
@RequestBody ClouddriverService.AccountDefinition accountDefinition | ||
) { | ||
clouddriverService.updateAccountDefinition(accountDefinition) | ||
} | ||
|
||
@DeleteMapping('/{accountName}') | ||
@ApiOperation(value = 'Deletes an account definition by name.', | ||
notes = 'Deleted accounts can be restored via the update API. Previously deleted accounts cannot be "created" again to avoid conflicts with existing pipelines.') | ||
@PreAuthorize("hasPermission(#definition.name, 'ACCOUNT', 'WRITE')") | ||
@Alpha | ||
void deleteAccount( | ||
@ApiParam('Name of account definition to delete.') | ||
@PathVariable String accountName | ||
) { | ||
clouddriverService.deleteAccountDefinition(accountName) | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Note: I've changed permissions here to allow any authenticated user to create an account definition. It's on them to specify relevant permissions. Requires WRITE permissions to update and otherwise use the account (same semantics as READ and WRITE permissions on accounts everywhere else).
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 forget how this was previously, but I suspect we'd like to lock this down more than this. At least WRITE permissions makes sense to me. I suppose other permissions likely need to be in place for spinnaker to be able to use an account, so I can see how opening it up could be ok...but if nothing else it feels like opening up to mistakes, or possibly even a denial of service as extra accounts use resources.
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.
Previously, I had an admin check on these endpoints. Given that users have to supply their own credentials references (and grant the running Spinnaker access to those secrets), there doesn't seem to be much to secure here. I was thinking perhaps a config setting or table for storing groups or roles that are allowed to create/update/delete resources here, though users will only be able to affect accounts they have permission to write, and they have the ability to grant permissions during create to whoever.
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.
Guess I need to learn more what's in a credentials reference...but isn't there still a possibility of some malicious user creating a ton of accounts -- even ones that spinnaker doesn't have access to -- and using a bunch of resources. I'm thinking of attempting to cache those accounts and then spamming the logs.
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 a fair concern, though there's already a security setting for which group(s) are required in order to login. I haven't been able to think of a better way to manage the permissions to create accounts, though perhaps that could be a config setting rather than anything dynamic?
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.
For my own usage, I want users to be able to configure their own account credentials without involving the developers to update our config and redeploy anything. I also want to stop using shared account credentials, but that's difficult to do without providing a way for users to configure their own credentials (i.e., I don't have access to the same systems as my users do, so the user is responsible for linking their system to ours either way whether it be via importing a secret or by granting access to my shared credentials to their system). This is also why I'm thinking it might make sense to make configurable as some use cases could be like mine while other people's use cases could be more locked down.
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.
@dbyron-sf - I work with @jvz, and as jvz stated, we want people to be able to fully manage there accounts. That can be through creation or setting limitations.
One thing jvz and I have talked about is potentially allowing you to add a pluggable authorizer to allow users to specify the limitations that they wants. How does that sound?
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 could be ok...but sounds fancier/more complicated than maybe we need? Would a config flag be enough? I'm assuming fiat, so then it's configuring required permissions there...
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.
Yea, config flag would suffice as well. Generally permissions can get complicated, so having a pluggable authorizer may be nice. However, we can start with a config flag and if things get complicated, we can always change 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.
After some offline conversations, sounds like we're going to stick with
isAuthorized
for this PR and move to something that uses fiat as a second step. Is there some way to mark these endpoints as Alpha until they stabilize?