Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Abdul/UI timeout fix #5362

Merged
merged 16 commits into from
Aug 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/config/shared/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ func DefaultGlobalConfig() *GlobalConfig {
BackgroundColor: w.String("#3864f2"), // Chef Success blue
TextColor: w.String("#FFFFFF"), // White
},
SessionSettings: &SessionSettings{
EnableIdleTimeout: w.Bool(false),
IdleTimeoutMinutes: w.Int32(30),
},
},
}
}
Expand Down
1,597 changes: 846 additions & 751 deletions api/config/shared/global.pb.go

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions api/config/shared/global.proto
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ message V1 {
repeated chef.automate.infra.config.FrontendTLSCredential frontend_tls = 7;
Disclosure disclosure = 8;
Banner banner = 9;
SessionSettings session_settings = 10;
}

message External {
Expand Down Expand Up @@ -250,3 +251,8 @@ message Banner {
google.protobuf.StringValue background_color = 3;
google.protobuf.StringValue text_color = 4;
}

message SessionSettings {
google.protobuf.BoolValue enable_idle_timeout = 1;
google.protobuf.Int32Value idle_timeout_minutes = 2;
}
30 changes: 21 additions & 9 deletions api/config/ui/config_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ func NewConfigRequest() *ConfigRequest {
Ngx: &ConfigRequest_V1_System_Nginx{
Http: &ConfigRequest_V1_System_Nginx_Http{},
},
Banner: &ConfigRequest_V1_Banner{},
CustomSettings: &ConfigRequest_V1_System_CustomSettings{
Banner: &ConfigRequest_V1_Banner{},
SessionSettings: &ConfigRequest_V1_SessionSettings{},
},
},
Svc: &ConfigRequest_V1_Service{},
},
Expand All @@ -36,10 +39,14 @@ func DefaultConfigRequest() *ConfigRequest {
c.V1.Sys.Ngx.Http.SslCiphers = w.String(ac.InternalCipherSuite)
c.V1.Sys.Ngx.Http.SslProtocols = w.String("TLSv1.2 TLSv1.3")

c.V1.Sys.Banner.Show = w.Bool(false)
c.V1.Sys.Banner.Message = w.String("")
c.V1.Sys.Banner.BackgroundColor = w.String("3864f2") // Chef Success blue
c.V1.Sys.Banner.TextColor = w.String("FFFFFF")
c.V1.Sys.CustomSettings.Banner.Show = w.Bool(false)
c.V1.Sys.CustomSettings.Banner.Message = w.String("")
c.V1.Sys.CustomSettings.Banner.BackgroundColor = w.String("3864f2") // Chef Success blue
c.V1.Sys.CustomSettings.Banner.TextColor = w.String("FFFFFF")

c.V1.Sys.CustomSettings.SessionSettings.EnableIdleTimeout = w.Bool(false)
c.V1.Sys.CustomSettings.SessionSettings.IdleTimeoutMinutes = w.Int32(30)

return c
}

Expand Down Expand Up @@ -80,17 +87,22 @@ func (c *ConfigRequest) SetGlobalConfig(g *ac.GlobalConfig) {
}

if g.GetV1().GetBanner().GetShow() != nil {
c.V1.Sys.Banner.Show.Value = g.GetV1().GetBanner().GetShow().GetValue()
c.V1.Sys.CustomSettings.Banner.Show.Value = g.GetV1().GetBanner().GetShow().GetValue()
if bannerMessage := g.GetV1().GetBanner().GetMessage().GetValue(); bannerMessage != "" {
c.V1.Sys.Banner.Message.Value = bannerMessage
c.V1.Sys.CustomSettings.Banner.Message.Value = bannerMessage
}

if textColor := g.GetV1().GetBanner().GetTextColor().GetValue(); textColor != "" {
c.V1.Sys.Banner.TextColor.Value = textColor
c.V1.Sys.CustomSettings.Banner.TextColor.Value = textColor
}

if backgroundColor := g.GetV1().GetBanner().GetBackgroundColor().GetValue(); backgroundColor != "" {
c.V1.Sys.Banner.BackgroundColor.Value = backgroundColor
c.V1.Sys.CustomSettings.Banner.BackgroundColor.Value = backgroundColor
}
}

if g.GetV1().GetSessionSettings().GetEnableIdleTimeout() != nil {
c.V1.Sys.CustomSettings.SessionSettings.EnableIdleTimeout = w.Bool(g.GetV1().GetSessionSettings().GetEnableIdleTimeout().GetValue())
c.V1.Sys.CustomSettings.SessionSettings.IdleTimeoutMinutes = w.Int32(g.GetV1().GetSessionSettings().GetIdleTimeoutMinutes().GetValue())
}
}
551 changes: 358 additions & 193 deletions api/config/ui/config_request.pb.go

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion api/config/ui/config_request.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ message ConfigRequest {
chef.automate.infra.config.TLSCredentials tls = 3;
Nginx ngx = 4;
Log log = 5;
Banner banner = 6;
CustomSettings custom_settings = 6;

message CustomSettings {
Banner banner = 1;
SessionSettings session_settings = 2;
}

message Service {
google.protobuf.StringValue host = 1 [deprecated=true]; // The listen host is no longer setable(localhost only)
Expand Down Expand Up @@ -75,5 +80,10 @@ message ConfigRequest {
google.protobuf.StringValue background_color = 3;
google.protobuf.StringValue text_color = 4;
}

message SessionSettings {
google.protobuf.BoolValue enable_idle_timeout = 1;
google.protobuf.Int32Value idle_timeout_minutes = 2;
}
}
}
1 change: 1 addition & 0 deletions buf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -440,4 +440,5 @@ breaking:
- external/infra_proxy/request/roles.proto
- external/infra_proxy/response/roles.proto
- external/compliance/reporting/reporting.proto
- config/ui/config_request.proto

