/* * Copyright (c) 2020 InterDigital Communications, Inc * * Licensed under the Apache License, Version 2.0 (the \"License\"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an \"AS IS\" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * AdvantEDGE Platform Controller REST API * * This API is the main Platform Controller API for scenario configuration & sandbox management
**Micro-service**
[meep-pfm-ctrl](https://github.com/InterDigitalInc/AdvantEDGE/tree/master/go-apps/meep-platform-ctrl)
**Type & Usage**
Platform main interface used by controller software to configure scenarios and manage sandboxes in the AdvantEDGE platform
**Details**
API details available at _your-AdvantEDGE-ip-address/api_
*
* API version: 1.0.0
* Contact: AdvantEDGE@InterDigital.com
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package server
import (
"context"
"crypto/rand"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"sync"
"time"
dataModel "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-data-model"
log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger"
ms "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-metric-store"
mq "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-mq"
pcc "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-platform-ctrl-client"
sm "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-sessions"
users "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-users"
"github.com/google/go-github/github"
"github.com/gorilla/mux"
"github.com/lkysow/go-gitlab"
"github.com/roymx/viper"
"golang.org/x/oauth2"
)
const OAUTH_PROVIDER_GITHUB = "github"
const OAUTH_PROVIDER_GITLAB = "gitlab"
const OAUTH_PROVIDER_LOCAL = "local"
const moduleName = "meep-auth-svc"
const moduleNamespace = "default"
const postgisUser = "postgres"
const postgisPwd = "pwd"
const permissionsRoot = "services"
const pfmCtrlBasepath = "http://meep-platform-ctrl/platform-ctrl/v1"
type LoginRequest struct {
provider string
timer *time.Timer
}
type AuthSvc struct {
sessionMgr *sm.SessionMgr
userStore *users.Connector
metricStore *ms.MetricStore
mqGlobal *mq.MsgQueue
pfmCtrlClient *pcc.APIClient
maxSessions int
uri string
oauthConfigs map[string]*oauth2.Config
loginRequests map[string]*LoginRequest
}
var mutex sync.Mutex
var gitlabApiUrl = ""
// Declare as variables to enable overwrite in test
var redisDBAddr = "meep-redis-master:6379"
var influxDBAddr string = "http://meep-influxdb.default.svc.cluster.local:8086"
// Auth Service
var authSvc *AuthSvc
func Init() (err error) {
// Create new Platform Controller
authSvc = new(AuthSvc)
// Create message queue
authSvc.mqGlobal, err = mq.NewMsgQueue(mq.GetGlobalName(), moduleName, moduleNamespace, redisDBAddr)
if err != nil {
log.Error("Failed to create Message Queue with error: ", err)
return err
}
log.Info("Message Queue created")
// Create Platform Controller REST API client
pfmCtrlClientCfg := pcc.NewConfiguration()
pfmCtrlClientCfg.BasePath = pfmCtrlBasepath
authSvc.pfmCtrlClient = pcc.NewAPIClient(pfmCtrlClientCfg)
if authSvc.pfmCtrlClient == nil {
err := errors.New("Failed to create Platform Ctrl REST API client")
return err
}
log.Info("Platform Ctrl REST API client created")
// Connect to Session Manager
authSvc.sessionMgr, err = sm.NewSessionMgr(moduleName, "", redisDBAddr, redisDBAddr)
if err != nil {
log.Error("Failed connection to Session Manager: ", err.Error())
return err
}
log.Info("Connected to Session Manager")
// Connect to User Store
authSvc.userStore, err = users.NewConnector(moduleName, postgisUser, postgisPwd, "", "")
if err != nil {
log.Error("Failed connection to User Store: ", err.Error())
return err
}
_ = authSvc.userStore.CreateTables()
log.Info("Connected to User Store")
// Set endpoint authorization permissions
setPermissions()
// Connect to Metric Store
authSvc.metricStore, err = ms.NewMetricStore("session-metrics", "global", influxDBAddr, ms.MetricsDbDisabled)
if err != nil {
log.Error("Failed connection to Metric Store: ", err)
return err
}
// Retrieve maximum session count from environment variable
if maxSessions, err := strconv.ParseInt(os.Getenv("MEEP_MAX_SESSIONS"), 10, 0); err == nil {
authSvc.maxSessions = int(maxSessions)
}
log.Info("MEEP_MAX_SESSIONS: ", authSvc.maxSessions)
// Get default platform URI
authSvc.uri = strings.TrimSpace(os.Getenv("MEEP_HOST_URL"))
// Initialize OAuth
authSvc.oauthConfigs = make(map[string]*oauth2.Config)
authSvc.loginRequests = make(map[string]*LoginRequest)
// Initialize Github config
githubEnabledStr := strings.TrimSpace(os.Getenv("MEEP_OAUTH_GITHUB_ENABLED"))
githubEnabled, err := strconv.ParseBool(githubEnabledStr)
if err == nil && githubEnabled {
clientId := strings.TrimSpace(os.Getenv("MEEP_OAUTH_GITHUB_CLIENT_ID"))
secret := strings.TrimSpace(os.Getenv("MEEP_OAUTH_GITHUB_SECRET"))
redirectUri := strings.TrimSpace(os.Getenv("MEEP_OAUTH_GITHUB_REDIRECT_URI"))
authUrl := strings.TrimSpace(os.Getenv("MEEP_OAUTH_GITHUB_AUTH_URL"))
tokenUrl := strings.TrimSpace(os.Getenv("MEEP_OAUTH_GITHUB_TOKEN_URL"))
if clientId != "" && secret != "" && redirectUri != "" && authUrl != "" && tokenUrl != "" {
oauthConfig := &oauth2.Config{
ClientID: clientId,
ClientSecret: secret,
RedirectURL: redirectUri,
Scopes: []string{},
Endpoint: oauth2.Endpoint{
AuthURL: authUrl,
TokenURL: tokenUrl,
},
}
authSvc.oauthConfigs[OAUTH_PROVIDER_GITHUB] = oauthConfig
log.Info("GitHub OAuth provider enabled")
}
}
// Initialize GitLab config
gitlabEnabledStr := strings.TrimSpace(os.Getenv("MEEP_OAUTH_GITLAB_ENABLED"))
gitlabEnabled, err := strconv.ParseBool(gitlabEnabledStr)
if err == nil && gitlabEnabled {
gitlabApiUrl = strings.TrimSpace(os.Getenv("MEEP_OAUTH_GITLAB_API_URL"))
clientId := strings.TrimSpace(os.Getenv("MEEP_OAUTH_GITLAB_CLIENT_ID"))
secret := strings.TrimSpace(os.Getenv("MEEP_OAUTH_GITLAB_SECRET"))
redirectUri := strings.TrimSpace(os.Getenv("MEEP_OAUTH_GITLAB_REDIRECT_URI"))
authUrl := strings.TrimSpace(os.Getenv("MEEP_OAUTH_GITLAB_AUTH_URL"))
tokenUrl := strings.TrimSpace(os.Getenv("MEEP_OAUTH_GITLAB_TOKEN_URL"))
if clientId != "" && secret != "" && redirectUri != "" && authUrl != "" && tokenUrl != "" {
oauthConfig := &oauth2.Config{
ClientID: clientId,
ClientSecret: secret,
RedirectURL: redirectUri,
Scopes: []string{"read_user"},
Endpoint: oauth2.Endpoint{
AuthURL: authUrl,
TokenURL: tokenUrl,
},
}
authSvc.oauthConfigs[OAUTH_PROVIDER_GITLAB] = oauthConfig
log.Info("GitLab OAuth provider enabled")
}
}
return nil
}
func Run() (err error) {
// Start Session Watchdog
err = authSvc.sessionMgr.StartSessionWatchdog(sessionTimeoutCb)
if err != nil {
log.Error("Failed start Session Watchdog: ", err.Error())
return err
}
return nil
}
func setPermissions() {
// Flush old permissions
ps := authSvc.sessionMgr.GetPermissionStore()
ps.Flush()
// Read & apply API permissions from file
permissionsFile := "/permissions.yaml"
permissions := viper.New()
permissions.SetConfigFile(permissionsFile)
err := permissions.ReadInConfig()
if err != nil {
log.Warn("Failed to read permissions from file")
log.Warn("Granting full API access for all roles by default")
_ = ps.SetDefaultPermission(&sm.Permission{Mode: sm.ModeAllow})
return
}
// Loop through services
for service := range permissions.GetStringMap(permissionsRoot) {
// Default permissions
if service == "default" {
permissionsRoute := permissionsRoot + ".default"
permission := new(sm.Permission)
permission.Mode = permissions.GetString(permissionsRoute + ".mode")
permission.RolePermissions = make(map[string]string)
for role, access := range permissions.GetStringMapString(permissionsRoute + ".roles") {
permission.RolePermissions[role] = access
}
_ = ps.SetDefaultPermission(permission)
} else {
// Service route names
permissionsService := permissionsRoot + "." + service
for name := range permissions.GetStringMap(permissionsService) {
permissionsRoute := permissionsService + "." + name
permission := new(sm.Permission)
permission.Mode = permissions.GetString(permissionsRoute + ".mode")
permission.RolePermissions = make(map[string]string)
for role, access := range permissions.GetStringMapString(permissionsRoute + ".roles") {
permission.RolePermissions[role] = access
}
_ = ps.Set(service, name, permission)
}
}
}
}
func sessionTimeoutCb(session *sm.Session) {
log.Info("Session timed out. ID[", session.ID, "] Username[", session.Username, "]")
var metric ms.SessionMetric
metric.Provider = session.Provider
metric.User = session.Username
metric.Sandbox = session.Sandbox
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeTimeout, metric)
// Destroy session sandbox
_, _ = authSvc.pfmCtrlClient.SandboxControlApi.DeleteSandbox(context.TODO(), session.Sandbox)
}
// Generate a random state string
func generateState(n int) (string, error) {
data := make([]byte, n)
if _, err := io.ReadFull(rand.Reader, data); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(data), nil
}
func getUniqueState() (state string, err error) {
for i := 0; i < 3; i++ {
// Get random state
randState, err := generateState(20)
if err != nil {
log.Error(err.Error())
return "", err
}
// Make sure state is unique
if _, found := authSvc.loginRequests[randState]; !found {
return randState, nil
}
}
return "", errors.New("Failed to generate a random state string")
}
func getLoginRequest(state string) *LoginRequest {
mutex.Lock()
defer mutex.Unlock()
request, found := authSvc.loginRequests[state]
if !found {
return nil
}
return request
}
func setLoginRequest(state string, request *LoginRequest) {
mutex.Lock()
defer mutex.Unlock()
authSvc.loginRequests[state] = request
}
func delLoginRequest(state string) {
mutex.Lock()
defer mutex.Unlock()
request, found := authSvc.loginRequests[state]
if !found {
return
}
if request.timer != nil {
request.timer.Stop()
}
delete(authSvc.loginRequests, state)
}
func getErrUrl(err string) string {
return authSvc.uri + "?err=" + strings.ReplaceAll(err, " ", "+")
}
func asAuthenticate(w http.ResponseWriter, r *http.Request) {
// Get service & sandbox name from request parameters
vars := mux.Vars(r)
svcName := vars["svc"]
sboxName := vars["sbox"]
log.Debug("svcName: ", svcName, " sboxName: ", sboxName)
// Get target method & URL from forwarded request headers
targetMethod := r.Header.Get("X-Original-Method")
targetUrl := r.Header.Get("X-Original-URL")
log.Debug("targetMethod: ", targetMethod, " targetUrl: ", targetUrl)
// Get target permissions
// TODO -- parse permissions file on startup to create regexp table
// // Get permission store instance
// ps := pfmCtrl.sessionMgr.GetPermissionStore()
// if targetMethod != "" && targetUrl != "" {
// url, err := url.ParseRequestURI(targetUrl)
// if err == nil {
// }
// }
permission := new(sm.Permission)
// permission.Mode = sm.ModeAllow
permission.Mode = sm.ModeVerify
permission.RolePermissions = make(map[string]string)
permission.RolePermissions[sm.RoleAdmin] = sm.ModeAllow
permission.RolePermissions[sm.RoleUser] = sm.ModeBlock
// // Use default permission if none found
// if permission == nil {
// permission, err = ps.GetDefaultPermission()
// if err != nil || permission == nil {
// http.Error(w, "Unauthorized", http.StatusUnauthorized)
// return
// }
// }
// Get session store instance
ss := authSvc.sessionMgr.GetSessionStore()
// Handle according to permission mode
switch permission.Mode {
case sm.ModeBlock:
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
case sm.ModeAllow:
case sm.ModeVerify:
// Retrieve user session, if any
session, err := ss.Get(r)
if err != nil || session == nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Verify role permissions
role := session.Role
if role == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
access := permission.RolePermissions[role]
if access != sm.AccessGranted {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// For non-admin users, verify session sandbox matches service sandbox, if any
if session.Role != sm.RoleAdmin && sboxName != "" && sboxName != session.Sandbox {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
default:
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
}
func asAuthorize(w http.ResponseWriter, r *http.Request) {
var metric ms.SessionMetric
// Retrieve query parameters
query := r.URL.Query()
code := query.Get("code")
state := query.Get("state")
// Validate request state
request := getLoginRequest(state)
if request == nil {
err := errors.New("Invalid OAuth state")
log.Error(err.Error())
metric.Description = err.Error()
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Redirect(w, r, getErrUrl(err.Error()), http.StatusFound)
return
}
// Get provider-specific OAuth config
provider := request.provider
config, found := authSvc.oauthConfigs[provider]
if !found {
err := errors.New("Provider config not found for: " + provider)
log.Error(err.Error())
metric.Description = err.Error()
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Redirect(w, r, getErrUrl(err.Error()), http.StatusFound)
return
}
metric.Provider = provider
// Delete login request & timer
delLoginRequest(state)
// Retrieve access token
token, err := config.Exchange(context.Background(), code)
if err != nil {
log.Error(err.Error())
metric.Description = err.Error()
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Redirect(w, r, getErrUrl(err.Error()), http.StatusFound)
return
}
oauthClient := config.Client(context.Background(), token)
if oauthClient == nil {
err = errors.New("Failed to create new oauth client")
log.Error(err.Error())
metric.Description = err.Error()
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Redirect(w, r, getErrUrl(err.Error()), http.StatusFound)
return
}
// Retrieve User ID
var userId string
switch provider {
case OAUTH_PROVIDER_GITHUB:
client := github.NewClient(oauthClient)
if client == nil {
err = errors.New("Failed to create new GitHub client")
log.Error(err.Error())
metric.Description = err.Error()
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Redirect(w, r, getErrUrl(err.Error()), http.StatusFound)
return
}
user, _, err := client.Users.Get(context.Background(), "")
if err != nil {
log.Error(err.Error())
metric.Description = err.Error()
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Redirect(w, r, getErrUrl("Failed to retrieve GitHub user ID"), http.StatusFound)
return
}
userId = *user.Login
case OAUTH_PROVIDER_GITLAB:
client := gitlab.NewOAuthClient(oauthClient, token.AccessToken)
if client == nil {
err = errors.New("Failed to create new GitLab client")
log.Error(err.Error())
metric.Description = err.Error()
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Redirect(w, r, getErrUrl(err.Error()), http.StatusFound)
return
}
// Override default gitlab base URL
if gitlabApiUrl != "" {
err = client.SetBaseURL(gitlabApiUrl)
if err != nil {
log.Error(err.Error())
metric.Description = err.Error()
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Redirect(w, r, getErrUrl("Failed to set GitLab API base url"), http.StatusFound)
return
}
}
user, _, err := client.Users.CurrentUser()
if err != nil {
log.Error(err.Error())
metric.Description = err.Error()
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Redirect(w, r, getErrUrl("Failed to retrieve GitLab user ID"), http.StatusFound)
return
}
userId = user.Username
default:
}
metric.User = userId
// Start user session
sandboxName, err, errCode := startSession(provider, userId, w, r)
if err != nil {
log.Error(err.Error())
metric.Description = err.Error()
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Redirect(w, r, getErrUrl(err.Error()), errCode)
return
}
metric.Sandbox = sandboxName
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeLogin, metric)
// Redirect user to sandbox
http.Redirect(w, r, authSvc.uri+"?sbox="+sandboxName+"&user="+userId, http.StatusFound)
}
func asLogin(w http.ResponseWriter, r *http.Request) {
log.Info("----- OAUTH LOGIN -----")
var metric ms.SessionMetric
// Retrieve query parameters
query := r.URL.Query()
provider := query.Get("provider")
metric.Provider = provider
// Get provider-specific OAuth config
config, found := authSvc.oauthConfigs[provider]
if !found {
err := errors.New("Provider config not found for: " + provider)
log.Error(err.Error())
metric.Description = err.Error()
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Redirect(w, r, getErrUrl(err.Error()), http.StatusFound)
return
}
// Generate unique random state string
state, err := getUniqueState()
if err != nil {
log.Error(err.Error())
metric.Description = err.Error()
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Redirect(w, r, getErrUrl(err.Error()), http.StatusFound)
return
}
// Track oauth request & handle
request := &LoginRequest{
provider: provider,
timer: time.NewTimer(10 * time.Minute),
}
setLoginRequest(state, request)
// Start timer to remove request from map
go func() {
<-request.timer.C
delLoginRequest(state)
}()
// Generate provider-specific oauth redirect
uri := config.AuthCodeURL(state, oauth2.AccessTypeOnline)
http.Redirect(w, r, uri, http.StatusFound)
}
func asLoginUser(w http.ResponseWriter, r *http.Request) {
log.Info("----- LOGIN -----")
var metric ms.SessionMetric
// Get form data
username := r.FormValue("username")
password := r.FormValue("password")
metric.Provider = OAUTH_PROVIDER_LOCAL
metric.User = username
// Validate user credentials
authenticated, err := authSvc.userStore.AuthenticateUser(OAUTH_PROVIDER_LOCAL, username, password)
if err != nil || !authenticated {
if err != nil {
metric.Description = err.Error()
} else {
metric.Description = "Unauthorized"
}
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Start user session
sandboxName, err, errCode := startSession(OAUTH_PROVIDER_LOCAL, username, w, r)
if err != nil {
log.Error(err.Error())
metric.Description = err.Error()
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeError, metric)
http.Error(w, err.Error(), errCode)
return
}
metric.Sandbox = sandboxName
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeLogin, metric)
// Prepare response
var sandbox dataModel.Sandbox
sandbox.Name = sandboxName
// Format response
jsonResponse, err := json.Marshal(sandbox)
if err != nil {
log.Error(err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Send response
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, string(jsonResponse))
}
// Retrieve existing user session or create a new one
func startSession(provider string, username string, w http.ResponseWriter, r *http.Request) (sandboxName string, err error, code int) {
// Get existing session by user name, if any
sessionStore := authSvc.sessionMgr.GetSessionStore()
session, err := sessionStore.GetByName(provider, username)
if err != nil {
// Check if max session count is reached before creating a new one
count := sessionStore.GetCount()
if count >= authSvc.maxSessions {
err = errors.New("Maximum session count exceeded")
return "", err, http.StatusServiceUnavailable
}
// Get requested sandbox name & role from user profile, if any
role := users.RoleUser
user, err := authSvc.userStore.GetUser(provider, username)
if err == nil {
sandboxName = user.Sboxname
role = user.Role
}
// Create sandbox
var sandboxConfig pcc.SandboxConfig
if sandboxName == "" {
sandbox, _, err := authSvc.pfmCtrlClient.SandboxControlApi.CreateSandbox(context.TODO(), sandboxConfig)
if err != nil {
return "", err, http.StatusInternalServerError
}
sandboxName = sandbox.Name
} else {
_, err := authSvc.pfmCtrlClient.SandboxControlApi.CreateSandboxWithName(context.TODO(), sandboxName, sandboxConfig)
if err != nil {
return "", err, http.StatusInternalServerError
}
}
// Create new session
session = new(sm.Session)
session.ID = ""
session.Username = username
session.Provider = provider
session.Sandbox = sandboxName
session.Role = role
} else {
sandboxName = session.Sandbox
}
// Set session
err, code = sessionStore.Set(session, w, r)
if err != nil {
log.Error("Failed to set session with err: ", err.Error())
// Remove newly created sandbox on failure
if session.ID == "" {
_, _ = authSvc.pfmCtrlClient.SandboxControlApi.DeleteSandbox(context.TODO(), sandboxName)
}
return "", err, code
}
return sandboxName, nil, http.StatusOK
}
func asLogout(w http.ResponseWriter, r *http.Request) {
log.Info("----- LOGOUT -----")
var metric ms.SessionMetric
// Get existing session
sessionStore := authSvc.sessionMgr.GetSessionStore()
session, err := sessionStore.Get(r)
if err == nil {
metric.Provider = session.Provider
metric.User = session.Username
metric.Sandbox = session.Sandbox
// Delete sandbox
_, _ = authSvc.pfmCtrlClient.SandboxControlApi.DeleteSandbox(context.TODO(), session.Sandbox)
}
// Delete session
err, code := sessionStore.Del(w, r)
if err != nil {
log.Error("Failed to delete session with err: ", err.Error())
http.Error(w, err.Error(), code)
return
}
_ = authSvc.metricStore.SetSessionMetric(ms.SesMetTypeLogout, metric)
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
}
func asTriggerWatchdog(w http.ResponseWriter, r *http.Request) {
// Refresh session
sessionStore := authSvc.sessionMgr.GetSessionStore()
err, code := sessionStore.Refresh(w, r)
if err != nil {
log.Error("Failed to refresh session with err: ", err.Error())
http.Error(w, err.Error(), code)
return
}
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
}