Merge branch 'release/1.21.0'

pull/2926/head 1.21.0
Anthony Lapenna 2019-06-04 15:51:20 +12:00
commit c1433eff0d
661 changed files with 15673 additions and 3336 deletions

12
.babelrc Normal file
View File

@ -0,0 +1,12 @@
{
"plugins": ["lodash", "angularjs-annotate"],
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"useBuiltIns": "usage"
}
]
]
}

View File

@ -53,6 +53,7 @@ plugins:
mass_threshold: 80 mass_threshold: 80
eslint: eslint:
enabled: true enabled: true
channel: "eslint-5"
config: config:
config: .eslintrc.yml config: .eslintrc.yml
fixme: fixme:

3
.eslintignore Normal file
View File

@ -0,0 +1,3 @@
node_modules/
dist/
test/

View File

@ -1,287 +1,292 @@
env: env:
browser: true browser: true
jquery: true jquery: true
node: true
es6: true
globals:
angular: true
__CONFIG_GA_ID: true
# globals:
# angular: true
# $: true
# _: true
# moment: true
# filesize: true
# splitargs: true
extends: extends:
- 'eslint:recommended' - 'eslint:recommended'
# http://eslint.org/docs/rules/ parserOptions:
ecmaVersion: 2018
sourceType: module
ecmaFeatures:
modules: true
# # http://eslint.org/docs/rules/
rules: rules:
# Possible Errors # # Possible Errors
no-await-in-loop: off # no-await-in-loop: off
no-cond-assign: error # no-cond-assign: error
no-console: off # no-console: off
no-constant-condition: error # no-constant-condition: error
no-control-regex: error # no-control-regex: error
no-debugger: error # no-debugger: error
no-dupe-args: error # no-dupe-args: error
no-dupe-keys: error # no-dupe-keys: error
no-duplicate-case: error # no-duplicate-case: error
no-empty-character-class: error # no-empty-character-class: error
no-empty: error no-empty: warn
no-ex-assign: error # no-ex-assign: error
no-extra-boolean-cast: error # no-extra-boolean-cast: error
no-extra-parens: off # no-extra-parens: off
no-extra-semi: error # no-extra-semi: error
no-func-assign: error # no-func-assign: error
no-inner-declarations: # no-inner-declarations:
- error # - error
- functions # - functions
no-invalid-regexp: error # no-invalid-regexp: error
no-irregular-whitespace: error # no-irregular-whitespace: error
no-negated-in-lhs: error # no-negated-in-lhs: error
no-obj-calls: error # no-obj-calls: error
no-prototype-builtins: off # no-prototype-builtins: off
no-regex-spaces: error # no-regex-spaces: error
no-sparse-arrays: error # no-sparse-arrays: error
no-template-curly-in-string: off # no-template-curly-in-string: off
no-unexpected-multiline: error # no-unexpected-multiline: error
no-unreachable: error # no-unreachable: error
no-unsafe-finally: off # no-unsafe-finally: off
no-unsafe-negation: off # no-unsafe-negation: off
use-isnan: error # use-isnan: error
valid-jsdoc: off # valid-jsdoc: off
valid-typeof: error # valid-typeof: error
# Best Practices # # Best Practices
accessor-pairs: error # accessor-pairs: error
array-callback-return: off # array-callback-return: off
block-scoped-var: off # block-scoped-var: off
class-methods-use-this: off # class-methods-use-this: off
complexity: # complexity:
- error # - error
- 6 # - 6
consistent-return: off # consistent-return: off
curly: off # curly: off
default-case: off # default-case: off
dot-location: off # dot-location: off
dot-notation: off # dot-notation: off
eqeqeq: error # eqeqeq: error
guard-for-in: error # guard-for-in: error
no-alert: error # no-alert: error
no-caller: error # no-caller: error
no-case-declarations: error # no-case-declarations: error
no-div-regex: error # no-div-regex: error
no-else-return: off # no-else-return: off
no-empty-function: off no-empty-function: warn
no-empty-pattern: error # no-empty-pattern: error
no-eq-null: error # no-eq-null: error
no-eval: error # no-eval: error
no-extend-native: error # no-extend-native: error
no-extra-bind: error # no-extra-bind: error
no-extra-label: off # no-extra-label: off
no-fallthrough: error # no-fallthrough: error
no-floating-decimal: off # no-floating-decimal: off
no-global-assign: off # no-global-assign: off
no-implicit-coercion: off # no-implicit-coercion: off
no-implied-eval: error # no-implied-eval: error
no-invalid-this: off # no-invalid-this: off
no-iterator: error # no-iterator: error
no-labels: # no-labels:
- error # - error
- allowLoop: true # - allowLoop: true
allowSwitch: true # allowSwitch: true
no-lone-blocks: error # no-lone-blocks: error
no-loop-func: error # no-loop-func: error
no-magic-number: off # no-magic-number: off
no-multi-spaces: off # no-multi-spaces: off
no-multi-str: off # no-multi-str: off
no-native-reassign: error # no-native-reassign: error
no-new-func: error # no-new-func: error
no-new-wrappers: error # no-new-wrappers: error
no-new: error # no-new: error
no-octal-escape: error # no-octal-escape: error
no-octal: error # no-octal: error
no-param-reassign: off # no-param-reassign: off
no-proto: error # no-proto: error
no-redeclare: error # no-redeclare: error
no-restricted-properties: off # no-restricted-properties: off
no-return-assign: error # no-return-assign: error
no-return-await: off # no-return-await: off
no-script-url: error # no-script-url: error
no-self-assign: off # no-self-assign: off
no-self-compare: error # no-self-compare: error
no-sequences: off # no-sequences: off
no-throw-literal: off # no-throw-literal: off
no-unmodified-loop-condition: off # no-unmodified-loop-condition: off
no-unused-expressions: error # no-unused-expressions: error
no-unused-labels: off # no-unused-labels: off
no-useless-call: error # no-useless-call: error
no-useless-concat: error # no-useless-concat: error
no-useless-escape: off no-useless-escape: off
no-useless-return: off # no-useless-return: off
no-void: error # no-void: error
no-warning-comments: off # no-warning-comments: off
no-with: error # no-with: error
prefer-promise-reject-errors: off # prefer-promise-reject-errors: off
radix: error # radix: error
require-await: off # require-await: off
vars-on-top: off # vars-on-top: off
wrap-iife: error # wrap-iife: error
yoda: off # yoda: off
# Strict # # Strict
strict: off # strict: off
# Variables # # Variables
init-declarations: off # init-declarations: off
no-catch-shadow: error # no-catch-shadow: error
no-delete-var: error # no-delete-var: error
no-label-var: error # no-label-var: error
no-restricted-globals: off # no-restricted-globals: off
no-shadow-restricted-names: error # no-shadow-restricted-names: error
no-shadow: off # no-shadow: off
no-undef-init: error # no-undef-init: error
no-undef: off # no-undef: off
no-undefined: off # no-undefined: off
no-unused-vars: # no-unused-vars:
- warn # - warn
- # -
vars: local # vars: local
no-use-before-define: off # no-use-before-define: off
# Node.js and CommonJS # # Node.js and CommonJS
callback-return: error # callback-return: error
global-require: error # global-require: error
handle-callback-err: error # handle-callback-err: error
no-mixed-requires: off # no-mixed-requires: off
no-new-require: off # no-new-require: off
no-path-concat: error # no-path-concat: error
no-process-env: off # no-process-env: off
no-process-exit: error # no-process-exit: error
no-restricted-modules: off # no-restricted-modules: off
no-sync: off # no-sync: off
# Stylistic Issues # # Stylistic Issues
array-bracket-spacing: off # array-bracket-spacing: off
block-spacing: off # block-spacing: off
brace-style: off # brace-style: off
camelcase: off # camelcase: off
capitalized-comments: off # capitalized-comments: off
comma-dangle: # comma-dangle:
- error # - error
- never # - never
comma-spacing: off # comma-spacing: off
comma-style: off # comma-style: off
computed-property-spacing: off # computed-property-spacing: off
consistent-this: off # consistent-this: off
eol-last: off # eol-last: off
func-call-spacing: off # func-call-spacing: off
func-name-matching: off # func-name-matching: off
func-names: off # func-names: off
func-style: off # func-style: off
id-length: off # id-length: off
id-match: off # id-match: off
indent: off # indent: off
jsx-quotes: off # jsx-quotes: off
key-spacing: off # key-spacing: off
keyword-spacing: off # keyword-spacing: off
line-comment-position: off # line-comment-position: off
linebreak-style: # linebreak-style:
- error # - error
- unix # - unix
lines-around-comment: off # lines-around-comment: off
lines-around-directive: off # lines-around-directive: off
max-depth: off # max-depth: off
max-len: off # max-len: off
max-nested-callbacks: off # max-nested-callbacks: off
max-params: off # max-params: off
max-statements-per-line: off # max-statements-per-line: off
max-statements: # max-statements:
- error # - error
- 30 # - 30
multiline-ternary: off # multiline-ternary: off
new-cap: off # new-cap: off
new-parens: off # new-parens: off
newline-after-var: off # newline-after-var: off
newline-before-return: off # newline-before-return: off
newline-per-chained-call: off # newline-per-chained-call: off
no-array-constructor: off # no-array-constructor: off
no-bitwise: off # no-bitwise: off
no-continue: off # no-continue: off
no-inline-comments: off # no-inline-comments: off
no-lonely-if: off # no-lonely-if: off
no-mixed-operators: off # no-mixed-operators: off
no-mixed-spaces-and-tabs: off # no-mixed-spaces-and-tabs: off
no-multi-assign: off # no-multi-assign: off
no-multiple-empty-lines: off # no-multiple-empty-lines: off
no-negated-condition: off # no-negated-condition: off
no-nested-ternary: off # no-nested-ternary: off
no-new-object: off # no-new-object: off
no-plusplus: off # no-plusplus: off
no-restricted-syntax: off # no-restricted-syntax: off
no-spaced-func: off # no-spaced-func: off
no-tabs: off # no-tabs: off
no-ternary: off # no-ternary: off
no-trailing-spaces: off # no-trailing-spaces: off
no-underscore-dangle: off # no-underscore-dangle: off
no-unneeded-ternary: off # no-unneeded-ternary: off
object-curly-newline: off # object-curly-newline: off
object-curly-spacing: off # object-curly-spacing: off
object-property-newline: off # object-property-newline: off
one-var-declaration-per-line: off # one-var-declaration-per-line: off
one-var: off # one-var: off
operator-assignment: off # operator-assignment: off
operator-linebreak: off # operator-linebreak: off
padded-blocks: off # padded-blocks: off
quote-props: off # quote-props: off
quotes: # quotes:
- error # - error
- single # - single
require-jsdoc: off # require-jsdoc: off
semi-spacing: off # semi-spacing: off
semi: # semi:
- error # - error
- always # - always
sort-keys: off # sort-keys: off
sort-vars: off # sort-vars: off
space-before-blocks: off # space-before-blocks: off
space-before-function-paren: off # space-before-function-paren: off
space-in-parens: off # space-in-parens: off
space-infix-ops: off # space-infix-ops: off
space-unary-ops: off # space-unary-ops: off
spaced-comment: off # spaced-comment: off
template-tag-spacing: off # template-tag-spacing: off
unicode-bom: off # unicode-bom: off
wrap-regex: off # wrap-regex: off
# ECMAScript 6 # # ECMAScript 6
arrow-body-style: off # arrow-body-style: off
arrow-parens: off # arrow-parens: off
arrow-spacing: off # arrow-spacing: off
constructor-super: off # constructor-super: off
generator-star-spacing: off # generator-star-spacing: off
no-class-assign: off # no-class-assign: off
no-confusing-arrow: off # no-confusing-arrow: off
no-const-assign: off # no-const-assign: off
no-dupe-class-members: off # no-dupe-class-members: off
no-duplicate-imports: off # no-duplicate-imports: off
no-new-symbol: off # no-new-symbol: off
no-restricted-imports: off # no-restricted-imports: off
no-this-before-super: off # no-this-before-super: off
no-useless-computed-key: off # no-useless-computed-key: off
no-useless-constructor: off # no-useless-constructor: off
no-useless-rename: off # no-useless-rename: off
no-var: off # no-var: off
object-shorthand: off # object-shorthand: off
prefer-arrow-callback: off # prefer-arrow-callback: off
prefer-const: off # prefer-const: off
prefer-destructuring: off # prefer-destructuring: off
prefer-numeric-literals: off # prefer-numeric-literals: off
prefer-rest-params: off # prefer-rest-params: off
prefer-reflect: off # prefer-reflect: off
prefer-spread: off # prefer-spread: off
prefer-template: off # prefer-template: off
require-yield: off # require-yield: off
rest-spread-spacing: off # rest-spread-spacing: off
sort-imports: off # sort-imports: off
symbol-description: off # symbol-description: off
template-curly-spacing: off # template-curly-spacing: off
yield-star-spacing: off # yield-star-spacing: off

View File

@ -8,7 +8,9 @@ about: Create a bug report
Thanks for reporting a bug for Portainer ! Thanks for reporting a bug for Portainer !
Do you need help or have a question? Come chat with us on Slack http://portainer.io/slack/ or gitter https://gitter.im/portainer/Lobby. You can find more information about Portainer support framework policy here: https://www.portainer.io/2019/04/portainer-support-policy/
Do you need help or have a question? Come chat with us on Slack http://portainer.io/slack/.
Before opening a new issue, make sure that we do not have any duplicates Before opening a new issue, make sure that we do not have any duplicates
already open. You can ensure this by searching the issue list for this already open. You can ensure this by searching the issue list for this

View File

@ -6,7 +6,9 @@ about: Ask us a question about Portainer usage or deployment
<!-- <!--
Do you need help or have a question? Come chat with us on Slack http://portainer.io/slack/ or gitter https://gitter.im/portainer/Lobby. You can find more information about Portainer support framework policy here: https://www.portainer.io/2019/04/portainer-support-policy/
Do you need help or have a question? Come chat with us on Slack http://portainer.io/slack/
Also, be sure to check our FAQ and documentation first: https://portainer.readthedocs.io Also, be sure to check our FAQ and documentation first: https://portainer.readthedocs.io
--> -->

View File

