mirror of
https://github.com/gravitl/netmaker.git
synced 2025-09-10 07:05:28 +08:00
Merge branch 'develop' into NM-49
# Conflicts: # pro/auth/sync.go
This commit is contained in:
commit
99f1be4aba
17 changed files with 342 additions and 46 deletions
|
@ -3,7 +3,6 @@ package auth
|
|||
import (
|
||||
"github.com/gravitl/netmaker/logic"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// == consts ==
|
||||
|
@ -11,10 +10,6 @@ const (
|
|||
node_signin_length = 64
|
||||
)
|
||||
|
||||
var (
|
||||
auth_provider *oauth2.Config
|
||||
)
|
||||
|
||||
func isUserIsAllowed(username, network string) (*models.User, error) {
|
||||
|
||||
user, err := logic.GetUser(username)
|
||||
|
|
|
@ -110,7 +110,7 @@ func SessionHandler(conn *websocket.Conn) {
|
|||
return
|
||||
}
|
||||
} else { // handle SSO / OAuth
|
||||
if auth_provider == nil {
|
||||
if !logic.IsOAuthConfigured() {
|
||||
err = conn.WriteMessage(messageType, []byte("Oauth not configured"))
|
||||
if err != nil {
|
||||
logger.Log(0, "error during message writing:", err.Error())
|
||||
|
|
|
@ -377,15 +377,6 @@ func identifySettingsUpdateAction(old, new models.ServerSettings) models.Action
|
|||
return models.UpdateMonitoringAndDebuggingSettings
|
||||
}
|
||||
|
||||
if old.Theme != new.Theme {
|
||||
return models.UpdateDisplaySettings
|
||||
}
|
||||
|
||||
if old.TextSize != new.TextSize ||
|
||||
old.ReducedMotion != new.ReducedMotion {
|
||||
return models.UpdateAccessibilitySettings
|
||||
}
|
||||
|
||||
if old.EmailSenderAddr != new.EmailSenderAddr ||
|
||||
old.EmailSenderUser != new.EmailSenderUser ||
|
||||
old.EmailSenderPassword != new.EmailSenderPassword ||
|
||||
|
|
|
@ -49,6 +49,8 @@ func userHandlers(r *mux.Router) {
|
|||
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUser)))).Methods(http.MethodGet)
|
||||
r.HandleFunc("/api/users/{username}/enable", logic.SecurityCheck(true, http.HandlerFunc(enableUserAccount))).Methods(http.MethodPost)
|
||||
r.HandleFunc("/api/users/{username}/disable", logic.SecurityCheck(true, http.HandlerFunc(disableUserAccount))).Methods(http.MethodPost)
|
||||
r.HandleFunc("/api/users/{username}/settings", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserSettings)))).Methods(http.MethodGet)
|
||||
r.HandleFunc("/api/users/{username}/settings", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(updateUserSettings)))).Methods(http.MethodPut)
|
||||
r.HandleFunc("/api/v1/users", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserV1)))).Methods(http.MethodGet)
|
||||
r.HandleFunc("/api/users", logic.SecurityCheck(true, http.HandlerFunc(getUsers))).Methods(http.MethodGet)
|
||||
r.HandleFunc("/api/v1/users/roles", logic.SecurityCheck(true, http.HandlerFunc(ListRoles))).Methods(http.MethodGet)
|
||||
|
@ -817,6 +819,46 @@ func disableUserAccount(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnSuccessResponse(w, r, "user account disabled")
|
||||
}
|
||||
|
||||
// @Summary Get a user's preferences and settings
|
||||
// @Router /api/users/{username}/settings [get]
|
||||
// @Tags Users
|
||||
// @Param username path string true "Username of the user"
|
||||
// @Success 200 {object} models.SuccessResponse
|
||||
func getUserSettings(w http.ResponseWriter, r *http.Request) {
|
||||
userID := r.Header.Get("user")
|
||||
userSettings := logic.GetUserSettings(userID)
|
||||
logic.ReturnSuccessResponseWithJson(w, r, userSettings, "fetched user settings")
|
||||
}
|
||||
|
||||
// @Summary Update a user's preferences and settings
|
||||
// @Router /api/users/{username}/settings [put]
|
||||
// @Tags Users
|
||||
// @Param username path string true "Username of the user"
|
||||
// @Success 200 {object} models.SuccessResponse
|
||||
// @Failure 400 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
func updateUserSettings(w http.ResponseWriter, r *http.Request) {
|
||||
userID := r.Header.Get("user")
|
||||
var req models.UserSettings
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to decode request body: ", err.Error())
|
||||
err = fmt.Errorf("invalid request body: %v", err)
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
|
||||
err = logic.UpsertUserSettings(userID, req)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to update user settings: %v", err.Error())
|
||||
logger.Log(0, err.Error())
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
return
|
||||
}
|
||||
|
||||
logic.ReturnSuccessResponseWithJson(w, r, req, "updated user settings")
|
||||
}
|
||||
|
||||
// swagger:route GET /api/v1/users user getUserV1
|
||||
//
|
||||
// Get an individual user with role info.
|
||||
|
@ -1383,6 +1425,7 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
}
|
||||
_ = logic.DeleteUserInvite(user.UserName)
|
||||
mq.PublishPeerUpdate(false)
|
||||
if servercfg.IsDNSMode() {
|
||||
logic.SetDNS()
|
||||
|
|
|
@ -38,6 +38,7 @@ func ClearSuperUserCache() {
|
|||
superUser = models.User{}
|
||||
}
|
||||
|
||||
var IsOAuthConfigured = func() bool { return false }
|
||||
var ResetAuthProvider = func() {}
|
||||
var ResetIDPSyncHook = func() {}
|
||||
|
||||
|
|
|
@ -15,11 +15,17 @@ import (
|
|||
"github.com/gravitl/netmaker/servercfg"
|
||||
)
|
||||
|
||||
var serverSettingsDBKey = "server_cfg"
|
||||
var ServerSettingsDBKey = "server_cfg"
|
||||
var SettingsMutex = &sync.RWMutex{}
|
||||
|
||||
var defaultUserSettings = models.UserSettings{
|
||||
TextSize: "16",
|
||||
Theme: models.Dark,
|
||||
ReducedMotion: false,
|
||||
}
|
||||
|
||||
func GetServerSettings() (s models.ServerSettings) {
|
||||
data, err := database.FetchRecord(database.SERVER_SETTINGS, serverSettingsDBKey)
|
||||
data, err := database.FetchRecord(database.SERVER_SETTINGS, ServerSettingsDBKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -42,13 +48,47 @@ func UpsertServerSettings(s models.ServerSettings) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = database.Insert(serverSettingsDBKey, string(data), database.SERVER_SETTINGS)
|
||||
err = database.Insert(ServerSettingsDBKey, string(data), database.SERVER_SETTINGS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetUserSettings(userID string) models.UserSettings {
|
||||
data, err := database.FetchRecord(database.SERVER_SETTINGS, userID)
|
||||
if err != nil {
|
||||
return defaultUserSettings
|
||||
}
|
||||
var userSettings models.UserSettings
|
||||
err = json.Unmarshal([]byte(data), &userSettings)
|
||||
if err != nil {
|
||||
return defaultUserSettings
|
||||
}
|
||||
|
||||
return userSettings
|
||||
}
|
||||
|
||||
func UpsertUserSettings(userID string, userSettings models.UserSettings) error {
|
||||
if userSettings.TextSize == "" {
|
||||
userSettings.TextSize = "16"
|
||||
}
|
||||
|
||||
if userSettings.Theme == "" {
|
||||
userSettings.Theme = models.Dark
|
||||
}
|
||||
|
||||
data, err := json.Marshal(userSettings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return database.Insert(userID, string(data), database.SERVER_SETTINGS)
|
||||
}
|
||||
|
||||
func DeleteUserSettings(userID string) error {
|
||||
return database.DeleteRecord(database.SERVER_SETTINGS, userID)
|
||||
}
|
||||
|
||||
func ValidateNewSettings(req models.ServerSettings) bool {
|
||||
// TODO: add checks for different fields
|
||||
if req.JwtValidityDuration > 525600 || req.JwtValidityDuration < 5 {
|
||||
|
@ -87,9 +127,6 @@ func GetServerSettingsFromEnv() (s models.ServerSettings) {
|
|||
DefaultDomain: servercfg.GetDefaultDomain(),
|
||||
Stun: servercfg.IsStunEnabled(),
|
||||
StunServers: servercfg.GetStunServers(),
|
||||
TextSize: "16",
|
||||
Theme: models.Dark,
|
||||
ReducedMotion: false,
|
||||
}
|
||||
|
||||
return
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
|
||||
// Run - runs all migrations
|
||||
func Run() {
|
||||
settings()
|
||||
migrateSettings()
|
||||
updateEnrollmentKeys()
|
||||
assignSuperAdmin()
|
||||
createDefaultTagsAndPolicies()
|
||||
|
@ -635,8 +635,8 @@ func migrateToEgressV1() {
|
|||
}
|
||||
}
|
||||
|
||||
func settings() {
|
||||
_, err := database.FetchRecords(database.SERVER_SETTINGS)
|
||||
func migrateSettings() {
|
||||
_, err := database.FetchRecord(database.SERVER_SETTINGS, logic.ServerSettingsDBKey)
|
||||
if database.IsEmptyRecord(err) {
|
||||
logic.UpsertServerSettings(logic.GetServerSettingsFromEnv())
|
||||
}
|
||||
|
|
|
@ -29,8 +29,6 @@ const (
|
|||
UpdateClientSettings Action = "UPDATE_CLIENT_SETTINGS"
|
||||
UpdateAuthenticationSecuritySettings Action = "UPDATE_AUTHENTICATION_SECURITY_SETTINGS"
|
||||
UpdateMonitoringAndDebuggingSettings Action = "UPDATE_MONITORING_AND_DEBUGGING_SETTINGS"
|
||||
UpdateDisplaySettings Action = "UPDATE_DISPLAY_SETTINGS"
|
||||
UpdateAccessibilitySettings Action = "UPDATE_ACCESSIBILITY_SETTINGS"
|
||||
UpdateSMTPSettings Action = "UPDATE_EMAIL_SETTINGS"
|
||||
UpdateIDPSettings Action = "UPDATE_IDP_SETTINGS"
|
||||
)
|
||||
|
|
|
@ -47,8 +47,11 @@ type ServerSettings struct {
|
|||
DefaultDomain string `json:"default_domain"`
|
||||
Stun bool `json:"stun"`
|
||||
StunServers string `json:"stun_servers"`
|
||||
Theme Theme `json:"theme"`
|
||||
TextSize string `json:"text_size"`
|
||||
ReducedMotion bool `json:"reduced_motion"`
|
||||
AuditLogsRetentionPeriodInDays int `json:"audit_logs_retention_period"`
|
||||
}
|
||||
|
||||
type UserSettings struct {
|
||||
Theme Theme `json:"theme"`
|
||||
TextSize string `json:"text_size"`
|
||||
ReducedMotion bool `json:"reduced_motion"`
|
||||
}
|
||||
|
|
|
@ -406,3 +406,20 @@ type RsrcURLInfo struct {
|
|||
Method string
|
||||
Path string
|
||||
}
|
||||
|
||||
type IDPSyncStatus struct {
|
||||
// Status would be one of: in_progress, completed or failed.
|
||||
Status string `json:"status"`
|
||||
// Description is empty if the sync is ongoing or completed,
|
||||
// and describes the error when the sync fails.
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type IDPSyncTestRequest struct {
|
||||
AuthProvider string `json:"auth_provider"`
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
AzureTenantID string `json:"azure_tenant_id"`
|
||||
GoogleAdminEmail string `json:"google_admin_email"`
|
||||
GoogleSACredsJson string `json:"google_sa_creds_json"`
|
||||
}
|
||||
|
|
|
@ -106,6 +106,10 @@ func ResetAuthProvider() {
|
|||
InitializeAuthProvider()
|
||||
}
|
||||
|
||||
func IsOAuthConfigured() bool {
|
||||
return auth_provider != nil
|
||||
}
|
||||
|
||||
// InitializeAuthProvider - initializes the auth provider if any is present
|
||||
func InitializeAuthProvider() string {
|
||||
var functions = getCurrentAuthFunctions()
|
||||
|
|
|
@ -20,6 +20,8 @@ import (
|
|||
var (
|
||||
cancelSyncHook context.CancelFunc
|
||||
hookStopWg sync.WaitGroup
|
||||
idpSyncMtx sync.Mutex
|
||||
idpSyncErr error
|
||||
)
|
||||
|
||||
func ResetIDPSyncHook() {
|
||||
|
@ -58,6 +60,8 @@ func runIDPSyncHook(ctx context.Context) {
|
|||
}
|
||||
|
||||
func SyncFromIDP() error {
|
||||
idpSyncMtx.Lock()
|
||||
defer idpSyncMtx.Unlock()
|
||||
settings := logic.GetServerSettings()
|
||||
|
||||
var idpClient idp.Client
|
||||
|
@ -65,14 +69,18 @@ func SyncFromIDP() error {
|
|||
var idpGroups []idp.Group
|
||||
var err error
|
||||
|
||||
defer func() {
|
||||
idpSyncErr = err
|
||||
}()
|
||||
|
||||
switch settings.AuthProvider {
|
||||
case "google":
|
||||
idpClient, err = google.NewGoogleWorkspaceClient()
|
||||
idpClient, err = google.NewGoogleWorkspaceClientFromSettings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "azure-ad":
|
||||
idpClient = azure.NewAzureEntraIDClient()
|
||||
idpClient = azure.NewAzureEntraIDClientFromSettings()
|
||||
case "okta":
|
||||
idpClient, err = okta.NewOktaClient()
|
||||
if err != nil {
|
||||
|
@ -80,7 +88,8 @@ func SyncFromIDP() error {
|
|||
}
|
||||
default:
|
||||
if settings.AuthProvider != "" {
|
||||
return fmt.Errorf("invalid auth provider: %s", settings.AuthProvider)
|
||||
err = fmt.Errorf("invalid auth provider: %s", settings.AuthProvider)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,7 +110,8 @@ func SyncFromIDP() error {
|
|||
return err
|
||||
}
|
||||
|
||||
return syncGroups(idpGroups)
|
||||
err = syncGroups(idpGroups)
|
||||
return err
|
||||
}
|
||||
|
||||
func syncUsers(idpUsers []idp.User) error {
|
||||
|
@ -316,3 +326,23 @@ func syncGroups(idpGroups []idp.Group) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetIDPSyncStatus() models.IDPSyncStatus {
|
||||
if idpSyncMtx.TryLock() {
|
||||
defer idpSyncMtx.Unlock()
|
||||
return models.IDPSyncStatus{
|
||||
Status: "in_progress",
|
||||
}
|
||||
} else {
|
||||
if idpSyncErr == nil {
|
||||
return models.IDPSyncStatus{
|
||||
Status: "completed",
|
||||
}
|
||||
} else {
|
||||
return models.IDPSyncStatus{
|
||||
Status: "failed",
|
||||
Description: idpSyncErr.Error(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gravitl/netmaker/pro/idp"
|
||||
"github.com/gravitl/netmaker/pro/idp/azure"
|
||||
"github.com/gravitl/netmaker/pro/idp/google"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
@ -64,6 +67,8 @@ func UserHandlers(r *mux.Router) {
|
|||
r.HandleFunc("/api/users/ingress/{ingress_id}", logic.SecurityCheck(true, http.HandlerFunc(ingressGatewayUsers))).Methods(http.MethodGet)
|
||||
|
||||
r.HandleFunc("/api/idp/sync", logic.SecurityCheck(true, http.HandlerFunc(syncIDP))).Methods(http.MethodPost)
|
||||
r.HandleFunc("/api/idp/sync/test", logic.SecurityCheck(true, http.HandlerFunc(testIDPSync))).Methods(http.MethodPost)
|
||||
r.HandleFunc("/api/idp/sync/status", logic.SecurityCheck(true, http.HandlerFunc(getIDPSyncStatus))).Methods(http.MethodGet)
|
||||
r.HandleFunc("/api/idp", logic.SecurityCheck(true, http.HandlerFunc(removeIDPIntegration))).Methods(http.MethodDelete)
|
||||
}
|
||||
|
||||
|
@ -1618,6 +1623,54 @@ func syncIDP(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnSuccessResponse(w, r, "starting sync from idp")
|
||||
}
|
||||
|
||||
// @Summary Test IDP Sync Credentials.
|
||||
// @Router /api/idp/sync/test [post]
|
||||
// @Tags IDP
|
||||
// @Success 200 {object} models.SuccessResponse
|
||||
// @Failure 400 {object} models.ErrorResponse
|
||||
func testIDPSync(w http.ResponseWriter, r *http.Request) {
|
||||
var req models.IDPSyncTestRequest
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to decode request body: %v", err)
|
||||
logger.Log(0, err.Error())
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
|
||||
var idpClient idp.Client
|
||||
switch req.AuthProvider {
|
||||
case "google":
|
||||
idpClient, err = google.NewGoogleWorkspaceClient(req.GoogleAdminEmail, req.GoogleSACredsJson)
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
case "azure-ad":
|
||||
idpClient = azure.NewAzureEntraIDClient(req.ClientID, req.ClientSecret, req.AzureTenantID)
|
||||
default:
|
||||
err = fmt.Errorf("invalid auth provider: %s", req.AuthProvider)
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
|
||||
err = idpClient.Verify()
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
|
||||
logic.ReturnSuccessResponse(w, r, "idp sync test successful")
|
||||
}
|
||||
|
||||
// @Summary Gets idp sync status.
|
||||
// @Router /api/idp/sync/status [get]
|
||||
// @Tags IDP
|
||||
// @Success 200 {object} models.SuccessResponse
|
||||
func getIDPSyncStatus(w http.ResponseWriter, r *http.Request) {
|
||||
logic.ReturnSuccessResponseWithJson(w, r, proAuth.GetIDPSyncStatus(), "idp sync status retrieved")
|
||||
}
|
||||
|
||||
// @Summary Remove idp integration.
|
||||
// @Router /api/idp [delete]
|
||||
// @Tags IDP
|
||||
|
|
|
@ -16,14 +16,80 @@ type Client struct {
|
|||
tenantID string
|
||||
}
|
||||
|
||||
func NewAzureEntraIDClient() *Client {
|
||||
func NewAzureEntraIDClient(clientID, clientSecret, tenantID string) *Client {
|
||||
return &Client{
|
||||
clientID: clientID,
|
||||
clientSecret: clientSecret,
|
||||
tenantID: tenantID,
|
||||
}
|
||||
}
|
||||
|
||||
func NewAzureEntraIDClientFromSettings() *Client {
|
||||
settings := logic.GetServerSettings()
|
||||
|
||||
return &Client{
|
||||
clientID: settings.ClientID,
|
||||
clientSecret: settings.ClientSecret,
|
||||
tenantID: settings.AzureTenant,
|
||||
return NewAzureEntraIDClient(settings.ClientID, settings.ClientSecret, settings.AzureTenant)
|
||||
}
|
||||
|
||||
func (a *Client) Verify() error {
|
||||
accessToken, err := a.getAccessToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", "https://graph.microsoft.com/v1.0/users?$select=id,userPrincipalName,displayName,accountEnabled&$top=1", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", "Bearer "+accessToken)
|
||||
req.Header.Add("Accept", "application/json")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
var users getUsersResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&users)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if users.Error.Code != "" {
|
||||
return errors.New(users.Error.Message)
|
||||
}
|
||||
|
||||
req, err = http.NewRequest("GET", "https://graph.microsoft.com/v1.0/groups?$select=id,displayName&$expand=members($select=id)&$top=1", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", "Bearer "+accessToken)
|
||||
req.Header.Add("Accept", "application/json")
|
||||
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
var groups getGroupsResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&groups)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if groups.Error.Code != "" {
|
||||
return errors.New(groups.Error.Message)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Client) GetUsers() ([]idp.User, error) {
|
||||
|
@ -55,6 +121,10 @@ func (a *Client) GetUsers() ([]idp.User, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if users.Error.Code != "" {
|
||||
return nil, errors.New(users.Error.Message)
|
||||
}
|
||||
|
||||
retval := make([]idp.User, len(users.Value))
|
||||
for i, user := range users.Value {
|
||||
retval[i] = idp.User{
|
||||
|
@ -97,6 +167,10 @@ func (a *Client) GetGroups() ([]idp.Group, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if groups.Error.Code != "" {
|
||||
return nil, errors.New(groups.Error.Message)
|
||||
}
|
||||
|
||||
retval := make([]idp.Group, len(groups.Value))
|
||||
for i, group := range groups.Value {
|
||||
retvalMembers := make([]string, len(group.Members))
|
||||
|
@ -141,11 +215,12 @@ func (a *Client) getAccessToken() (string, error) {
|
|||
return token, nil
|
||||
}
|
||||
|
||||
return "", errors.New("failed to get access token")
|
||||
return "", errors.New("invalid credentials")
|
||||
}
|
||||
|
||||
type getUsersResponse struct {
|
||||
OdataContext string `json:"@odata.context"`
|
||||
Error errorResponse `json:"error"`
|
||||
OdataContext string `json:"@odata.context"`
|
||||
Value []struct {
|
||||
Id string `json:"id"`
|
||||
UserPrincipalName string `json:"userPrincipalName"`
|
||||
|
@ -155,7 +230,8 @@ type getUsersResponse struct {
|
|||
}
|
||||
|
||||
type getGroupsResponse struct {
|
||||
OdataContext string `json:"@odata.context"`
|
||||
Error errorResponse `json:"error"`
|
||||
OdataContext string `json:"@odata.context"`
|
||||
Value []struct {
|
||||
Id string `json:"id"`
|
||||
DisplayName string `json:"displayName"`
|
||||
|
@ -165,3 +241,13 @@ type getGroupsResponse struct {
|
|||
} `json:"members"`
|
||||
} `json:"value"`
|
||||
}
|
||||
|
||||
type errorResponse struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
InnerError struct {
|
||||
Date string `json:"date"`
|
||||
RequestId string `json:"request-id"`
|
||||
ClientRequestId string `json:"client-request-id"`
|
||||
} `json:"innerError"`
|
||||
}
|
||||
|
|
|
@ -4,9 +4,11 @@ import (
|
|||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/gravitl/netmaker/logic"
|
||||
"github.com/gravitl/netmaker/pro/idp"
|
||||
admindir "google.golang.org/api/admin/directory/v1"
|
||||
"google.golang.org/api/googleapi"
|
||||
"google.golang.org/api/impersonate"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
@ -15,10 +17,8 @@ type Client struct {
|
|||
service *admindir.Service
|
||||
}
|
||||
|
||||
func NewGoogleWorkspaceClient() (*Client, error) {
|
||||
settings := logic.GetServerSettings()
|
||||
|
||||
credsJson, err := base64.StdEncoding.DecodeString(settings.GoogleSACredsJson)
|
||||
func NewGoogleWorkspaceClient(adminEmail, creds string) (*Client, error) {
|
||||
credsJson, err := base64.StdEncoding.DecodeString(creds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ func NewGoogleWorkspaceClient() (*Client, error) {
|
|||
admindir.AdminDirectoryGroupReadonlyScope,
|
||||
admindir.AdminDirectoryGroupMemberReadonlyScope,
|
||||
},
|
||||
Subject: settings.GoogleAdminEmail,
|
||||
Subject: adminEmail,
|
||||
},
|
||||
option.WithCredentialsJSON(credsJson),
|
||||
)
|
||||
|
@ -59,6 +59,42 @@ func NewGoogleWorkspaceClient() (*Client, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func NewGoogleWorkspaceClientFromSettings() (*Client, error) {
|
||||
settings := logic.GetServerSettings()
|
||||
|
||||
return NewGoogleWorkspaceClient(settings.GoogleAdminEmail, settings.GoogleSACredsJson)
|
||||
}
|
||||
|
||||
func (g *Client) Verify() error {
|
||||
_, err := g.service.Users.List().
|
||||
Customer("my_customer").
|
||||
MaxResults(1).
|
||||
Do()
|
||||
if err != nil {
|
||||
var gerr *googleapi.Error
|
||||
if errors.As(err, &gerr) {
|
||||
return errors.New(gerr.Message)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = g.service.Groups.List().
|
||||
Customer("my_customer").
|
||||
MaxResults(1).
|
||||
Do()
|
||||
if err != nil {
|
||||
var gerr *googleapi.Error
|
||||
if errors.As(err, &gerr) {
|
||||
return errors.New(gerr.Message)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Client) GetUsers() ([]idp.User, error) {
|
||||
var retval []idp.User
|
||||
err := g.service.Users.List().
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package idp
|
||||
|
||||
type Client interface {
|
||||
Verify() error
|
||||
GetUsers() ([]User, error)
|
||||
GetGroups() ([]Group, error)
|
||||
}
|
||||
|
|
|
@ -134,6 +134,7 @@ func InitPro() {
|
|||
logic.GetUserGroupsInNetwork = proLogic.GetUserGroupsInNetwork
|
||||
logic.GetUserGroup = proLogic.GetUserGroup
|
||||
logic.GetNodeStatus = proLogic.GetNodeStatus
|
||||
logic.IsOAuthConfigured = auth.IsOAuthConfigured
|
||||
logic.ResetAuthProvider = auth.ResetAuthProvider
|
||||
logic.ResetIDPSyncHook = auth.ResetIDPSyncHook
|
||||
logic.EmailInit = email.Init
|
||||
|
|
Loading…
Add table
Reference in a new issue