4 changes: 2 additions & 2 deletions components/automate-load-balancer/habitat/config/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,8 @@ http {
add_header X-Content-Type-Options "nosniff" always;
}

location /banner.js {
proxy_pass https://automate-ui/banner.js;
location /custom_settings.js {
proxy_pass https://automate-ui/custom_settings.js;
proxy_set_header Connection ""; # Required to make persistent connections happen
add_header X-Frame-Options sameorigin; # forbid other <iframe>ing
add_header Cache-Control "private"; # allow caching, but no sharing of cached data
Expand Down
1 change: 0 additions & 1 deletion components/automate-ui/habitat/config/banner.js

This file was deleted.

1 change: 1 addition & 0 deletions components/automate-ui/habitat/config/custom_settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ toJson cfg.custom_settings }}
4 changes: 2 additions & 2 deletions components/automate-ui/habitat/config/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ http {
ssl_ciphers {{cfg.ngx.http.ssl_ciphers}};
ssl_prefer_server_ciphers on;

location /banner.js {
location /custom_settings.js {
add_header Cache-Control "private, no-cache, no-store";
alias {{../pkg.svc_config_path}}/banner.js;
alias {{../pkg.svc_config_path}}/custom_settings.js;
break;
}

Expand Down
1 change: 1 addition & 0 deletions components/automate-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@ngrx/store": "11.0.1",
"@ngrx/store-devtools": "11.0.1",
"@nguniversal/express-engine": "11.2.1",
"broadcast-channel": "^3.7.0",
"classlist.js": "^1.1.20150312",
"core-js": "^3.9.1",
"d3": "^6.6.0",
Expand Down
24 changes: 12 additions & 12 deletions components/automate-ui/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,17 @@ import { AppConfigService } from 'app/services/app-config/app-config.service';
: StoreDevtoolsModule.instrument({ maxAge: 25 /* states */, actionSanitizer, stateSanitizer })
],
providers: [
// Initilization for warning banner component
{
provide: APP_INITIALIZER,
multi: true,
deps: [AppConfigService],
useFactory: (appConfigService: AppConfigService) => {
return () => {
return appConfigService.loadAppConfig();
};
}
},
AdminKeyRequests,
ApiTokenRequests,
AttributesService,
Expand Down Expand Up @@ -364,18 +375,7 @@ import { AppConfigService } from 'app/services/app-config/app-config.service';
TelemetryService,
UserPermsRequests,
UserPreferencesRequests,
UserRequests,
// Initilization for warning banner component
{
provide: APP_INITIALIZER,
multi: true,
deps: [AppConfigService],
useFactory: (appConfigService: AppConfigService) => {
return () => {
return appConfigService.loadAppConfig();
};
}
}
UserRequests
],
bootstrap: [ AppComponent ],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { AppConfigService } from './app-config.service';

const EXPECTED_URL = '/banner.js';
const EXPECTED_URL = '/custom_settings.js';

describe('AppConfigService', () => {
let service: AppConfigService;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
import { HttpClient, HttpBackend } from '@angular/common/http';
import { Injectable } from '@angular/core';

interface ConfigTypes {
interface BannerConfigTypes {
show?: boolean;
message?: string;
background_color?: string;
text_color?: string;
}

interface SessionSettings {
enable_idle_timeout?: boolean;
idle_timeout_minutes?: number;
}

interface ConfigTypes {
banner?: BannerConfigTypes;
session_settings?: SessionSettings;
}

const initialConfig = {
show: null,
message: null,
background_color: null,
text_color: null
banner: {
show: null,
message: null,
background_color: null,
text_color: null
},
session_settings: {
enable_idle_timeout: null,
idle_timeout_minutes: null
}
};

@Injectable({
Expand All @@ -26,27 +42,35 @@ export class AppConfigService {
constructor(private handler: HttpBackend) { }

public loadAppConfig() {
return new HttpClient(this.handler).get('/banner.js')
return new HttpClient(this.handler).get('/custom_settings.js')
.toPromise()
.then(data => this.appConfig = data)
.then((data: any) => this.appConfig = data)
// when there is no config, we can just reset the config to its initial empty values
.catch(_error => this.appConfig = initialConfig);
}

get showBanner(): boolean {
return this.appConfig.show;
return this.appConfig.banner.show;
}

get bannerMessage(): string {
return this.appConfig.message;
return this.appConfig.banner.message;
}

get idleTimeout(): number {
return this.appConfig.session_settings.idle_timeout_minutes;
}

get isIdleTimeoutEnabled(): boolean {
return this.appConfig.session_settings.enable_idle_timeout;
}

get bannerBackgroundColor(): string {
return this.convertToHex(this.appConfig.background_color);
return this.convertToHex(this.appConfig.banner.background_color);
}

get bannerTextColor(): string {
return this.convertToHex(this.appConfig.text_color);
return this.convertToHex(this.appConfig.banner.text_color);
}

private convertToHex(color: string): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { NgrxStateAtom } from 'app/ngrx.reducers';
import { Observable, ReplaySubject, timer, throwError } from 'rxjs';
import { map, mergeMap, filter, retryWhen, delay } from 'rxjs/operators';
import { isNull, isNil } from 'lodash';
import { BroadcastChannel } from 'broadcast-channel';

import { environment } from 'environments/environment';
import { Jwt, IDToken } from 'app/helpers/jwt/jwt';
import { SetUserSelfID } from 'app/entities/users/userself.actions';
import { AppConfigService } from 'app/services/app-config/app-config.service';

import { UserPreferencesService } from '../user-preferences/user-preferences.service';
// Should never be on in production. Modify environment.ts locally
Expand Down Expand Up @@ -40,6 +42,9 @@ export class ChefSessionService implements CanActivate {
private isRefreshing: boolean;
private tokenProvider: ReplaySubject<string>;

isIdleTimeoutEnabled = false; // default it's false
idleTimeout = 30; // 30mins default

// Session state keys - We use session storage to save state here because
// the application can be reinitialized multiple time during a single session.
//// Flag to store whether or not the modal has been displayed this session.
Expand All @@ -48,9 +53,13 @@ export class ChefSessionService implements CanActivate {

constructor(private store: Store<NgrxStateAtom>,
handler: HttpBackend,
private userPrefService: UserPreferencesService) {
private userPrefService: UserPreferencesService,
private appConfigService: AppConfigService) {
// In dev mode, set a generic session so we don't
// have to round-trip to the oidc provider (dex).
setTimeout(() => this.callIdleTimeout(), 30 * 1000);
// callIdleTimeout will get excuted after 30 sec.

this.tokenProvider = new ReplaySubject(1);
if (USE_DEFAULT_SESSION) {
this.setDefaultSession();
Expand Down Expand Up @@ -123,6 +132,17 @@ export class ChefSessionService implements CanActivate {
);
}

callIdleTimeout(): void {
this.isIdleTimeoutEnabled = this.appConfigService.isIdleTimeoutEnabled;
this.idleTimeout = this.appConfigService.idleTimeout;

console.log(this.appConfigService.isIdleTimeoutEnabled, 'this.appConfigService.isIdleTimeoutEnabled');

if (this.isIdleTimeoutEnabled && this.idleTimeout > 0) {
this.idleLogout(this.idleTimeout); // Time in minutes
}
}

ingestIDToken(idToken: string): void {
if (!idToken) {
return;
Expand Down Expand Up @@ -221,6 +241,39 @@ export class ChefSessionService implements CanActivate {
}
}

idleLogout(idleTimeout: number): void {
let idleTime = 0;
const broadcastChannel = new BroadcastChannel('tabsCheckForIdleTimeout');

// Increment the idle time counter after every minute.
setInterval(timerIncrement.bind(this), 60 * 1000);
window.onload = resetTimer;
window.onmousemove = resetTimer;
window.onmousedown = resetTimer; // catches touchscreen presses as well
window.ontouchstart = resetTimer; // catches touchscreen swipes as well
window.onclick = resetTimer; // catches touchpad clicks as well
window.onkeydown = resetTimer;
window.addEventListener('scroll', resetTimer, true);
broadcastChannel.onmessage = resetTimer;
// catches different tabs/window of same browser activity

function resetTimer() {
// this is for testing multi tabs
if (idleTime > 0) {
console.log('called broadcast message on different tab');
broadcastChannel.postMessage('resetTimer');
}
idleTime = 0;
}

function timerIncrement() {
idleTime = idleTime + 1;
if (idleTime === idleTimeout + 1) {
this.logout();
}
}
}

// TODO(sr) 2019/08/26: I don't think we should use these global variables.
// Instead, we should take the information about the currently-viewed page
// from the ngrx store.
Expand Down
6 changes: 3 additions & 3 deletions dev/patch.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[global.v1]
[global.v1.disclosure]
show = false
show = true
message_file_path = "/src/dev/banner_message.txt"
[global.v1.banner]
show = false
show = true
message = "Changing the text around"
background_color = "3864f2" # Must be a hex value minus the hash symbol
text_color = "FFF" # Must be a hex value minus the hash symbol

# Find valid HEX codes at https://htmlcolorcodes.com/
# check your HTML validation at https://validator.w3.org/
# check your HTML validation at https://validator.w3.org/
4 changes: 4 additions & 0 deletions dev/session.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[global.v1]
[global.v1.session_settings]
enable_idle_timeout = true
idle_timeout_minutes = 1