@ -8,7 +8,7 @@ about: Suggest a feature/enhancement that should be added in Portainer
Thanks for opening a feature request for Portainer ! Thanks for opening a feature request for Portainer !
Do you need help or have a question? Come chat with us on Slack http://portainer.io/slack/ or gitter https://gitter.im/portainer/Lobby. Do you need help or have a question? Come chat with us on Slack http://portainer.io/slack/
Before opening a new issue, make sure that we do not have any duplicates Before opening a new issue, make sure that we do not have any duplicates
already open. You can ensure this by searching the issue list for this already open. You can ensure this by searching the issue list for this

View File

@ -1,4 +1,3 @@
<p align="center"> <p align="center">
<img title="portainer" src='https://github.com/portainer/portainer/blob/develop/assets/images/logo_alt.png?raw=true' /> <img title="portainer" src='https://github.com/portainer/portainer/blob/develop/assets/images/logo_alt.png?raw=true' />
</p> </p>
@ -11,10 +10,8 @@
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHXZJQNJQ36H6) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHXZJQNJQ36H6)
**_Portainer_** is a lightweight management UI which allows you to **easily** manage your different Docker environments (Docker hosts or Swarm clusters). **_Portainer_** is a lightweight management UI which allows you to **easily** manage your different Docker environments (Docker hosts or Swarm clusters).
**_Portainer_** is meant to be as **simple** to deploy as it is to use. It consists of a single container that can run on any Docker engine (can be deployed as Linux container or a Windows native container, supports other platforms too).
**_Portainer_** is meant to be as **simple** to deploy as it is to use. It consists of a single container that can run on any Docker engine (can be deployed as Linux container or a Windows native container). **_Portainer_** allows you to manage your all your Docker resources (containers, images, volumes, networks and more) ! It is compatible with the *standalone Docker* engine and with *Docker Swarm mode*.
**_Portainer_** allows you to manage your Docker containers, images, volumes, networks and more ! It is compatible with the *standalone Docker* engine and with *Docker Swarm mode*.
## Demo ## Demo
@ -37,6 +34,8 @@ Unlike the public demo, the playground sessions are deleted after 4 hours. Apart
## Getting help ## Getting help
**NOTE**: You can find more information about Portainer support framework policy here: https://www.portainer.io/2019/04/portainer-support-policy/
* Issues: https://github.com/portainer/portainer/issues * Issues: https://github.com/portainer/portainer/issues
* FAQ: https://portainer.readthedocs.io/en/latest/faq.html * FAQ: https://portainer.readthedocs.io/en/latest/faq.html
* Slack (chat): https://portainer.io/slack/ * Slack (chat): https://portainer.io/slack/

View File

