Compare commits
8 Commits
develop
...
feat/EE-64
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30fc71e382 | ||
|
|
50ed81daaa | ||
|
|
c75f3ff0aa | ||
|
|
130f53cbe0 | ||
|
|
6bf3eb4afd | ||
|
|
b99cf4b3c8 | ||
|
|
0a8d722aed | ||
|
|
16507ded13 |
11
api/bolt/migrator/migrate_dbversion30.go
Normal file
11
api/bolt/migrator/migrate_dbversion30.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package migrator
|
||||
|
||||
func (m *Migrator) updateSettingsToDB31() error {
|
||||
legacySettings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
legacySettings.OAuthSettings.SSO = false
|
||||
legacySettings.OAuthSettings.LogoutURI = ""
|
||||
return m.settingsService.UpdateSettings(legacySettings)
|
||||
}
|
||||
64
api/bolt/migrator/migrate_dbversion30_test.go
Normal file
64
api/bolt/migrator/migrate_dbversion30_test.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/portainer/portainer/api/bolt/settings"
|
||||
)
|
||||
|
||||
var (
|
||||
testingDBStorePath string
|
||||
testingDBFileName string
|
||||
dummyLogoURL string
|
||||
dbConn *bolt.DB
|
||||
settingsService *settings.Service
|
||||
)
|
||||
|
||||
func setup() error {
|
||||
testingDBStorePath, _ = os.Getwd()
|
||||
testingDBFileName = "portainer-ee-mig-30.db"
|
||||
dummyLogoURL = "example.com"
|
||||
var err error
|
||||
dbConn, err = initTestingDBConn(testingDBStorePath, testingDBFileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dummySettingsObj := map[string]interface{}{
|
||||
"LogoURL": dummyLogoURL,
|
||||
}
|
||||
settingsService, err = initTestingSettingsService(dbConn, dummySettingsObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestUpdateSettingsToDB31(t *testing.T) {
|
||||
if err := setup(); err != nil {
|
||||
t.Errorf("failed to complete testing setups, err: %v", err)
|
||||
}
|
||||
defer dbConn.Close()
|
||||
defer os.Remove(testingDBFileName)
|
||||
m := &Migrator{
|
||||
db: dbConn,
|
||||
settingsService: settingsService,
|
||||
}
|
||||
if err := m.updateSettingsToDB31(); err != nil {
|
||||
t.Errorf("failed to update settings: %v", err)
|
||||
}
|
||||
updatedSettings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
t.Errorf("failed to retrieve the updated settings: %v", err)
|
||||
}
|
||||
if updatedSettings.LogoURL != dummyLogoURL {
|
||||
t.Errorf("unexpected value changes in the updated settings, want LogoURL value: %s, got LogoURL value: %s", dummyLogoURL, updatedSettings.LogoURL)
|
||||
}
|
||||
if updatedSettings.OAuthSettings.SSO != false {
|
||||
t.Errorf("unexpected default OAuth SSO setting, want: false, got: %t", updatedSettings.OAuthSettings.SSO)
|
||||
}
|
||||
if updatedSettings.OAuthSettings.LogoutURI != "" {
|
||||
t.Errorf("unexpected default OAuth HideInternalAuth setting, want:, got: %s", updatedSettings.OAuthSettings.LogoutURI)
|
||||
}
|
||||
}
|
||||
38
api/bolt/migrator/migrate_test_helper.go
Normal file
38
api/bolt/migrator/migrate_test_helper.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/portainer/portainer/api/bolt/internal"
|
||||
"github.com/portainer/portainer/api/bolt/settings"
|
||||
)
|
||||
|
||||
// initTestingDBConn creates a raw bolt DB connection
|
||||
// for unit testing usage only since using NewStore will cause cycle import inside migrator pkg
|
||||
func initTestingDBConn(storePath, fileName string) (*bolt.DB, error) {
|
||||
databasePath := path.Join(storePath, fileName)
|
||||
dbConn, err := bolt.Open(databasePath, 0600, &bolt.Options{Timeout: 1 * time.Second})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dbConn, nil
|
||||
}
|
||||
|
||||
// initTestingDBConn creates a settings service with raw bolt DB connection
|
||||
// for unit testing usage only since using NewStore will cause cycle import inside migrator pkg
|
||||
func initTestingSettingsService(dbConn *bolt.DB, preSetObj map[string]interface{}) (*settings.Service, error) {
|
||||
internalDBConn := &internal.DbConnection{
|
||||
DB: dbConn,
|
||||
}
|
||||
settingsService, err := settings.NewService(internalDBConn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//insert a obj
|
||||
if err := internal.UpdateObject(internalDBConn, "settings", []byte("SETTINGS"), preSetObj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return settingsService, nil
|
||||
}
|
||||
@@ -358,5 +358,13 @@ func (m *Migrator) Migrate() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Portainer 2.5.0
|
||||
if m.currentDBVersion < 31 {
|
||||
err := m.updateSettingsToDB31()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return m.versionService.StoreDBVersion(portainer.DBVersion)
|
||||
}
|
||||
|
||||
@@ -489,6 +489,8 @@ type (
|
||||
Scopes string `json:"Scopes"`
|
||||
OAuthAutoCreateUsers bool `json:"OAuthAutoCreateUsers"`
|
||||
DefaultTeamID TeamID `json:"DefaultTeamID"`
|
||||
SSO bool `json:"SSO"`
|
||||
LogoutURI string `json:"LogoutURI"`
|
||||
}
|
||||
|
||||
// Pair defines a key/value string pair
|
||||
@@ -1329,7 +1331,7 @@ const (
|
||||
// APIVersion is the version number of the Portainer API
|
||||
APIVersion = "2.4.0"
|
||||
// DBVersion is the version number of the Portainer database
|
||||
DBVersion = 27
|
||||
DBVersion = 31
|
||||
// ComposeSyntaxMaxVersion is a maximum supported version of the docker compose syntax
|
||||
ComposeSyntaxMaxVersion = "3.9"
|
||||
// AssetsServerURL represents the URL of the Portainer asset server
|
||||
|
||||
@@ -1,4 +1,32 @@
|
||||
<div>
|
||||
<div
|
||||
><div class="col-sm-12 form-section-title">
|
||||
Single Sign-On
|
||||
</div>
|
||||
<!-- SSO -->
|
||||
<div class="form-group">
|
||||
<label for="use-sso" class="control-label col-sm-2 text-left" style="padding-top: 0;">
|
||||
Use SSO
|
||||
<portainer-tooltip position="bottom" message="When using SSO the OAuth provider is not forced to prompt for credentials."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9">
|
||||
<label class="switch"> <input id="use-sso" type="checkbox" ng-model="$ctrl.settings.SSO" /><i></i> </label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !SSO -->
|
||||
|
||||
<!-- HideInternalAuth -->
|
||||
<div class="form-group" ng-if="$ctrl.settings.SSO">
|
||||
<label for="hide-internal-auth" class="control-label col-sm-2 text-left" style="padding-top: 0;">
|
||||
Hide internal authentication prompt
|
||||
</label>
|
||||
<div class="col-sm-9">
|
||||
<label class="switch"> <input id="hide-internal-auth" type="checkbox" disabled /><i></i> </label>
|
||||
<span class="text-muted small" style="margin-left: 15px;">
|
||||
This feature is available in <a href="https://www.portainer.io/business-upsell?from=hide-internal-auth" target="_blank"> Portainer Business Edition</a>.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !HideInternalAuth -->
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Automatic user provisioning
|
||||
</div>
|
||||
@@ -105,7 +133,18 @@
|
||||
<input type="text" class="form-control" id="oauth_redirect_uri" ng-model="$ctrl.settings.RedirectURI" placeholder="http://yourportainer.com/" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="oauth_logout_url" class="col-sm-3 col-lg-2 control-label text-left">
|
||||
Logout URL
|
||||
<portainer-tooltip
|
||||
position="bottom"
|
||||
message="URL used by Portainer to redirect the user to the OAuth provider in order to log the user out of the identity provider session."
|
||||
></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="oauth_logout_url" ng-model="$ctrl.settings.LogoutURI" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="oauth_user_identifier" class="col-sm-3 col-lg-2 control-label text-left">
|
||||
User identifier
|
||||
|
||||
@@ -2,7 +2,7 @@ import angular from 'angular';
|
||||
|
||||
class LogoutController {
|
||||
/* @ngInject */
|
||||
constructor($async, $state, $transition$, Authentication, StateManager, Notifications, LocalStorage) {
|
||||
constructor($async, $state, $transition$, $window, Authentication, StateManager, Notifications, LocalStorage, SettingsService) {
|
||||
this.$async = $async;
|
||||
this.$state = $state;
|
||||
this.$transition$ = $transition$;
|
||||
@@ -11,6 +11,7 @@ class LogoutController {
|
||||
this.StateManager = StateManager;
|
||||
this.Notifications = Notifications;
|
||||
this.LocalStorage = LocalStorage;
|
||||
this.SettingsService = SettingsService;
|
||||
|
||||
this.logo = this.StateManager.getState().application.logo;
|
||||
this.logoutAsync = this.logoutAsync.bind(this);
|
||||
@@ -24,11 +25,17 @@ class LogoutController {
|
||||
async logoutAsync() {
|
||||
const error = this.$transition$.params().error;
|
||||
const performApiLogout = this.$transition$.params().performApiLogout;
|
||||
const settings = await this.SettingsService.publicSettings();
|
||||
|
||||
try {
|
||||
await this.Authentication.logout(performApiLogout);
|
||||
} finally {
|
||||
this.LocalStorage.storeLogoutReason(error);
|
||||
this.$state.go('portainer.auth', { reload: true });
|
||||
if (settings.OAuthLogoutURI) {
|
||||
this.$window.location.href = settings.OAuthLogoutURI;
|
||||
} else {
|
||||
this.$state.go('portainer.auth', { reload: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user