@ -6,24 +6,25 @@ import (
"time" "time"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/dockerhub" "github.com/portainer/portainer/api/bolt/dockerhub"
"github.com/portainer/portainer/bolt/endpoint" "github.com/portainer/portainer/api/bolt/endpoint"
"github.com/portainer/portainer/bolt/endpointgroup" "github.com/portainer/portainer/api/bolt/endpointgroup"
"github.com/portainer/portainer/bolt/extension" "github.com/portainer/portainer/api/bolt/extension"
"github.com/portainer/portainer/bolt/migrator" "github.com/portainer/portainer/api/bolt/migrator"
"github.com/portainer/portainer/bolt/registry" "github.com/portainer/portainer/api/bolt/registry"
"github.com/portainer/portainer/bolt/resourcecontrol" "github.com/portainer/portainer/api/bolt/resourcecontrol"
"github.com/portainer/portainer/bolt/schedule" "github.com/portainer/portainer/api/bolt/role"
"github.com/portainer/portainer/bolt/settings" "github.com/portainer/portainer/api/bolt/schedule"
"github.com/portainer/portainer/bolt/stack" "github.com/portainer/portainer/api/bolt/settings"
"github.com/portainer/portainer/bolt/tag" "github.com/portainer/portainer/api/bolt/stack"
"github.com/portainer/portainer/bolt/team" "github.com/portainer/portainer/api/bolt/tag"
"github.com/portainer/portainer/bolt/teammembership" "github.com/portainer/portainer/api/bolt/team"
"github.com/portainer/portainer/bolt/template" "github.com/portainer/portainer/api/bolt/teammembership"
"github.com/portainer/portainer/bolt/user" "github.com/portainer/portainer/api/bolt/template"
"github.com/portainer/portainer/bolt/version" "github.com/portainer/portainer/api/bolt/user"
"github.com/portainer/portainer/bolt/webhook" "github.com/portainer/portainer/api/bolt/version"
"github.com/portainer/portainer/api/bolt/webhook"
) )
const ( const (
@ -37,6 +38,7 @@ type Store struct {
db *bolt.DB db *bolt.DB
checkForDataMigration bool checkForDataMigration bool
fileService portainer.FileService fileService portainer.FileService
RoleService *role.Service
DockerHubService *dockerhub.Service DockerHubService *dockerhub.Service
EndpointGroupService *endpointgroup.Service EndpointGroupService *endpointgroup.Service
EndpointService *endpoint.Service EndpointService *endpoint.Service
@ -89,29 +91,6 @@ func (store *Store) Open() error {
return store.initServices() return store.initServices()
} }
// Init creates the default data set.
func (store *Store) Init() error {
groups, err := store.EndpointGroupService.EndpointGroups()
if err != nil {
return err
}
if len(groups) == 0 {
unassignedGroup := &portainer.EndpointGroup{
Name: "Unassigned",
Description: "Unassigned endpoints",
Labels: []portainer.Pair{},
AuthorizedUsers: []portainer.UserID{},
AuthorizedTeams: []portainer.TeamID{},
Tags: []string{},
}
return store.EndpointGroupService.CreateEndpointGroup(unassignedGroup)
}
return nil
}
// Close closes the BoltDB database. // Close closes the BoltDB database.
func (store *Store) Close() error { func (store *Store) Close() error {
if store.db != nil { if store.db != nil {
@ -140,6 +119,7 @@ func (store *Store) MigrateData() error {
EndpointGroupService: store.EndpointGroupService, EndpointGroupService: store.EndpointGroupService,
EndpointService: store.EndpointService, EndpointService: store.EndpointService,
ExtensionService: store.ExtensionService, ExtensionService: store.ExtensionService,
RegistryService: store.RegistryService,
ResourceControlService: store.ResourceControlService, ResourceControlService: store.ResourceControlService,
SettingsService: store.SettingsService, SettingsService: store.SettingsService,
StackService: store.StackService, StackService: store.StackService,
@ -162,6 +142,12 @@ func (store *Store) MigrateData() error {
} }
func (store *Store) initServices() error { func (store *Store) initServices() error {
authorizationsetService, err := role.NewService(store.db)
if err != nil {
return err
}
store.RoleService = authorizationsetService
dockerhubService, err := dockerhub.NewService(store.db) dockerhubService, err := dockerhub.NewService(store.db)
if err != nil { if err != nil {
return err return err

View File

@ -1,8 +1,8 @@
package dockerhub package dockerhub
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

View File

@ -1,8 +1,8 @@
package endpoint package endpoint
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

View File

@ -1,8 +1,8 @@
package endpointgroup package endpointgroup
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

View File

@ -1,8 +1,8 @@
package extension package extension
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

432
api/bolt/init.go Normal file
View File

@ -0,0 +1,432 @@
package bolt
import portainer "github.com/portainer/portainer/api"
// Init creates the default data set.
func (store *Store) Init() error {
groups, err := store.EndpointGroupService.EndpointGroups()
if err != nil {
return err
}
if len(groups) == 0 {
unassignedGroup := &portainer.EndpointGroup{
Name: "Unassigned",
Description: "Unassigned endpoints",
Labels: []portainer.Pair{},
UserAccessPolicies: portainer.UserAccessPolicies{},
TeamAccessPolicies: portainer.TeamAccessPolicies{},
Tags: []string{},
}
err = store.EndpointGroupService.CreateEndpointGroup(unassignedGroup)
if err != nil {
return err
}
}
roles, err := store.RoleService.Roles()
if err != nil {
return err
}
if len(roles) == 0 {
environmentAdministratorRole := &portainer.Role{
Name: "Endpoint administrator",
Description: "Full control of all resources in an endpoint",
Authorizations: map[portainer.Authorization]bool{
portainer.OperationDockerContainerArchiveInfo: true,
portainer.OperationDockerContainerList: true,
portainer.OperationDockerContainerExport: true,
portainer.OperationDockerContainerChanges: true,
portainer.OperationDockerContainerInspect: true,
portainer.OperationDockerContainerTop: true,
portainer.OperationDockerContainerLogs: true,
portainer.OperationDockerContainerStats: true,
portainer.OperationDockerContainerAttachWebsocket: true,
portainer.OperationDockerContainerArchive: true,
portainer.OperationDockerContainerCreate: true,
portainer.OperationDockerContainerPrune: true,
portainer.OperationDockerContainerKill: true,
portainer.OperationDockerContainerPause: true,
portainer.OperationDockerContainerUnpause: true,
portainer.OperationDockerContainerRestart: true,
portainer.OperationDockerContainerStart: true,
portainer.OperationDockerContainerStop: true,
portainer.OperationDockerContainerWait: true,
portainer.OperationDockerContainerResize: true,
portainer.OperationDockerContainerAttach: true,
portainer.OperationDockerContainerExec: true,
portainer.OperationDockerContainerRename: true,
portainer.OperationDockerContainerUpdate: true,
portainer.OperationDockerContainerPutContainerArchive: true,
portainer.OperationDockerContainerDelete: true,
portainer.OperationDockerImageList: true,
portainer.OperationDockerImageSearch: true,
portainer.OperationDockerImageGetAll: true,
portainer.OperationDockerImageGet: true,
portainer.OperationDockerImageHistory: true,
portainer.OperationDockerImageInspect: true,
portainer.OperationDockerImageLoad: true,
portainer.OperationDockerImageCreate: true,
portainer.OperationDockerImagePrune: true,
portainer.OperationDockerImagePush: true,
portainer.OperationDockerImageTag: true,
portainer.OperationDockerImageDelete: true,
portainer.OperationDockerImageCommit: true,
portainer.OperationDockerImageBuild: true,
portainer.OperationDockerNetworkList: true,
portainer.OperationDockerNetworkInspect: true,
portainer.OperationDockerNetworkCreate: true,
portainer.OperationDockerNetworkConnect: true,
portainer.OperationDockerNetworkDisconnect: true,
portainer.OperationDockerNetworkPrune: true,
portainer.OperationDockerNetworkDelete: true,
portainer.OperationDockerVolumeList: true,
portainer.OperationDockerVolumeInspect: true,
portainer.OperationDockerVolumeCreate: true,
portainer.OperationDockerVolumePrune: true,
portainer.OperationDockerVolumeDelete: true,
portainer.OperationDockerExecInspect: true,
portainer.OperationDockerExecStart: true,
portainer.OperationDockerExecResize: true,
portainer.OperationDockerSwarmInspect: true,
portainer.OperationDockerSwarmUnlockKey: true,
portainer.OperationDockerSwarmInit: true,
portainer.OperationDockerSwarmJoin: true,
portainer.OperationDockerSwarmLeave: true,
portainer.OperationDockerSwarmUpdate: true,
portainer.OperationDockerSwarmUnlock: true,
portainer.OperationDockerNodeList: true,
portainer.OperationDockerNodeInspect: true,
portainer.OperationDockerNodeUpdate: true,
portainer.OperationDockerNodeDelete: true,
portainer.OperationDockerServiceList: true,
portainer.OperationDockerServiceInspect: true,
portainer.OperationDockerServiceLogs: true,
portainer.OperationDockerServiceCreate: true,
portainer.OperationDockerServiceUpdate: true,
portainer.OperationDockerServiceDelete: true,
portainer.OperationDockerSecretList: true,
portainer.OperationDockerSecretInspect: true,
portainer.OperationDockerSecretCreate: true,
portainer.OperationDockerSecretUpdate: true,
portainer.OperationDockerSecretDelete: true,
portainer.OperationDockerConfigList: true,
portainer.OperationDockerConfigInspect: true,
portainer.OperationDockerConfigCreate: true,
portainer.OperationDockerConfigUpdate: true,
portainer.OperationDockerConfigDelete: true,
portainer.OperationDockerTaskList: true,
portainer.OperationDockerTaskInspect: true,
portainer.OperationDockerTaskLogs: true,
portainer.OperationDockerPluginList: true,
portainer.OperationDockerPluginPrivileges: true,
portainer.OperationDockerPluginInspect: true,
portainer.OperationDockerPluginPull: true,
portainer.OperationDockerPluginCreate: true,
portainer.OperationDockerPluginEnable: true,
portainer.OperationDockerPluginDisable: true,
portainer.OperationDockerPluginPush: true,
portainer.OperationDockerPluginUpgrade: true,
portainer.OperationDockerPluginSet: true,
portainer.OperationDockerPluginDelete: true,
portainer.OperationDockerSessionStart: true,
portainer.OperationDockerDistributionInspect: true,
portainer.OperationDockerBuildPrune: true,
portainer.OperationDockerBuildCancel: true,
portainer.OperationDockerPing: true,
portainer.OperationDockerInfo: true,
portainer.OperationDockerVersion: true,
portainer.OperationDockerEvents: true,
portainer.OperationDockerSystem: true,
portainer.OperationDockerUndefined: true,
portainer.OperationDockerAgentPing: true,
portainer.OperationDockerAgentList: true,
portainer.OperationDockerAgentHostInfo: true,
portainer.OperationDockerAgentBrowseDelete: true,
portainer.OperationDockerAgentBrowseGet: true,
portainer.OperationDockerAgentBrowseList: true,
portainer.OperationDockerAgentBrowsePut: true,
portainer.OperationDockerAgentBrowseRename: true,
portainer.OperationDockerAgentUndefined: true,
portainer.OperationPortainerResourceControlCreate: true,
portainer.OperationPortainerResourceControlUpdate: true,
portainer.OperationPortainerResourceControlDelete: true,
portainer.OperationPortainerStackList: true,
portainer.OperationPortainerStackInspect: true,
portainer.OperationPortainerStackFile: true,
portainer.OperationPortainerStackCreate: true,
portainer.OperationPortainerStackMigrate: true,
portainer.OperationPortainerStackUpdate: true,
portainer.OperationPortainerStackDelete: true,
portainer.OperationPortainerWebsocketExec: true,
portainer.OperationPortainerWebhookList: true,
portainer.OperationPortainerWebhookCreate: true,
portainer.OperationPortainerWebhookDelete: true,
portainer.OperationIntegrationStoridgeAdmin: true,
portainer.EndpointResourcesAccess: true,
},
}
err = store.RoleService.CreateRole(environmentAdministratorRole)
if err != nil {
return err
}
environmentReadOnlyUserRole := &portainer.Role{
Name: "Helpdesk",
Description: "Read-only access of all resources in an endpoint",
Authorizations: map[portainer.Authorization]bool{
portainer.OperationDockerContainerArchiveInfo: true,
portainer.OperationDockerContainerList: true,
portainer.OperationDockerContainerChanges: true,
portainer.OperationDockerContainerInspect: true,
portainer.OperationDockerContainerTop: true,
portainer.OperationDockerContainerLogs: true,
portainer.OperationDockerContainerStats: true,
portainer.OperationDockerImageList: true,
portainer.OperationDockerImageSearch: true,
portainer.OperationDockerImageGetAll: true,
portainer.OperationDockerImageGet: true,
portainer.OperationDockerImageHistory: true,
portainer.OperationDockerImageInspect: true,
portainer.OperationDockerNetworkList: true,
portainer.OperationDockerNetworkInspect: true,
portainer.OperationDockerVolumeList: true,
portainer.OperationDockerVolumeInspect: true,
portainer.OperationDockerSwarmInspect: true,
portainer.OperationDockerNodeList: true,
portainer.OperationDockerNodeInspect: true,
portainer.OperationDockerServiceList: true,
portainer.OperationDockerServiceInspect: true,
portainer.OperationDockerServiceLogs: true,
portainer.OperationDockerSecretList: true,
portainer.OperationDockerSecretInspect: true,
portainer.OperationDockerConfigList: true,
portainer.OperationDockerConfigInspect: true,
portainer.OperationDockerTaskList: true,
portainer.OperationDockerTaskInspect: true,
portainer.OperationDockerTaskLogs: true,
portainer.OperationDockerPluginList: true,
portainer.OperationDockerDistributionInspect: true,
portainer.OperationDockerPing: true,
portainer.OperationDockerInfo: true,
portainer.OperationDockerVersion: true,
portainer.OperationDockerEvents: true,
portainer.OperationDockerSystem: true,
portainer.OperationDockerAgentPing: true,
portainer.OperationDockerAgentList: true,
portainer.OperationDockerAgentHostInfo: true,
portainer.OperationDockerAgentBrowseGet: true,
portainer.OperationDockerAgentBrowseList: true,
portainer.OperationPortainerStackList: true,
portainer.OperationPortainerStackInspect: true,
portainer.OperationPortainerStackFile: true,
portainer.OperationPortainerWebhookList: true,
portainer.EndpointResourcesAccess: true,
},
}
err = store.RoleService.CreateRole(environmentReadOnlyUserRole)
if err != nil {
return err
}
standardUserRole := &portainer.Role{
Name: "Standard user",
Description: "Full control of assigned resources in an endpoint",
Authorizations: map[portainer.Authorization]bool{
portainer.OperationDockerContainerArchiveInfo: true,
portainer.OperationDockerContainerList: true,
portainer.OperationDockerContainerExport: true,
portainer.OperationDockerContainerChanges: true,
portainer.OperationDockerContainerInspect: true,
portainer.OperationDockerContainerTop: true,
portainer.OperationDockerContainerLogs: true,
portainer.OperationDockerContainerStats: true,
portainer.OperationDockerContainerAttachWebsocket: true,
portainer.OperationDockerContainerArchive: true,
portainer.OperationDockerContainerCreate: true,
portainer.OperationDockerContainerKill: true,
portainer.OperationDockerContainerPause: true,
portainer.OperationDockerContainerUnpause: true,
portainer.OperationDockerContainerRestart: true,
portainer.OperationDockerContainerStart: true,
portainer.OperationDockerContainerStop: true,
portainer.OperationDockerContainerWait: true,
portainer.OperationDockerContainerResize: true,
portainer.OperationDockerContainerAttach: true,
portainer.OperationDockerContainerExec: true,
portainer.OperationDockerContainerRename: true,
portainer.OperationDockerContainerUpdate: true,
portainer.OperationDockerContainerPutContainerArchive: true,
portainer.OperationDockerContainerDelete: true,
portainer.OperationDockerImageList: true,
portainer.OperationDockerImageSearch: true,
portainer.OperationDockerImageGetAll: true,
portainer.OperationDockerImageGet: true,
portainer.OperationDockerImageHistory: true,
portainer.OperationDockerImageInspect: true,
portainer.OperationDockerImageLoad: true,
portainer.OperationDockerImageCreate: true,
portainer.OperationDockerImagePush: true,
portainer.OperationDockerImageTag: true,
portainer.OperationDockerImageDelete: true,
portainer.OperationDockerImageCommit: true,
portainer.OperationDockerImageBuild: true,
portainer.OperationDockerNetworkList: true,
portainer.OperationDockerNetworkInspect: true,
portainer.OperationDockerNetworkCreate: true,
portainer.OperationDockerNetworkConnect: true,
portainer.OperationDockerNetworkDisconnect: true,
portainer.OperationDockerNetworkDelete: true,
portainer.OperationDockerVolumeList: true,
portainer.OperationDockerVolumeInspect: true,
portainer.OperationDockerVolumeCreate: true,
portainer.OperationDockerVolumeDelete: true,
portainer.OperationDockerExecInspect: true,
portainer.OperationDockerExecStart: true,
portainer.OperationDockerExecResize: true,
portainer.OperationDockerSwarmInspect: true,
portainer.OperationDockerSwarmUnlockKey: true,
portainer.OperationDockerSwarmInit: true,
portainer.OperationDockerSwarmJoin: true,
portainer.OperationDockerSwarmLeave: true,
portainer.OperationDockerSwarmUpdate: true,
portainer.OperationDockerSwarmUnlock: true,
portainer.OperationDockerNodeList: true,
portainer.OperationDockerNodeInspect: true,
portainer.OperationDockerNodeUpdate: true,
portainer.OperationDockerNodeDelete: true,
portainer.OperationDockerServiceList: true,
portainer.OperationDockerServiceInspect: true,
portainer.OperationDockerServiceLogs: true,
portainer.OperationDockerServiceCreate: true,
portainer.OperationDockerServiceUpdate: true,
portainer.OperationDockerServiceDelete: true,
portainer.OperationDockerSecretList: true,
portainer.OperationDockerSecretInspect: true,
portainer.OperationDockerSecretCreate: true,
portainer.OperationDockerSecretUpdate: true,
portainer.OperationDockerSecretDelete: true,
portainer.OperationDockerConfigList: true,
portainer.OperationDockerConfigInspect: true,
portainer.OperationDockerConfigCreate: true,
portainer.OperationDockerConfigUpdate: true,
portainer.OperationDockerConfigDelete: true,
portainer.OperationDockerTaskList: true,
portainer.OperationDockerTaskInspect: true,
portainer.OperationDockerTaskLogs: true,
portainer.OperationDockerPluginList: true,
portainer.OperationDockerPluginPrivileges: true,
portainer.OperationDockerPluginInspect: true,
portainer.OperationDockerPluginPull: true,
portainer.OperationDockerPluginCreate: true,
portainer.OperationDockerPluginEnable: true,
portainer.OperationDockerPluginDisable: true,
portainer.OperationDockerPluginPush: true,
portainer.OperationDockerPluginUpgrade: true,
portainer.OperationDockerPluginSet: true,
portainer.OperationDockerPluginDelete: true,
portainer.OperationDockerSessionStart: true,
portainer.OperationDockerDistributionInspect: true,
portainer.OperationDockerBuildPrune: true,
portainer.OperationDockerBuildCancel: true,
portainer.OperationDockerPing: true,
portainer.OperationDockerInfo: true,
portainer.OperationDockerVersion: true,
portainer.OperationDockerEvents: true,
portainer.OperationDockerSystem: true,
portainer.OperationDockerUndefined: true,
portainer.OperationDockerAgentPing: true,
portainer.OperationDockerAgentList: true,
portainer.OperationDockerAgentHostInfo: true,
portainer.OperationDockerAgentBrowseDelete: true,
portainer.OperationDockerAgentBrowseGet: true,
portainer.OperationDockerAgentBrowseList: true,
portainer.OperationDockerAgentBrowsePut: true,
portainer.OperationDockerAgentBrowseRename: true,
portainer.OperationDockerAgentUndefined: true,
portainer.OperationPortainerResourceControlCreate: true,
portainer.OperationPortainerResourceControlUpdate: true,
portainer.OperationPortainerResourceControlDelete: true,
portainer.OperationPortainerStackList: true,
portainer.OperationPortainerStackInspect: true,
portainer.OperationPortainerStackFile: true,
portainer.OperationPortainerStackCreate: true,
portainer.OperationPortainerStackMigrate: true,
portainer.OperationPortainerStackUpdate: true,
portainer.OperationPortainerStackDelete: true,
portainer.OperationPortainerWebsocketExec: true,
portainer.OperationPortainerWebhookList: true,
portainer.OperationPortainerWebhookCreate: true,
},
}
err = store.RoleService.CreateRole(standardUserRole)
if err != nil {
return err
}
readOnlyUserRole := &portainer.Role{
Name: "Read-only user",
Description: "Read-only access of assigned resources in an endpoint",
Authorizations: map[portainer.Authorization]bool{
portainer.OperationDockerContainerArchiveInfo: true,
portainer.OperationDockerContainerList: true,
portainer.OperationDockerContainerChanges: true,
portainer.OperationDockerContainerInspect: true,
portainer.OperationDockerContainerTop: true,
portainer.OperationDockerContainerLogs: true,
portainer.OperationDockerContainerStats: true,
portainer.OperationDockerImageList: true,
portainer.OperationDockerImageSearch: true,
portainer.OperationDockerImageGetAll: true,
portainer.OperationDockerImageGet: true,
portainer.OperationDockerImageHistory: true,
portainer.OperationDockerImageInspect: true,
portainer.OperationDockerNetworkList: true,
portainer.OperationDockerNetworkInspect: true,
portainer.OperationDockerVolumeList: true,
portainer.OperationDockerVolumeInspect: true,
portainer.OperationDockerSwarmInspect: true,
portainer.OperationDockerNodeList: true,
portainer.OperationDockerNodeInspect: true,
portainer.OperationDockerServiceList: true,
portainer.OperationDockerServiceInspect: true,
portainer.OperationDockerServiceLogs: true,
portainer.OperationDockerSecretList: true,
portainer.OperationDockerSecretInspect: true,
portainer.OperationDockerConfigList: true,
portainer.OperationDockerConfigInspect: true,
portainer.OperationDockerTaskList: true,
portainer.OperationDockerTaskInspect: true,
portainer.OperationDockerTaskLogs: true,
portainer.OperationDockerPluginList: true,
portainer.OperationDockerDistributionInspect: true,
portainer.OperationDockerPing: true,
portainer.OperationDockerInfo: true,
portainer.OperationDockerVersion: true,
portainer.OperationDockerEvents: true,
portainer.OperationDockerSystem: true,
portainer.OperationDockerAgentPing: true,
portainer.OperationDockerAgentList: true,
portainer.OperationDockerAgentHostInfo: true,
portainer.OperationDockerAgentBrowseGet: true,
portainer.OperationDockerAgentBrowseList: true,
portainer.OperationPortainerStackList: true,
portainer.OperationPortainerStackInspect: true,
portainer.OperationPortainerStackFile: true,
portainer.OperationPortainerWebhookList: true,
},
}
err = store.RoleService.CreateRole(readOnlyUserRole)
if err != nil {
return err
}
}
return nil
}

View File

@ -4,7 +4,7 @@ import (
"encoding/binary" "encoding/binary"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// Itob returns an 8-byte big endian representation of v. // Itob returns an 8-byte big endian representation of v.

View File

@ -2,8 +2,8 @@ package migrator
import ( import (
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/user" "github.com/portainer/portainer/api/bolt/user"
) )
func (m *Migrator) updateAdminUserToDBVersion1() error { func (m *Migrator) updateAdminUserToDBVersion1() error {

View File

@ -2,8 +2,8 @@ package migrator
import ( import (
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
) )
func (m *Migrator) updateResourceControlsToDBVersion2() error { func (m *Migrator) updateResourceControlsToDBVersion2() error {

View File

@ -1,6 +1,6 @@
package migrator package migrator
import "github.com/portainer/portainer" import "github.com/portainer/portainer/api"
func (m *Migrator) updateEndpointsToVersion11() error { func (m *Migrator) updateEndpointsToVersion11() error {
legacyEndpoints, err := m.endpointService.Endpoints() legacyEndpoints, err := m.endpointService.Endpoints()

View File

@ -5,9 +5,9 @@ import (
"strings" "strings"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/portainer/portainer/bolt/stack" "github.com/portainer/portainer/api/bolt/stack"
) )
func (m *Migrator) updateEndpointsToVersion12() error { func (m *Migrator) updateEndpointsToVersion12() error {

View File

@ -1,6 +1,6 @@
package migrator package migrator
import "github.com/portainer/portainer" import "github.com/portainer/portainer/api"
func (m *Migrator) updateSettingsToVersion13() error { func (m *Migrator) updateSettingsToVersion13() error {
legacySettings, err := m.settingsService.Settings() legacySettings, err := m.settingsService.Settings()

View File

@ -3,7 +3,7 @@ package migrator
import ( import (
"strings" "strings"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
func (m *Migrator) updateSettingsToDBVersion15() error { func (m *Migrator) updateSettingsToDBVersion15() error {

View File

@ -0,0 +1,125 @@
package migrator
import (
portainer "github.com/portainer/portainer/api"
)
func (m *Migrator) updateUsersToDBVersion18() error {
legacyUsers, err := m.userService.Users()
if err != nil {
return err
}
for _, user := range legacyUsers {
user.PortainerAuthorizations = map[portainer.Authorization]bool{
portainer.OperationPortainerDockerHubInspect: true,
portainer.OperationPortainerEndpointGroupList: true,
portainer.OperationPortainerEndpointList: true,
portainer.OperationPortainerEndpointInspect: true,
portainer.OperationPortainerEndpointExtensionAdd: true,
portainer.OperationPortainerEndpointExtensionRemove: true,
portainer.OperationPortainerExtensionList: true,
portainer.OperationPortainerMOTD: true,
portainer.OperationPortainerRegistryList: true,
portainer.OperationPortainerRegistryInspect: true,
portainer.OperationPortainerTeamList: true,
portainer.OperationPortainerTemplateList: true,
portainer.OperationPortainerTemplateInspect: true,
portainer.OperationPortainerUserList: true,
portainer.OperationPortainerUserMemberships: true,
}
err = m.userService.UpdateUser(user.ID, &user)
if err != nil {
return err
}
}
return nil
}
func (m *Migrator) updateEndpointsToDBVersion18() error {
legacyEndpoints, err := m.endpointService.Endpoints()
if err != nil {
return err
}
for _, endpoint := range legacyEndpoints {
endpoint.UserAccessPolicies = make(portainer.UserAccessPolicies)
for _, userID := range endpoint.AuthorizedUsers {
endpoint.UserAccessPolicies[userID] = portainer.AccessPolicy{
RoleID: 4,
}
}
endpoint.TeamAccessPolicies = make(portainer.TeamAccessPolicies)
for _, teamID := range endpoint.AuthorizedTeams {
endpoint.TeamAccessPolicies[teamID] = portainer.AccessPolicy{
RoleID: 4,
}
}
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
if err != nil {
return err
}
}
return nil
}
func (m *Migrator) updateEndpointGroupsToDBVersion18() error {
legacyEndpointGroups, err := m.endpointGroupService.EndpointGroups()
if err != nil {
return err
}
for _, endpointGroup := range legacyEndpointGroups {
endpointGroup.UserAccessPolicies = make(portainer.UserAccessPolicies)
for _, userID := range endpointGroup.AuthorizedUsers {
endpointGroup.UserAccessPolicies[userID] = portainer.AccessPolicy{
RoleID: 4,
}
}
endpointGroup.TeamAccessPolicies = make(portainer.TeamAccessPolicies)
for _, teamID := range endpointGroup.AuthorizedTeams {
endpointGroup.TeamAccessPolicies[teamID] = portainer.AccessPolicy{
RoleID: 4,
}
}
err = m.endpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
if err != nil {
return err
}
}
return nil
}
func (m *Migrator) updateRegistriesToDBVersion18() error {
legacyRegistries, err := m.registryService.Registries()
if err != nil {
return err
}
for _, registry := range legacyRegistries {
registry.UserAccessPolicies = make(portainer.UserAccessPolicies)
for _, userID := range registry.AuthorizedUsers {
registry.UserAccessPolicies[userID] = portainer.AccessPolicy{}
}
registry.TeamAccessPolicies = make(portainer.TeamAccessPolicies)
for _, teamID := range registry.AuthorizedTeams {
registry.TeamAccessPolicies[teamID] = portainer.AccessPolicy{}
}
err = m.registryService.UpdateRegistry(registry.ID, &registry)
if err != nil {
return err
}
}
return nil
}

View File

@ -1,6 +1,6 @@
package migrator package migrator
import "github.com/portainer/portainer" import "github.com/portainer/portainer/api"
func (m *Migrator) updateSettingsToDBVersion3() error { func (m *Migrator) updateSettingsToDBVersion3() error {
legacySettings, err := m.settingsService.Settings() legacySettings, err := m.settingsService.Settings()

View File

@ -1,6 +1,6 @@
package migrator package migrator
import "github.com/portainer/portainer" import "github.com/portainer/portainer/api"
func (m *Migrator) updateEndpointsToDBVersion4() error { func (m *Migrator) updateEndpointsToDBVersion4() error {
legacyEndpoints, err := m.endpointService.Endpoints() legacyEndpoints, err := m.endpointService.Endpoints()

View File

@ -1,6 +1,6 @@
package migrator package migrator
import "github.com/portainer/portainer" import "github.com/portainer/portainer/api"
func (m *Migrator) updateEndpointsToVersion8() error { func (m *Migrator) updateEndpointsToVersion8() error {
legacyEndpoints, err := m.endpointService.Endpoints() legacyEndpoints, err := m.endpointService.Endpoints()

View File

@ -1,6 +1,6 @@
package migrator package migrator
import "github.com/portainer/portainer" import "github.com/portainer/portainer/api"
func (m *Migrator) updateEndpointsToVersion9() error { func (m *Migrator) updateEndpointsToVersion9() error {
legacyEndpoints, err := m.endpointService.Endpoints() legacyEndpoints, err := m.endpointService.Endpoints()

View File

@ -1,6 +1,6 @@
package migrator package migrator
import "github.com/portainer/portainer" import "github.com/portainer/portainer/api"
func (m *Migrator) updateEndpointsToVersion10() error { func (m *Migrator) updateEndpointsToVersion10() error {
legacyEndpoints, err := m.endpointService.Endpoints() legacyEndpoints, err := m.endpointService.Endpoints()

View File

@ -2,16 +2,17 @@ package migrator
import ( import (
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/endpoint" "github.com/portainer/portainer/api/bolt/endpoint"
"github.com/portainer/portainer/bolt/endpointgroup" "github.com/portainer/portainer/api/bolt/endpointgroup"
"github.com/portainer/portainer/bolt/extension" "github.com/portainer/portainer/api/bolt/extension"
"github.com/portainer/portainer/bolt/resourcecontrol" "github.com/portainer/portainer/api/bolt/registry"
"github.com/portainer/portainer/bolt/settings" "github.com/portainer/portainer/api/bolt/resourcecontrol"
"github.com/portainer/portainer/bolt/stack" "github.com/portainer/portainer/api/bolt/settings"
"github.com/portainer/portainer/bolt/template" "github.com/portainer/portainer/api/bolt/stack"
"github.com/portainer/portainer/bolt/user" "github.com/portainer/portainer/api/bolt/template"
"github.com/portainer/portainer/bolt/version" "github.com/portainer/portainer/api/bolt/user"
"github.com/portainer/portainer/api/bolt/version"
) )
type ( type (
@ -22,6 +23,7 @@ type (
endpointGroupService *endpointgroup.Service endpointGroupService *endpointgroup.Service
endpointService *endpoint.Service endpointService *endpoint.Service
extensionService *extension.Service extensionService *extension.Service
registryService *registry.Service
resourceControlService *resourcecontrol.Service resourceControlService *resourcecontrol.Service
settingsService *settings.Service settingsService *settings.Service
stackService *stack.Service stackService *stack.Service
@ -38,6 +40,7 @@ type (
EndpointGroupService *endpointgroup.Service EndpointGroupService *endpointgroup.Service
EndpointService *endpoint.Service EndpointService *endpoint.Service
ExtensionService *extension.Service ExtensionService *extension.Service
RegistryService *registry.Service
ResourceControlService *resourcecontrol.Service ResourceControlService *resourcecontrol.Service
SettingsService *settings.Service SettingsService *settings.Service
StackService *stack.Service StackService *stack.Service
@ -56,6 +59,7 @@ func NewMigrator(parameters *Parameters) *Migrator {
endpointGroupService: parameters.EndpointGroupService, endpointGroupService: parameters.EndpointGroupService,
endpointService: parameters.EndpointService, endpointService: parameters.EndpointService,
extensionService: parameters.ExtensionService, extensionService: parameters.ExtensionService,
registryService: parameters.RegistryService,
resourceControlService: parameters.ResourceControlService, resourceControlService: parameters.ResourceControlService,
settingsService: parameters.SettingsService, settingsService: parameters.SettingsService,
templateService: parameters.TemplateService, templateService: parameters.TemplateService,
@ -222,5 +226,28 @@ func (m *Migrator) Migrate() error {
} }
} }
// Portainer 1.21.0
if m.currentDBVersion < 18 {
err := m.updateUsersToDBVersion18()
if err != nil {
return err
}
err = m.updateEndpointsToDBVersion18()
if err != nil {
return err
}
err = m.updateEndpointGroupsToDBVersion18()
if err != nil {
return err
}
err = m.updateRegistriesToDBVersion18()
if err != nil {
return err
}
}
return m.versionService.StoreDBVersion(portainer.DBVersion) return m.versionService.StoreDBVersion(portainer.DBVersion)
} }

View File

@ -1,8 +1,8 @@
package registry package registry
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

View File

@ -1,8 +1,8 @@
package resourcecontrol package resourcecontrol
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

83
api/bolt/role/role.go Normal file
View File

@ -0,0 +1,83 @@
package role
import (
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "roles"
)
// Service represents a service for managing endpoint data.
type Service struct {
db *bolt.DB
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
}, nil
}
// Role returns a Role by ID
func (service *Service) Role(ID portainer.RoleID) (*portainer.Role, error) {
var set portainer.Role
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.db, BucketName, identifier, &set)
if err != nil {
return nil, err
}
return &set, nil
}
// Roles return an array containing all the sets.
func (service *Service) Roles() ([]portainer.Role, error) {
var sets = make([]portainer.Role, 0)
err := service.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
var set portainer.Role
err := internal.UnmarshalObject(v, &set)
if err != nil {
return err
}
sets = append(sets, set)
}
return nil
})
return sets, err
}
// CreateRole creates a new Role.
func (service *Service) CreateRole(set *portainer.Role) error {
return service.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
id, _ := bucket.NextSequence()
set.ID = portainer.RoleID(id)
data, err := internal.MarshalObject(set)
if err != nil {
return err
}
return bucket.Put(internal.Itob(int(set.ID)), data)
})
}

View File

@ -1,8 +1,8 @@
package schedule package schedule
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

View File

@ -1,8 +1,8 @@
package settings package settings
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

View File

@ -1,8 +1,8 @@
package stack package stack
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

View File

@ -1,8 +1,8 @@
package tag package tag
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

View File

@ -1,8 +1,8 @@
package team package team
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

View File

@ -1,8 +1,8 @@
package teammembership package teammembership
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

View File

@ -1,8 +1,8 @@
package template package template
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

View File

@ -1,8 +1,8 @@
package user package user
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

View File

@ -4,8 +4,8 @@ import (
"strconv" "strconv"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
) )
const ( const (

View File

@ -1,8 +1,8 @@
package webhook package webhook
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt/internal" "github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )

View File

@ -3,7 +3,7 @@ package cli
import ( import (
"time" "time"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"os" "os"
"path/filepath" "path/filepath"

View File

@ -1,7 +1,7 @@
package cli package cli
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"fmt" "fmt"
"gopkg.in/alecthomas/kingpin.v2" "gopkg.in/alecthomas/kingpin.v2"

View File

@ -1,4 +1,4 @@
package main // import "github.com/portainer/portainer" package main
import ( import (
"encoding/json" "encoding/json"
@ -6,20 +6,20 @@ import (
"strings" "strings"
"time" "time"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/bolt" "github.com/portainer/portainer/api/bolt"
"github.com/portainer/portainer/cli" "github.com/portainer/portainer/api/cli"
"github.com/portainer/portainer/cron" "github.com/portainer/portainer/api/cron"
"github.com/portainer/portainer/crypto" "github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/docker" "github.com/portainer/portainer/api/docker"
"github.com/portainer/portainer/exec" "github.com/portainer/portainer/api/exec"
"github.com/portainer/portainer/filesystem" "github.com/portainer/portainer/api/filesystem"
"github.com/portainer/portainer/git" "github.com/portainer/portainer/api/git"
"github.com/portainer/portainer/http" "github.com/portainer/portainer/api/http"
"github.com/portainer/portainer/http/client" "github.com/portainer/portainer/api/http/client"
"github.com/portainer/portainer/jwt" "github.com/portainer/portainer/api/jwt"
"github.com/portainer/portainer/ldap" "github.com/portainer/portainer/api/ldap"
"github.com/portainer/portainer/libcompose" "github.com/portainer/portainer/api/libcompose"
"log" "log"
) )
@ -377,18 +377,18 @@ func createTLSSecuredEndpoint(flags *portainer.CLIFlags, endpointService portain
endpointID := endpointService.GetNextIdentifier() endpointID := endpointService.GetNextIdentifier()
endpoint := &portainer.Endpoint{ endpoint := &portainer.Endpoint{
ID: portainer.EndpointID(endpointID), ID: portainer.EndpointID(endpointID),
Name: "primary", Name: "primary",
URL: *flags.EndpointURL, URL: *flags.EndpointURL,
GroupID: portainer.EndpointGroupID(1), GroupID: portainer.EndpointGroupID(1),
Type: portainer.DockerEnvironment, Type: portainer.DockerEnvironment,
TLSConfig: tlsConfiguration, TLSConfig: tlsConfiguration,
AuthorizedUsers: []portainer.UserID{}, UserAccessPolicies: portainer.UserAccessPolicies{},
AuthorizedTeams: []portainer.TeamID{}, TeamAccessPolicies: portainer.TeamAccessPolicies{},
Extensions: []portainer.EndpointExtension{}, Extensions: []portainer.EndpointExtension{},
Tags: []string{}, Tags: []string{},
Status: portainer.EndpointStatusUp, Status: portainer.EndpointStatusUp,
Snapshots: []portainer.Snapshot{}, Snapshots: []portainer.Snapshot{},
} }
if strings.HasPrefix(endpoint.URL, "tcp://") { if strings.HasPrefix(endpoint.URL, "tcp://") {
@ -420,18 +420,18 @@ func createUnsecuredEndpoint(endpointURL string, endpointService portainer.Endpo
endpointID := endpointService.GetNextIdentifier() endpointID := endpointService.GetNextIdentifier()
endpoint := &portainer.Endpoint{ endpoint := &portainer.Endpoint{
ID: portainer.EndpointID(endpointID), ID: portainer.EndpointID(endpointID),
Name: "primary", Name: "primary",
URL: endpointURL, URL: endpointURL,
GroupID: portainer.EndpointGroupID(1), GroupID: portainer.EndpointGroupID(1),
Type: portainer.DockerEnvironment, Type: portainer.DockerEnvironment,
TLSConfig: portainer.TLSConfiguration{}, TLSConfig: portainer.TLSConfiguration{},
AuthorizedUsers: []portainer.UserID{}, UserAccessPolicies: portainer.UserAccessPolicies{},
AuthorizedTeams: []portainer.TeamID{}, TeamAccessPolicies: portainer.TeamAccessPolicies{},
Extensions: []portainer.EndpointExtension{}, Extensions: []portainer.EndpointExtension{},
Tags: []string{}, Tags: []string{},
Status: portainer.EndpointStatusUp, Status: portainer.EndpointStatusUp,
Snapshots: []portainer.Snapshot{}, Snapshots: []portainer.Snapshot{},
} }
return snapshotAndPersistEndpoint(endpoint, endpointService, snapshotter) return snapshotAndPersistEndpoint(endpoint, endpointService, snapshotter)
@ -627,6 +627,23 @@ func main() {
Username: "admin", Username: "admin",
Role: portainer.AdministratorRole, Role: portainer.AdministratorRole,
Password: adminPasswordHash, Password: adminPasswordHash,
PortainerAuthorizations: map[portainer.Authorization]bool{
portainer.OperationPortainerDockerHubInspect: true,
portainer.OperationPortainerEndpointGroupList: true,
portainer.OperationPortainerEndpointList: true,
portainer.OperationPortainerEndpointInspect: true,
portainer.OperationPortainerEndpointExtensionAdd: true,
portainer.OperationPortainerEndpointExtensionRemove: true,
portainer.OperationPortainerExtensionList: true,
portainer.OperationPortainerMOTD: true,
portainer.OperationPortainerRegistryList: true,
portainer.OperationPortainerRegistryInspect: true,
portainer.OperationPortainerTeamList: true,
portainer.OperationPortainerTemplateList: true,
portainer.OperationPortainerTemplateInspect: true,
portainer.OperationPortainerUserList: true,
portainer.OperationPortainerUserMemberships: true,
},
} }
err := store.UserService.CreateUser(user) err := store.UserService.CreateUser(user)
if err != nil { if err != nil {
@ -647,6 +664,7 @@ func main() {
AssetsPath: *flags.Assets, AssetsPath: *flags.Assets,
AuthDisabled: *flags.NoAuth, AuthDisabled: *flags.NoAuth,
EndpointManagement: endpointManagement, EndpointManagement: endpointManagement,
RoleService: store.RoleService,
UserService: store.UserService, UserService: store.UserService,
TeamService: store.TeamService, TeamService: store.TeamService,
TeamMembershipService: store.TeamMembershipService, TeamMembershipService: store.TeamMembershipService,

View File

@ -6,7 +6,7 @@ import (
"log" "log"
"strings" "strings"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// EndpointSyncJobRunner is used to run a EndpointSyncJob // EndpointSyncJobRunner is used to run a EndpointSyncJob

View File

@ -4,7 +4,7 @@ import (
"log" "log"
"time" "time"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// ScriptExecutionJobRunner is used to run a ScriptExecutionJob // ScriptExecutionJobRunner is used to run a ScriptExecutionJob

View File

@ -3,7 +3,7 @@ package cron
import ( import (
"log" "log"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// SnapshotJobRunner is used to run a SnapshotJob // SnapshotJobRunner is used to run a SnapshotJob

View File

@ -1,7 +1,7 @@
package cron package cron
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/robfig/cron" "github.com/robfig/cron"
) )

View File

@ -6,8 +6,8 @@ import (
"time" "time"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/crypto" "github.com/portainer/portainer/api/crypto"
) )
const ( const (

View File

@ -12,8 +12,8 @@ import (
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/strslice" "github.com/docker/docker/api/types/strslice"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/archive" "github.com/portainer/portainer/api/archive"
) )
// JobService represents a service that handles the execution of jobs // JobService represents a service that handles the execution of jobs

View File

@ -7,7 +7,7 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
func snapshot(cli *client.Client) (*portainer.Snapshot, error) { func snapshot(cli *client.Client) (*portainer.Snapshot, error) {

View File

@ -1,7 +1,7 @@
package docker package docker
import ( import (
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// Snapshotter represents a service used to create endpoint snapshots // Snapshotter represents a service used to create endpoint snapshots

View File

@ -4,6 +4,7 @@ package portainer
const ( const (
ErrUnauthorized = Error("Unauthorized") ErrUnauthorized = Error("Unauthorized")
ErrResourceAccessDenied = Error("Access denied to resource") ErrResourceAccessDenied = Error("Access denied to resource")
ErrAuthorizationRequired = Error("Authorization required for this operation")
ErrObjectNotFound = Error("Object not found inside the database") ErrObjectNotFound = Error("Object not found inside the database")
ErrMissingSecurityContext = Error("Unable to find security details in request context") ErrMissingSecurityContext = Error("Unable to find security details in request context")
) )

View File

@ -9,10 +9,11 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/orcaman/concurrent-map" "github.com/orcaman/concurrent-map"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/http/client" "github.com/portainer/portainer/api/http/client"
) )
var extensionDownloadBaseURL = "https://portainer-io-assets.sfo2.digitaloceanspaces.com/extensions/" var extensionDownloadBaseURL = "https://portainer-io-assets.sfo2.digitaloceanspaces.com/extensions/"
@ -20,6 +21,7 @@ var extensionDownloadBaseURL = "https://portainer-io-assets.sfo2.digitaloceanspa
var extensionBinaryMap = map[portainer.ExtensionID]string{ var extensionBinaryMap = map[portainer.ExtensionID]string{
portainer.RegistryManagementExtension: "extension-registry-management", portainer.RegistryManagementExtension: "extension-registry-management",
portainer.OAuthAuthenticationExtension: "extension-oauth-authentication", portainer.OAuthAuthenticationExtension: "extension-oauth-authentication",
portainer.RBACExtension: "extension-rbac",
} }
// ExtensionManager represents a service used to // ExtensionManager represents a service used to
@ -206,6 +208,8 @@ func (manager *ExtensionManager) startExtensionProcess(extension *portainer.Exte
return err return err
} }
time.Sleep(3 * time.Second)
manager.processes.Set(processKey(extension.ID), extensionProcess) manager.processes.Set(processKey(extension.ID), extensionProcess)
return nil return nil
} }

View File

@ -8,7 +8,7 @@ import (
"path" "path"
"runtime" "runtime"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// SwarmStackManager represents a service for managing stacks. // SwarmStackManager represents a service for managing stacks.

View File

@ -6,8 +6,8 @@ import (
"encoding/pem" "encoding/pem"
"io/ioutil" "io/ioutil"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/archive" "github.com/portainer/portainer/api/archive"
"io" "io"
"os" "os"

View File

@ -10,7 +10,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
const ( const (

View File

@ -9,7 +9,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
type authenticatePayload struct { type authenticatePayload struct {
@ -100,6 +100,23 @@ func (handler *Handler) authenticateLDAPAndCreateUser(w http.ResponseWriter, use
user := &portainer.User{ user := &portainer.User{
Username: username, Username: username,
Role: portainer.StandardUserRole, Role: portainer.StandardUserRole,
PortainerAuthorizations: map[portainer.Authorization]bool{
portainer.OperationPortainerDockerHubInspect: true,
portainer.OperationPortainerEndpointGroupList: true,
portainer.OperationPortainerEndpointList: true,
portainer.OperationPortainerEndpointInspect: true,
portainer.OperationPortainerEndpointExtensionAdd: true,
portainer.OperationPortainerEndpointExtensionRemove: true,
portainer.OperationPortainerExtensionList: true,
portainer.OperationPortainerMOTD: true,
portainer.OperationPortainerRegistryList: true,
portainer.OperationPortainerRegistryInspect: true,
portainer.OperationPortainerTeamList: true,
portainer.OperationPortainerTemplateList: true,
portainer.OperationPortainerTemplateInspect: true,
portainer.OperationPortainerUserList: true,
portainer.OperationPortainerUserMemberships: true,
},
} }
err = handler.UserService.CreateUser(user) err = handler.UserService.CreateUser(user)
@ -117,11 +134,60 @@ func (handler *Handler) authenticateLDAPAndCreateUser(w http.ResponseWriter, use
func (handler *Handler) writeToken(w http.ResponseWriter, user *portainer.User) *httperror.HandlerError { func (handler *Handler) writeToken(w http.ResponseWriter, user *portainer.User) *httperror.HandlerError {
tokenData := &portainer.TokenData{ tokenData := &portainer.TokenData{
ID: user.ID, ID: user.ID,
Username: user.Username, Username: user.Username,
Role: user.Role, Role: user.Role,
PortainerAuthorizations: user.PortainerAuthorizations,
} }
_, err := handler.ExtensionService.Extension(portainer.RBACExtension)
if err == portainer.ErrObjectNotFound {
return handler.persistAndWriteToken(w, tokenData)
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a extension with the specified identifier inside the database", err}
}
endpointAuthorizations, err := handler.getAuthorizations(user)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve authorizations associated to the user", err}
}
tokenData.EndpointAuthorizations = endpointAuthorizations
return handler.persistAndWriteToken(w, tokenData)
}
func (handler *Handler) getAuthorizations(user *portainer.User) (portainer.EndpointAuthorizations, error) {
endpointAuthorizations := portainer.EndpointAuthorizations{}
if user.Role == portainer.AdministratorRole {
return endpointAuthorizations, nil
}
userMemberships, err := handler.TeamMembershipService.TeamMembershipsByUserID(user.ID)
if err != nil {
return endpointAuthorizations, err
}
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return endpointAuthorizations, err
}
endpointGroups, err := handler.EndpointGroupService.EndpointGroups()
if err != nil {
return endpointAuthorizations, err
}
roles, err := handler.RoleService.Roles()
if err != nil {
return endpointAuthorizations, err
}
endpointAuthorizations = getUserEndpointAuthorizations(user, endpoints, endpointGroups, roles, userMemberships)
return endpointAuthorizations, nil
}
func (handler *Handler) persistAndWriteToken(w http.ResponseWriter, tokenData *portainer.TokenData) *httperror.HandlerError {
token, err := handler.JWTService.GenerateToken(tokenData) token, err := handler.JWTService.GenerateToken(tokenData)
if err != nil { if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to generate JWT token", err} return &httperror.HandlerError{http.StatusInternalServerError, "Unable to generate JWT token", err}

View File

@ -3,13 +3,13 @@ package auth
import ( import (
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"net/http"
"log" "log"
"net/http"
"github.com/asaskevich/govalidator" "github.com/asaskevich/govalidator"
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
type oauthPayload struct { type oauthPayload struct {
@ -113,6 +113,23 @@ func (handler *Handler) validateOAuth(w http.ResponseWriter, r *http.Request) *h
user = &portainer.User{ user = &portainer.User{
Username: username, Username: username,
Role: portainer.StandardUserRole, Role: portainer.StandardUserRole,
PortainerAuthorizations: map[portainer.Authorization]bool{
portainer.OperationPortainerDockerHubInspect: true,
portainer.OperationPortainerEndpointGroupList: true,
portainer.OperationPortainerEndpointList: true,
portainer.OperationPortainerEndpointInspect: true,
portainer.OperationPortainerEndpointExtensionAdd: true,
portainer.OperationPortainerEndpointExtensionRemove: true,
portainer.OperationPortainerExtensionList: true,
portainer.OperationPortainerMOTD: true,
portainer.OperationPortainerRegistryList: true,
portainer.OperationPortainerRegistryInspect: true,
portainer.OperationPortainerTeamList: true,
portainer.OperationPortainerTemplateList: true,
portainer.OperationPortainerTemplateInspect: true,
portainer.OperationPortainerUserList: true,
portainer.OperationPortainerUserMemberships: true,
},
} }
err = handler.UserService.CreateUser(user) err = handler.UserService.CreateUser(user)

View File

@ -0,0 +1,122 @@
package auth
import portainer "github.com/portainer/portainer/api"
func getUserEndpointAuthorizations(user *portainer.User, endpoints []portainer.Endpoint, endpointGroups []portainer.EndpointGroup, roles []portainer.Role, userMemberships []portainer.TeamMembership) portainer.EndpointAuthorizations {
endpointAuthorizations := make(portainer.EndpointAuthorizations)
groupUserAccessPolicies := map[portainer.EndpointGroupID]portainer.UserAccessPolicies{}
groupTeamAccessPolicies := map[portainer.EndpointGroupID]portainer.TeamAccessPolicies{}
for _, endpointGroup := range endpointGroups {
groupUserAccessPolicies[endpointGroup.ID] = endpointGroup.UserAccessPolicies
groupTeamAccessPolicies[endpointGroup.ID] = endpointGroup.TeamAccessPolicies
}
for _, endpoint := range endpoints {
authorizations := getAuthorizationsFromUserEndpointPolicy(user, &endpoint, roles)
if len(authorizations) > 0 {
endpointAuthorizations[endpoint.ID] = authorizations
continue
}
authorizations = getAuthorizationsFromUserEndpointGroupPolicy(user, &endpoint, roles, groupUserAccessPolicies)
if len(authorizations) > 0 {
endpointAuthorizations[endpoint.ID] = authorizations
continue
}
authorizations = getAuthorizationsFromTeamEndpointPolicies(userMemberships, &endpoint, roles)
if len(authorizations) > 0 {
endpointAuthorizations[endpoint.ID] = authorizations
continue
}
endpointAuthorizations[endpoint.ID] = getAuthorizationsFromTeamEndpointGroupPolicies(userMemberships, &endpoint, roles, groupTeamAccessPolicies)
}
return endpointAuthorizations
}
func getAuthorizationsFromUserEndpointPolicy(user *portainer.User, endpoint *portainer.Endpoint, roles []portainer.Role) portainer.Authorizations {
policyRoles := make([]portainer.RoleID, 0)
policy, ok := endpoint.UserAccessPolicies[user.ID]
if ok {
policyRoles = append(policyRoles, policy.RoleID)
}
return getAuthorizationsFromRoles(policyRoles, roles)
}
func getAuthorizationsFromUserEndpointGroupPolicy(user *portainer.User, endpoint *portainer.Endpoint, roles []portainer.Role, groupAccessPolicies map[portainer.EndpointGroupID]portainer.UserAccessPolicies) portainer.Authorizations {
policyRoles := make([]portainer.RoleID, 0)
policy, ok := groupAccessPolicies[endpoint.GroupID][user.ID]
if ok {
policyRoles = append(policyRoles, policy.RoleID)
}
return getAuthorizationsFromRoles(policyRoles, roles)
}
func getAuthorizationsFromTeamEndpointPolicies(memberships []portainer.TeamMembership, endpoint *portainer.Endpoint, roles []portainer.Role) portainer.Authorizations {
policyRoles := make([]portainer.RoleID, 0)
for _, membership := range memberships {
policy, ok := endpoint.TeamAccessPolicies[membership.TeamID]
if ok {
policyRoles = append(policyRoles, policy.RoleID)
}
}
return getAuthorizationsFromRoles(policyRoles, roles)
}
func getAuthorizationsFromTeamEndpointGroupPolicies(memberships []portainer.TeamMembership, endpoint *portainer.Endpoint, roles []portainer.Role, groupAccessPolicies map[portainer.EndpointGroupID]portainer.TeamAccessPolicies) portainer.Authorizations {
policyRoles := make([]portainer.RoleID, 0)
for _, membership := range memberships {
policy, ok := groupAccessPolicies[endpoint.GroupID][membership.TeamID]
if ok {
policyRoles = append(policyRoles, policy.RoleID)
}
}
return getAuthorizationsFromRoles(policyRoles, roles)
}
func getAuthorizationsFromRoles(roleIdentifiers []portainer.RoleID, roles []portainer.Role) portainer.Authorizations {
var roleAuthorizations []portainer.Authorizations
for _, id := range roleIdentifiers {
for _, role := range roles {
if role.ID == id {
roleAuthorizations = append(roleAuthorizations, role.Authorizations)
break
}
}
}
processedAuthorizations := make(portainer.Authorizations)
if len(roleAuthorizations) > 0 {
processedAuthorizations = roleAuthorizations[0]
for idx, authorizations := range roleAuthorizations {
if idx == 0 {
continue
}
processedAuthorizations = mergeAuthorizations(processedAuthorizations, authorizations)
}
}
return processedAuthorizations
}
func mergeAuthorizations(a, b portainer.Authorizations) portainer.Authorizations {
c := make(map[portainer.Authorization]bool)
for k := range b {
if _, ok := a[k]; ok {
c[k] = true
}
}
return c
}

View File

@ -5,9 +5,9 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/http/proxy" "github.com/portainer/portainer/api/http/proxy"
"github.com/portainer/portainer/http/security" "github.com/portainer/portainer/api/http/security"
) )
const ( const (
@ -30,6 +30,9 @@ type Handler struct {
TeamService portainer.TeamService TeamService portainer.TeamService
TeamMembershipService portainer.TeamMembershipService TeamMembershipService portainer.TeamMembershipService
ExtensionService portainer.ExtensionService ExtensionService portainer.ExtensionService
EndpointService portainer.EndpointService
EndpointGroupService portainer.EndpointGroupService
RoleService portainer.RoleService
ProxyManager *proxy.Manager ProxyManager *proxy.Manager
} }

View File

@ -7,7 +7,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
type dockerhubUpdatePayload struct { type dockerhubUpdatePayload struct {

View File

@ -5,8 +5,8 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/http/security" "github.com/portainer/portainer/api/http/security"
) )
func hideFields(dockerHub *portainer.DockerHub) { func hideFields(dockerHub *portainer.DockerHub) {
@ -25,9 +25,9 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
Router: mux.NewRouter(), Router: mux.NewRouter(),
} }
h.Handle("/dockerhub", h.Handle("/dockerhub",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.dockerhubInspect))).Methods(http.MethodGet) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.dockerhubInspect))).Methods(http.MethodGet)
h.Handle("/dockerhub", h.Handle("/dockerhub",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.dockerhubUpdate))).Methods(http.MethodPut) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.dockerhubUpdate))).Methods(http.MethodPut)
return h return h
} }

View File

@ -7,7 +7,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
type endpointGroupCreatePayload struct { type endpointGroupCreatePayload struct {
@ -36,11 +36,11 @@ func (handler *Handler) endpointGroupCreate(w http.ResponseWriter, r *http.Reque
} }
endpointGroup := &portainer.EndpointGroup{ endpointGroup := &portainer.EndpointGroup{
Name: payload.Name, Name: payload.Name,
Description: payload.Description, Description: payload.Description,
AuthorizedUsers: []portainer.UserID{}, UserAccessPolicies: portainer.UserAccessPolicies{},
AuthorizedTeams: []portainer.TeamID{}, TeamAccessPolicies: portainer.TeamAccessPolicies{},
Tags: payload.Tags, Tags: payload.Tags,
} }
err = handler.EndpointGroupService.CreateEndpointGroup(endpointGroup) err = handler.EndpointGroupService.CreateEndpointGroup(endpointGroup)

View File

@ -6,7 +6,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// DELETE request on /api/endpoint_groups/:id // DELETE request on /api/endpoint_groups/:id

View File

@ -6,7 +6,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// GET request on /api/endpoint_groups/:id // GET request on /api/endpoint_groups/:id

View File

@ -5,7 +5,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer/http/security" "github.com/portainer/portainer/api/http/security"
) )
// GET request on /api/endpoint_groups // GET request on /api/endpoint_groups

View File

@ -6,7 +6,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
type endpointGroupUpdatePayload struct { type endpointGroupUpdatePayload struct {
@ -14,6 +14,8 @@ type endpointGroupUpdatePayload struct {
Description string Description string
AssociatedEndpoints []portainer.EndpointID AssociatedEndpoints []portainer.EndpointID
Tags []string Tags []string
UserAccessPolicies portainer.UserAccessPolicies
TeamAccessPolicies portainer.TeamAccessPolicies
} }
func (payload *endpointGroupUpdatePayload) Validate(r *http.Request) error { func (payload *endpointGroupUpdatePayload) Validate(r *http.Request) error {
@ -52,20 +54,30 @@ func (handler *Handler) endpointGroupUpdate(w http.ResponseWriter, r *http.Reque
endpointGroup.Tags = payload.Tags endpointGroup.Tags = payload.Tags
} }
if payload.UserAccessPolicies != nil {
endpointGroup.UserAccessPolicies = payload.UserAccessPolicies
}
if payload.TeamAccessPolicies != nil {
endpointGroup.TeamAccessPolicies = payload.TeamAccessPolicies
}
err = handler.EndpointGroupService.UpdateEndpointGroup(endpointGroup.ID, endpointGroup) err = handler.EndpointGroupService.UpdateEndpointGroup(endpointGroup.ID, endpointGroup)
if err != nil { if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint group changes inside the database", err} return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint group changes inside the database", err}
} }
endpoints, err := handler.EndpointService.Endpoints() if payload.AssociatedEndpoints != nil {
if err != nil { endpoints, err := handler.EndpointService.Endpoints()
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from the database", err}
}
for _, endpoint := range endpoints {
err = handler.updateEndpointGroup(endpoint, portainer.EndpointGroupID(endpointGroupID), payload.AssociatedEndpoints)
if err != nil { if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to update endpoint", err} return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from the database", err}
}
for _, endpoint := range endpoints {
err = handler.updateEndpointGroup(endpoint, portainer.EndpointGroupID(endpointGroupID), payload.AssociatedEndpoints)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to update endpoint", err}
}
} }
} }

View File

@ -1,63 +0,0 @@
package endpointgroups
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
"github.com/portainer/portainer"
)
type endpointGroupUpdateAccessPayload struct {
AuthorizedUsers []int
AuthorizedTeams []int
}
func (payload *endpointGroupUpdateAccessPayload) Validate(r *http.Request) error {
return nil
}
// PUT request on /api/endpoint_groups/:id/access
func (handler *Handler) endpointGroupUpdateAccess(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
endpointGroupID, err := request.RetrieveNumericRouteVariableValue(r, "id")
if err != nil {
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint group identifier route variable", err}
}
var payload endpointGroupUpdateAccessPayload
err = request.DecodeAndValidateJSONPayload(r, &payload)
if err != nil {
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
}
endpointGroup, err := handler.EndpointGroupService.EndpointGroup(portainer.EndpointGroupID(endpointGroupID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint group with the specified identifier inside the database", err}
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint group with the specified identifier inside the database", err}
}
if payload.AuthorizedUsers != nil {
authorizedUserIDs := []portainer.UserID{}
for _, value := range payload.AuthorizedUsers {
authorizedUserIDs = append(authorizedUserIDs, portainer.UserID(value))
}
endpointGroup.AuthorizedUsers = authorizedUserIDs
}
if payload.AuthorizedTeams != nil {
authorizedTeamIDs := []portainer.TeamID{}
for _, value := range payload.AuthorizedTeams {
authorizedTeamIDs = append(authorizedTeamIDs, portainer.TeamID(value))
}
endpointGroup.AuthorizedTeams = authorizedTeamIDs
}
err = handler.EndpointGroupService.UpdateEndpointGroup(endpointGroup.ID, endpointGroup)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint group changes inside the database", err}
}
return response.JSON(w, endpointGroup)
}

View File

@ -5,8 +5,8 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/http/security" "github.com/portainer/portainer/api/http/security"
) )
// Handler is the HTTP handler used to handle endpoint group operations. // Handler is the HTTP handler used to handle endpoint group operations.
@ -22,17 +22,15 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
Router: mux.NewRouter(), Router: mux.NewRouter(),
} }
h.Handle("/endpoint_groups", h.Handle("/endpoint_groups",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointGroupCreate))).Methods(http.MethodPost) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointGroupCreate))).Methods(http.MethodPost)
h.Handle("/endpoint_groups", h.Handle("/endpoint_groups",
bouncer.RestrictedAccess(httperror.LoggerHandler(h.endpointGroupList))).Methods(http.MethodGet) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointGroupList))).Methods(http.MethodGet)
h.Handle("/endpoint_groups/{id}", h.Handle("/endpoint_groups/{id}",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointGroupInspect))).Methods(http.MethodGet) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointGroupInspect))).Methods(http.MethodGet)
h.Handle("/endpoint_groups/{id}", h.Handle("/endpoint_groups/{id}",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointGroupUpdate))).Methods(http.MethodPut) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointGroupUpdate))).Methods(http.MethodPut)
h.Handle("/endpoint_groups/{id}/access",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointGroupUpdateAccess))).Methods(http.MethodPut)
h.Handle("/endpoint_groups/{id}", h.Handle("/endpoint_groups/{id}",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointGroupDelete))).Methods(http.MethodDelete) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointGroupDelete))).Methods(http.MethodDelete)
return h return h
} }

View File

@ -3,9 +3,9 @@ package endpointproxy
import ( import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/http/proxy" "github.com/portainer/portainer/api/http/proxy"
"github.com/portainer/portainer/http/security" "github.com/portainer/portainer/api/http/security"
) )
// Handler is the HTTP handler used to proxy requests to external APIs. // Handler is the HTTP handler used to proxy requests to external APIs.
@ -23,10 +23,10 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
requestBouncer: bouncer, requestBouncer: bouncer,
} }
h.PathPrefix("/{id}/azure").Handler( h.PathPrefix("/{id}/azure").Handler(
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.proxyRequestsToAzureAPI))) bouncer.RestrictedAccess(httperror.LoggerHandler(h.proxyRequestsToAzureAPI)))
h.PathPrefix("/{id}/docker").Handler( h.PathPrefix("/{id}/docker").Handler(
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.proxyRequestsToDockerAPI))) bouncer.RestrictedAccess(httperror.LoggerHandler(h.proxyRequestsToDockerAPI)))
h.PathPrefix("/{id}/extensions/storidge").Handler( h.PathPrefix("/{id}/storidge").Handler(
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.proxyRequestsToStoridgeAPI))) bouncer.RestrictedAccess(httperror.LoggerHandler(h.proxyRequestsToStoridgeAPI)))
return h return h
} }

View File

@ -5,7 +5,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"net/http" "net/http"
) )
@ -23,9 +23,9 @@ func (handler *Handler) proxyRequestsToAzureAPI(w http.ResponseWriter, r *http.R
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err}
} }
err = handler.requestBouncer.EndpointAccess(r, endpoint) err = handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint, false)
if err != nil { if err != nil {
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", portainer.ErrEndpointAccessDenied} return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", err}
} }
var proxy http.Handler var proxy http.Handler

View File

@ -6,7 +6,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"net/http" "net/http"
) )
@ -28,9 +28,9 @@ func (handler *Handler) proxyRequestsToDockerAPI(w http.ResponseWriter, r *http.
return &httperror.HandlerError{http.StatusServiceUnavailable, "Unable to query endpoint", errors.New("Endpoint is down")} return &httperror.HandlerError{http.StatusServiceUnavailable, "Unable to query endpoint", errors.New("Endpoint is down")}
} }
err = handler.requestBouncer.EndpointAccess(r, endpoint) err = handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint, true)
if err != nil { if err != nil {
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", portainer.ErrEndpointAccessDenied} return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", err}
} }
var proxy http.Handler var proxy http.Handler

View File

@ -7,7 +7,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"net/http" "net/http"
) )
@ -25,9 +25,9 @@ func (handler *Handler) proxyRequestsToStoridgeAPI(w http.ResponseWriter, r *htt
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err}
} }
err = handler.requestBouncer.EndpointAccess(r, endpoint) err = handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint, false)
if err != nil { if err != nil {
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", portainer.ErrEndpointAccessDenied} return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", err}
} }
var storidgeExtension *portainer.EndpointExtension var storidgeExtension *portainer.EndpointExtension
@ -41,7 +41,7 @@ func (handler *Handler) proxyRequestsToStoridgeAPI(w http.ResponseWriter, r *htt
return &httperror.HandlerError{http.StatusServiceUnavailable, "Storidge extension not supported on this endpoint", portainer.ErrEndpointExtensionNotSupported} return &httperror.HandlerError{http.StatusServiceUnavailable, "Storidge extension not supported on this endpoint", portainer.ErrEndpointExtensionNotSupported}
} }
proxyExtensionKey := string(endpoint.ID) + "_" + string(portainer.StoridgeEndpointExtension) proxyExtensionKey := strconv.Itoa(endpointID) + "_" + strconv.Itoa(int(portainer.StoridgeEndpointExtension)) + "_" + storidgeExtension.URL
var proxy http.Handler var proxy http.Handler
proxy = handler.ProxyManager.GetLegacyExtensionProxy(proxyExtensionKey) proxy = handler.ProxyManager.GetLegacyExtensionProxy(proxyExtensionKey)
@ -53,6 +53,6 @@ func (handler *Handler) proxyRequestsToStoridgeAPI(w http.ResponseWriter, r *htt
} }
id := strconv.Itoa(endpointID) id := strconv.Itoa(endpointID)
http.StripPrefix("/"+id+"/extensions/storidge", proxy).ServeHTTP(w, r) http.StripPrefix("/"+id+"/storidge", proxy).ServeHTTP(w, r)
return nil return nil
} }

View File

@ -9,9 +9,9 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/crypto" "github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/http/client" "github.com/portainer/portainer/api/http/client"
) )
type endpointCreatePayload struct { type endpointCreatePayload struct {
@ -172,19 +172,19 @@ func (handler *Handler) createAzureEndpoint(payload *endpointCreatePayload) (*po
endpointID := handler.EndpointService.GetNextIdentifier() endpointID := handler.EndpointService.GetNextIdentifier()
endpoint := &portainer.Endpoint{ endpoint := &portainer.Endpoint{
ID: portainer.EndpointID(endpointID), ID: portainer.EndpointID(endpointID),
Name: payload.Name, Name: payload.Name,
URL: "https://management.azure.com", URL: "https://management.azure.com",
Type: portainer.AzureEnvironment, Type: portainer.AzureEnvironment,
GroupID: portainer.EndpointGroupID(payload.GroupID), GroupID: portainer.EndpointGroupID(payload.GroupID),
PublicURL: payload.PublicURL, PublicURL: payload.PublicURL,
AuthorizedUsers: []portainer.UserID{}, UserAccessPolicies: portainer.UserAccessPolicies{},
AuthorizedTeams: []portainer.TeamID{}, TeamAccessPolicies: portainer.TeamAccessPolicies{},
Extensions: []portainer.EndpointExtension{}, Extensions: []portainer.EndpointExtension{},
AzureCredentials: credentials, AzureCredentials: credentials,
Tags: payload.Tags, Tags: payload.Tags,
Status: portainer.EndpointStatusUp, Status: portainer.EndpointStatusUp,
Snapshots: []portainer.Snapshot{}, Snapshots: []portainer.Snapshot{},
} }
err = handler.EndpointService.CreateEndpoint(endpoint) err = handler.EndpointService.CreateEndpoint(endpoint)
@ -224,12 +224,12 @@ func (handler *Handler) createUnsecuredEndpoint(payload *endpointCreatePayload)
TLSConfig: portainer.TLSConfiguration{ TLSConfig: portainer.TLSConfiguration{
TLS: false, TLS: false,
}, },
AuthorizedUsers: []portainer.UserID{}, UserAccessPolicies: portainer.UserAccessPolicies{},
AuthorizedTeams: []portainer.TeamID{}, TeamAccessPolicies: portainer.TeamAccessPolicies{},
Extensions: []portainer.EndpointExtension{}, Extensions: []portainer.EndpointExtension{},
Tags: payload.Tags, Tags: payload.Tags,
Status: portainer.EndpointStatusUp, Status: portainer.EndpointStatusUp,
Snapshots: []portainer.Snapshot{}, Snapshots: []portainer.Snapshot{},
} }
err := handler.snapshotAndPersistEndpoint(endpoint) err := handler.snapshotAndPersistEndpoint(endpoint)
@ -268,12 +268,12 @@ func (handler *Handler) createTLSSecuredEndpoint(payload *endpointCreatePayload)
TLS: payload.TLS, TLS: payload.TLS,
TLSSkipVerify: payload.TLSSkipVerify, TLSSkipVerify: payload.TLSSkipVerify,
}, },
AuthorizedUsers: []portainer.UserID{}, UserAccessPolicies: portainer.UserAccessPolicies{},
AuthorizedTeams: []portainer.TeamID{}, TeamAccessPolicies: portainer.TeamAccessPolicies{},
Extensions: []portainer.EndpointExtension{}, Extensions: []portainer.EndpointExtension{},
Tags: payload.Tags, Tags: payload.Tags,
Status: portainer.EndpointStatusUp, Status: portainer.EndpointStatusUp,
Snapshots: []portainer.Snapshot{}, Snapshots: []portainer.Snapshot{},
} }
filesystemError := handler.storeTLSFiles(endpoint, payload) filesystemError := handler.storeTLSFiles(endpoint, payload)

View File

@ -7,7 +7,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// DELETE request on /api/endpoints/:id // DELETE request on /api/endpoints/:id

View File

@ -9,7 +9,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
type endpointExtensionAddPayload struct { type endpointExtensionAddPayload struct {
@ -50,9 +50,9 @@ func (handler *Handler) endpointExtensionAdd(w http.ResponseWriter, r *http.Requ
extensionType := portainer.EndpointExtensionType(payload.Type) extensionType := portainer.EndpointExtensionType(payload.Type)
var extension *portainer.EndpointExtension var extension *portainer.EndpointExtension
for _, ext := range endpoint.Extensions { for idx := range endpoint.Extensions {
if ext.Type == extensionType { if endpoint.Extensions[idx].Type == extensionType {
extension = &ext extension = &endpoint.Extensions[idx]
} }
} }

View File

@ -8,7 +8,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// DELETE request on /api/endpoints/:id/extensions/:extensionType // DELETE request on /api/endpoints/:id/extensions/:extensionType

View File

@ -6,7 +6,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// GET request on /api/endpoints/:id // GET request on /api/endpoints/:id
@ -23,9 +23,9 @@ func (handler *Handler) endpointInspect(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err}
} }
err = handler.requestBouncer.EndpointAccess(r, endpoint) err = handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint, false)
if err != nil { if err != nil {
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", portainer.ErrEndpointAccessDenied} return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", err}
} }
hideFields(endpoint) hideFields(endpoint)

View File

@ -8,7 +8,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
type endpointJobFromFilePayload struct { type endpointJobFromFilePayload struct {
@ -70,11 +70,6 @@ func (handler *Handler) endpointJob(w http.ResponseWriter, r *http.Request) *htt
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err}
} }
err = handler.requestBouncer.EndpointAccess(r, endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", portainer.ErrEndpointAccessDenied}
}
switch method { switch method {
case "file": case "file":
return handler.executeJobFromFile(w, r, endpoint, nodeName) return handler.executeJobFromFile(w, r, endpoint, nodeName)

View File

@ -5,7 +5,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer/http/security" "github.com/portainer/portainer/api/http/security"
) )
// GET request on /api/endpoints // GET request on /api/endpoints

View File

@ -6,7 +6,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// POST request on /api/endpoints/:id/snapshot // POST request on /api/endpoints/:id/snapshot

View File

@ -6,7 +6,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// POST request on /api/endpoints/snapshot // POST request on /api/endpoints/snapshot

View File

@ -7,8 +7,8 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/http/client" "github.com/portainer/portainer/api/http/client"
) )
type endpointUpdatePayload struct { type endpointUpdatePayload struct {
@ -24,6 +24,8 @@ type endpointUpdatePayload struct {
AzureTenantID *string AzureTenantID *string
AzureAuthenticationKey *string AzureAuthenticationKey *string
Tags []string Tags []string
UserAccessPolicies portainer.UserAccessPolicies
TeamAccessPolicies portainer.TeamAccessPolicies
} }
func (payload *endpointUpdatePayload) Validate(r *http.Request) error { func (payload *endpointUpdatePayload) Validate(r *http.Request) error {
@ -74,6 +76,14 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
endpoint.Tags = payload.Tags endpoint.Tags = payload.Tags
} }
if payload.UserAccessPolicies != nil {
endpoint.UserAccessPolicies = payload.UserAccessPolicies
}
if payload.TeamAccessPolicies != nil {
endpoint.TeamAccessPolicies = payload.TeamAccessPolicies
}
if payload.Status != nil { if payload.Status != nil {
switch *payload.Status { switch *payload.Status {
case 1: case 1:

View File

@ -1,67 +0,0 @@
package endpoints
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
"github.com/portainer/portainer"
)
type endpointUpdateAccessPayload struct {
AuthorizedUsers []int
AuthorizedTeams []int
}
func (payload *endpointUpdateAccessPayload) Validate(r *http.Request) error {
return nil
}
// PUT request on /api/endpoints/:id/access
func (handler *Handler) endpointUpdateAccess(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
if !handler.authorizeEndpointManagement {
return &httperror.HandlerError{http.StatusServiceUnavailable, "Endpoint management is disabled", ErrEndpointManagementDisabled}
}
endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id")
if err != nil {
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
var payload endpointUpdateAccessPayload
err = request.DecodeAndValidateJSONPayload(r, &payload)
if err != nil {
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
}
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err}
}
if payload.AuthorizedUsers != nil {
authorizedUserIDs := []portainer.UserID{}
for _, value := range payload.AuthorizedUsers {
authorizedUserIDs = append(authorizedUserIDs, portainer.UserID(value))
}
endpoint.AuthorizedUsers = authorizedUserIDs
}
if payload.AuthorizedTeams != nil {
authorizedTeamIDs := []portainer.TeamID{}
for _, value := range payload.AuthorizedTeams {
authorizedTeamIDs = append(authorizedTeamIDs, portainer.TeamID(value))
}
endpoint.AuthorizedTeams = authorizedTeamIDs
}
err = handler.EndpointService.UpdateEndpoint(endpoint.ID, endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint changes inside the database", err}
}
return response.JSON(w, endpoint)
}

View File

@ -2,9 +2,9 @@ package endpoints
import ( import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/http/proxy" "github.com/portainer/portainer/api/http/proxy"
"github.com/portainer/portainer/http/security" "github.com/portainer/portainer/api/http/security"
"net/http" "net/http"
@ -37,32 +37,30 @@ type Handler struct {
// NewHandler creates a handler to manage endpoint operations. // NewHandler creates a handler to manage endpoint operations.
func NewHandler(bouncer *security.RequestBouncer, authorizeEndpointManagement bool) *Handler { func NewHandler(bouncer *security.RequestBouncer, authorizeEndpointManagement bool) *Handler {
h := &Handler{ h := &Handler{
Router: mux.NewRouter(), Router: mux.NewRouter(),
authorizeEndpointManagement: authorizeEndpointManagement, authorizeEndpointManagement: authorizeEndpointManagement,
requestBouncer: bouncer, requestBouncer: bouncer,
} }
h.Handle("/endpoints", h.Handle("/endpoints",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointCreate))).Methods(http.MethodPost) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointCreate))).Methods(http.MethodPost)
h.Handle("/endpoints/snapshot", h.Handle("/endpoints/snapshot",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointSnapshots))).Methods(http.MethodPost) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointSnapshots))).Methods(http.MethodPost)
h.Handle("/endpoints", h.Handle("/endpoints",
bouncer.RestrictedAccess(httperror.LoggerHandler(h.endpointList))).Methods(http.MethodGet) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointList))).Methods(http.MethodGet)
h.Handle("/endpoints/{id}", h.Handle("/endpoints/{id}",
bouncer.RestrictedAccess(httperror.LoggerHandler(h.endpointInspect))).Methods(http.MethodGet) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointInspect))).Methods(http.MethodGet)
h.Handle("/endpoints/{id}", h.Handle("/endpoints/{id}",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointUpdate))).Methods(http.MethodPut) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointUpdate))).Methods(http.MethodPut)
h.Handle("/endpoints/{id}/access",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointUpdateAccess))).Methods(http.MethodPut)
h.Handle("/endpoints/{id}", h.Handle("/endpoints/{id}",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointDelete))).Methods(http.MethodDelete) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointDelete))).Methods(http.MethodDelete)
h.Handle("/endpoints/{id}/extensions", h.Handle("/endpoints/{id}/extensions",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.endpointExtensionAdd))).Methods(http.MethodPost) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointExtensionAdd))).Methods(http.MethodPost)
h.Handle("/endpoints/{id}/extensions/{extensionType}", h.Handle("/endpoints/{id}/extensions/{extensionType}",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.endpointExtensionRemove))).Methods(http.MethodDelete) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointExtensionRemove))).Methods(http.MethodDelete)
h.Handle("/endpoints/{id}/job", h.Handle("/endpoints/{id}/job",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointJob))).Methods(http.MethodPost) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointJob))).Methods(http.MethodPost)
h.Handle("/endpoints/{id}/snapshot", h.Handle("/endpoints/{id}/snapshot",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointSnapshot))).Methods(http.MethodPost) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.endpointSnapshot))).Methods(http.MethodPost)
return h return h
} }

View File

@ -8,7 +8,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
type extensionCreatePayload struct { type extensionCreatePayload struct {
@ -70,6 +70,13 @@ func (handler *Handler) extensionCreate(w http.ResponseWriter, r *http.Request)
extension.Enabled = true extension.Enabled = true
if extension.ID == portainer.RBACExtension {
err = handler.upgradeRBACData()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "An error occured during database update", err}
}
}
err = handler.ExtensionService.Persist(extension) err = handler.ExtensionService.Persist(extension)
if err != nil { if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist extension status inside the database", err} return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist extension status inside the database", err}

View File

@ -6,7 +6,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// DELETE request on /api/extensions/:id // DELETE request on /api/extensions/:id

View File

@ -8,8 +8,8 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/http/client" "github.com/portainer/portainer/api/http/client"
) )
// GET request on /api/extensions/:id // GET request on /api/extensions/:id

View File

@ -7,7 +7,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// GET request on /api/extensions?store=<store> // GET request on /api/extensions?store=<store>

View File

@ -7,7 +7,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
type extensionUpdatePayload struct { type extensionUpdatePayload struct {

View File

@ -5,15 +5,18 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/http/security" "github.com/portainer/portainer/api/http/security"
) )
// Handler is the HTTP handler used to handle extension operations. // Handler is the HTTP handler used to handle extension operations.
type Handler struct { type Handler struct {
*mux.Router *mux.Router
ExtensionService portainer.ExtensionService ExtensionService portainer.ExtensionService
ExtensionManager portainer.ExtensionManager ExtensionManager portainer.ExtensionManager
EndpointGroupService portainer.EndpointGroupService
EndpointService portainer.EndpointService
RegistryService portainer.RegistryService
} }
// NewHandler creates a handler to manage extension operations. // NewHandler creates a handler to manage extension operations.
@ -23,15 +26,15 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
} }
h.Handle("/extensions", h.Handle("/extensions",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.extensionList))).Methods(http.MethodGet) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.extensionList))).Methods(http.MethodGet)
h.Handle("/extensions", h.Handle("/extensions",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.extensionCreate))).Methods(http.MethodPost) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.extensionCreate))).Methods(http.MethodPost)
h.Handle("/extensions/{id}", h.Handle("/extensions/{id}",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.extensionInspect))).Methods(http.MethodGet) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.extensionInspect))).Methods(http.MethodGet)
h.Handle("/extensions/{id}", h.Handle("/extensions/{id}",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.extensionDelete))).Methods(http.MethodDelete) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.extensionDelete))).Methods(http.MethodDelete)
h.Handle("/extensions/{id}/update", h.Handle("/extensions/{id}/update",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.extensionUpdate))).Methods(http.MethodPost) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.extensionUpdate))).Methods(http.MethodPost)
return h return h
} }

View File

@ -0,0 +1,59 @@
package extensions
import portainer "github.com/portainer/portainer/api"
func updateUserAccessPolicyToReadOnlyRole(policies portainer.UserAccessPolicies, key portainer.UserID) {
tmp := policies[key]
tmp.RoleID = 4
policies[key] = tmp
}
func updateTeamAccessPolicyToReadOnlyRole(policies portainer.TeamAccessPolicies, key portainer.TeamID) {
tmp := policies[key]
tmp.RoleID = 4
policies[key] = tmp
}
func (handler *Handler) upgradeRBACData() error {
endpointGroups, err := handler.EndpointGroupService.EndpointGroups()
if err != nil {
return err
}
for _, endpointGroup := range endpointGroups {
for key := range endpointGroup.UserAccessPolicies {
updateUserAccessPolicyToReadOnlyRole(endpointGroup.UserAccessPolicies, key)
}
for key := range endpointGroup.TeamAccessPolicies {
updateTeamAccessPolicyToReadOnlyRole(endpointGroup.TeamAccessPolicies, key)
}
err := handler.EndpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
if err != nil {
return err
}
}
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return err
}
for _, endpoint := range endpoints {
for key := range endpoint.UserAccessPolicies {
updateUserAccessPolicyToReadOnlyRole(endpoint.UserAccessPolicies, key)
}
for key := range endpoint.TeamAccessPolicies {
updateTeamAccessPolicyToReadOnlyRole(endpoint.TeamAccessPolicies, key)
}
err := handler.EndpointService.UpdateEndpoint(endpoint.ID, &endpoint)
if err != nil {
return err
}
}
return nil
}

View File

@ -4,34 +4,36 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/portainer/portainer/http/handler/auth" "github.com/portainer/portainer/api/http/handler/schedules"
"github.com/portainer/portainer/http/handler/dockerhub"
"github.com/portainer/portainer/http/handler/endpointgroups" "github.com/portainer/portainer/api/http/handler/roles"
"github.com/portainer/portainer/http/handler/endpointproxy"
"github.com/portainer/portainer/http/handler/endpoints" "github.com/portainer/portainer/api/http/handler/auth"
"github.com/portainer/portainer/http/handler/extensions" "github.com/portainer/portainer/api/http/handler/dockerhub"
"github.com/portainer/portainer/http/handler/file" "github.com/portainer/portainer/api/http/handler/endpointgroups"
"github.com/portainer/portainer/http/handler/motd" "github.com/portainer/portainer/api/http/handler/endpointproxy"
"github.com/portainer/portainer/http/handler/registries" "github.com/portainer/portainer/api/http/handler/endpoints"
"github.com/portainer/portainer/http/handler/resourcecontrols" "github.com/portainer/portainer/api/http/handler/extensions"
"github.com/portainer/portainer/http/handler/schedules" "github.com/portainer/portainer/api/http/handler/file"
"github.com/portainer/portainer/http/handler/settings" "github.com/portainer/portainer/api/http/handler/motd"
"github.com/portainer/portainer/http/handler/stacks" "github.com/portainer/portainer/api/http/handler/registries"
"github.com/portainer/portainer/http/handler/status" "github.com/portainer/portainer/api/http/handler/resourcecontrols"
"github.com/portainer/portainer/http/handler/tags" "github.com/portainer/portainer/api/http/handler/settings"
"github.com/portainer/portainer/http/handler/teammemberships" "github.com/portainer/portainer/api/http/handler/stacks"
"github.com/portainer/portainer/http/handler/teams" "github.com/portainer/portainer/api/http/handler/status"
"github.com/portainer/portainer/http/handler/templates" "github.com/portainer/portainer/api/http/handler/tags"
"github.com/portainer/portainer/http/handler/upload" "github.com/portainer/portainer/api/http/handler/teammemberships"
"github.com/portainer/portainer/http/handler/users" "github.com/portainer/portainer/api/http/handler/teams"
"github.com/portainer/portainer/http/handler/webhooks" "github.com/portainer/portainer/api/http/handler/templates"
"github.com/portainer/portainer/http/handler/websocket" "github.com/portainer/portainer/api/http/handler/upload"
"github.com/portainer/portainer/api/http/handler/users"
"github.com/portainer/portainer/api/http/handler/webhooks"
"github.com/portainer/portainer/api/http/handler/websocket"
) )
// Handler is a collection of all the service handlers. // Handler is a collection of all the service handlers.
type Handler struct { type Handler struct {
AuthHandler *auth.Handler AuthHandler *auth.Handler
DockerHubHandler *dockerhub.Handler DockerHubHandler *dockerhub.Handler
EndpointGroupHandler *endpointgroups.Handler EndpointGroupHandler *endpointgroups.Handler
EndpointHandler *endpoints.Handler EndpointHandler *endpoints.Handler
@ -41,6 +43,8 @@ type Handler struct {
ExtensionHandler *extensions.Handler ExtensionHandler *extensions.Handler
RegistryHandler *registries.Handler RegistryHandler *registries.Handler
ResourceControlHandler *resourcecontrols.Handler ResourceControlHandler *resourcecontrols.Handler
RoleHandler *roles.Handler
SchedulesHanlder *schedules.Handler
SettingsHandler *settings.Handler SettingsHandler *settings.Handler
StackHandler *stacks.Handler StackHandler *stacks.Handler
StatusHandler *status.Handler StatusHandler *status.Handler
@ -52,7 +56,6 @@ type Handler struct {
UserHandler *users.Handler UserHandler *users.Handler
WebSocketHandler *websocket.Handler WebSocketHandler *websocket.Handler
WebhookHandler *webhooks.Handler WebhookHandler *webhooks.Handler
SchedulesHanlder *schedules.Handler
} }
// ServeHTTP delegates a request to the appropriate subhandler. // ServeHTTP delegates a request to the appropriate subhandler.
@ -68,21 +71,25 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch { switch {
case strings.Contains(r.URL.Path, "/docker/"): case strings.Contains(r.URL.Path, "/docker/"):
http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r) http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r)
case strings.Contains(r.URL.Path, "/extensions/storidge"): case strings.Contains(r.URL.Path, "/storidge/"):
http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r) http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r)
case strings.Contains(r.URL.Path, "/azure/"): case strings.Contains(r.URL.Path, "/azure/"):
http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r) http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r)
default: default:
http.StripPrefix("/api", h.EndpointHandler).ServeHTTP(w, r) http.StripPrefix("/api", h.EndpointHandler).ServeHTTP(w, r)
} }
case strings.HasPrefix(r.URL.Path, "/api/motd"):
http.StripPrefix("/api", h.MOTDHandler).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/extensions"): case strings.HasPrefix(r.URL.Path, "/api/extensions"):
http.StripPrefix("/api", h.ExtensionHandler).ServeHTTP(w, r) http.StripPrefix("/api", h.ExtensionHandler).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/motd"):
http.StripPrefix("/api", h.MOTDHandler).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/registries"): case strings.HasPrefix(r.URL.Path, "/api/registries"):
http.StripPrefix("/api", h.RegistryHandler).ServeHTTP(w, r) http.StripPrefix("/api", h.RegistryHandler).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/resource_controls"): case strings.HasPrefix(r.URL.Path, "/api/resource_controls"):
http.StripPrefix("/api", h.ResourceControlHandler).ServeHTTP(w, r) http.StripPrefix("/api", h.ResourceControlHandler).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/roles"):
http.StripPrefix("/api", h.RoleHandler).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/schedules"):
http.StripPrefix("/api", h.SchedulesHanlder).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/settings"): case strings.HasPrefix(r.URL.Path, "/api/settings"):
http.StripPrefix("/api", h.SettingsHandler).ServeHTTP(w, r) http.StripPrefix("/api", h.SettingsHandler).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/stacks"): case strings.HasPrefix(r.URL.Path, "/api/stacks"):
@ -105,8 +112,6 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.StripPrefix("/api", h.WebSocketHandler).ServeHTTP(w, r) http.StripPrefix("/api", h.WebSocketHandler).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/webhooks"): case strings.HasPrefix(r.URL.Path, "/api/webhooks"):
http.StripPrefix("/api", h.WebhookHandler).ServeHTTP(w, r) http.StripPrefix("/api", h.WebhookHandler).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/schedules"):
http.StripPrefix("/api", h.SchedulesHanlder).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/"): case strings.HasPrefix(r.URL.Path, "/"):
h.FileHandler.ServeHTTP(w, r) h.FileHandler.ServeHTTP(w, r)
} }

View File

@ -4,7 +4,7 @@ import (
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/portainer/portainer/http/security" "github.com/portainer/portainer/api/http/security"
) )
// Handler is the HTTP handler used to handle MOTD operations. // Handler is the HTTP handler used to handle MOTD operations.
@ -18,7 +18,7 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
Router: mux.NewRouter(), Router: mux.NewRouter(),
} }
h.Handle("/motd", h.Handle("/motd",
bouncer.AuthenticatedAccess(http.HandlerFunc(h.motd))).Methods(http.MethodGet) bouncer.AuthorizedAccess(http.HandlerFunc(h.motd))).Methods(http.MethodGet)
return h return h
} }

View File

@ -1,34 +1,55 @@
package motd package motd
import ( import (
"encoding/json"
"net/http" "net/http"
"strings"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/crypto" "github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/http/client" "github.com/portainer/portainer/api/http/client"
) )
type motdResponse struct { type motdResponse struct {
Title string `json:"Title"` Title string `json:"Title"`
Message string `json:"Message"` Message string `json:"Message"`
Hash []byte `json:"Hash"` ContentLayout map[string]string `json:"ContentLayout"`
Style string `json:"Style"`
Hash []byte `json:"Hash"`
}
type motdData struct {
Title string `json:"title"`
Message []string `json:"message"`
ContentLayout map[string]string `json:"contentLayout"`
Style string `json:"style"`
} }
func (handler *Handler) motd(w http.ResponseWriter, r *http.Request) { func (handler *Handler) motd(w http.ResponseWriter, r *http.Request) {
motd, err := client.Get(portainer.MessageOfTheDayURL, 0) motd, err := client.Get(portainer.MessageOfTheDayURL, 0)
if err != nil { if err != nil {
response.JSON(w, &motdResponse{Message: ""}) response.JSON(w, &motdResponse{Message: ""})
return return
} }
title, err := client.Get(portainer.MessageOfTheDayTitleURL, 0) var data motdData
err = json.Unmarshal(motd, &data)
if err != nil { if err != nil {
response.JSON(w, &motdResponse{Message: ""}) response.JSON(w, &motdResponse{Message: ""})
return return
} }
hash := crypto.HashFromBytes(motd) message := strings.Join(data.Message, "\n")
response.JSON(w, &motdResponse{Title: string(title), Message: string(motd), Hash: hash})
hash := crypto.HashFromBytes([]byte(message))
resp := motdResponse{
Title: data.Title,
Message: message,
Hash: hash,
ContentLayout: data.ContentLayout,
Style: data.Style,
}
response.JSON(w, &resp)
} }

View File

@ -5,9 +5,9 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/http/proxy" "github.com/portainer/portainer/api/http/proxy"
"github.com/portainer/portainer/http/security" "github.com/portainer/portainer/api/http/security"
) )
func hideFields(registry *portainer.Registry) { func hideFields(registry *portainer.Registry) {
@ -33,19 +33,17 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
} }
h.Handle("/registries", h.Handle("/registries",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.registryCreate))).Methods(http.MethodPost) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.registryCreate))).Methods(http.MethodPost)
h.Handle("/registries", h.Handle("/registries",
bouncer.RestrictedAccess(httperror.LoggerHandler(h.registryList))).Methods(http.MethodGet) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.registryList))).Methods(http.MethodGet)
h.Handle("/registries/{id}", h.Handle("/registries/{id}",
bouncer.RestrictedAccess(httperror.LoggerHandler(h.registryInspect))).Methods(http.MethodGet) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.registryInspect))).Methods(http.MethodGet)
h.Handle("/registries/{id}", h.Handle("/registries/{id}",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.registryUpdate))).Methods(http.MethodPut) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.registryUpdate))).Methods(http.MethodPut)
h.Handle("/registries/{id}/access",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.registryUpdateAccess))).Methods(http.MethodPut)
h.Handle("/registries/{id}/configure", h.Handle("/registries/{id}/configure",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.registryConfigure))).Methods(http.MethodPost) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.registryConfigure))).Methods(http.MethodPost)
h.Handle("/registries/{id}", h.Handle("/registries/{id}",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.registryDelete))).Methods(http.MethodDelete) bouncer.AuthorizedAccess(httperror.LoggerHandler(h.registryDelete))).Methods(http.MethodDelete)
h.PathPrefix("/registries/{id}/v2").Handler( h.PathPrefix("/registries/{id}/v2").Handler(
bouncer.RestrictedAccess(httperror.LoggerHandler(h.proxyRequestsToRegistryAPI))) bouncer.RestrictedAccess(httperror.LoggerHandler(h.proxyRequestsToRegistryAPI)))

View File

@ -7,7 +7,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
// request on /api/registries/:id/v2 // request on /api/registries/:id/v2

View File

@ -7,7 +7,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
type registryConfigurePayload struct { type registryConfigurePayload struct {

View File

@ -7,7 +7,7 @@ import (
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer" "github.com/portainer/portainer/api"
) )
type registryCreatePayload struct { type registryCreatePayload struct {
@ -53,14 +53,14 @@ func (handler *Handler) registryCreate(w http.ResponseWriter, r *http.Request) *
} }
registry := &portainer.Registry{ registry := &portainer.Registry{
Type: portainer.RegistryType(payload.Type), Type: portainer.RegistryType(payload.Type),
Name: payload.Name, Name: payload.Name,
URL: payload.URL, URL: payload.URL,
Authentication: payload.Authentication, Authentication: payload.Authentication,
Username: payload.Username, Username: payload.Username,
Password: payload.Password, Password: payload.Password,
AuthorizedUsers: []portainer.UserID{}, UserAccessPolicies: portainer.UserAccessPolicies{},
AuthorizedTeams: []portainer.TeamID{}, TeamAccessPolicies: portainer.TeamAccessPolicies{},
} }
err = handler.RegistryService.CreateRegistry(registry) err = handler.RegistryService.CreateRegistry(registry)

Some files were not shown because too many files have changed in this diff Show More