mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 20:38:59 +08:00
fix: changed ask admin message (#2215)
This commit is contained in:
parent
1f44f089e0
commit
ab514cc0f2
@ -9,6 +9,7 @@ import (
|
|||||||
"go.signoz.io/signoz/ee/query-service/license"
|
"go.signoz.io/signoz/ee/query-service/license"
|
||||||
baseapp "go.signoz.io/signoz/pkg/query-service/app"
|
baseapp "go.signoz.io/signoz/pkg/query-service/app"
|
||||||
baseint "go.signoz.io/signoz/pkg/query-service/interfaces"
|
baseint "go.signoz.io/signoz/pkg/query-service/interfaces"
|
||||||
|
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||||
rules "go.signoz.io/signoz/pkg/query-service/rules"
|
rules "go.signoz.io/signoz/pkg/query-service/rules"
|
||||||
"go.signoz.io/signoz/pkg/query-service/version"
|
"go.signoz.io/signoz/pkg/query-service/version"
|
||||||
)
|
)
|
||||||
@ -127,5 +128,11 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router) {
|
|||||||
|
|
||||||
func (ah *APIHandler) getVersion(w http.ResponseWriter, r *http.Request) {
|
func (ah *APIHandler) getVersion(w http.ResponseWriter, r *http.Request) {
|
||||||
version := version.GetVersion()
|
version := version.GetVersion()
|
||||||
ah.WriteJSON(w, r, map[string]string{"version": version, "ee": "Y"})
|
versionResponse := basemodel.GetVersionResponse{
|
||||||
|
Version: version,
|
||||||
|
EE: "Y",
|
||||||
|
SetupCompleted: ah.SetupCompleted,
|
||||||
|
}
|
||||||
|
|
||||||
|
ah.WriteJSON(w, r, versionResponse)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"go.signoz.io/signoz/ee/query-service/constants"
|
"go.signoz.io/signoz/ee/query-service/constants"
|
||||||
"go.signoz.io/signoz/ee/query-service/model"
|
"go.signoz.io/signoz/ee/query-service/model"
|
||||||
@ -87,9 +88,16 @@ func (ah *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// get invite object
|
// get invite object
|
||||||
invite, err := baseauth.ValidateInvite(ctx, req)
|
invite, err := baseauth.ValidateInvite(ctx, req)
|
||||||
if err != nil || invite == nil {
|
if err != nil {
|
||||||
zap.S().Errorf("failed to validate invite token", err)
|
zap.S().Errorf("failed to validate invite token", err)
|
||||||
|
RespondError(w, model.BadRequest(err), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if invite == nil {
|
||||||
|
zap.S().Errorf("failed to validate invite token: it is either empty or invalid", err)
|
||||||
RespondError(w, model.BadRequest(basemodel.ErrSignupFailed{}), nil)
|
RespondError(w, model.BadRequest(basemodel.ErrSignupFailed{}), nil)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get auth domain from email domain
|
// get auth domain from email domain
|
||||||
@ -250,15 +258,12 @@ func (ah *APIHandler) receiveGoogleAuth(w http.ResponseWriter, r *http.Request)
|
|||||||
http.Redirect(w, r, nextPage, http.StatusSeeOther)
|
http.Redirect(w, r, nextPage, http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// receiveSAML completes a SAML request and gets user logged in
|
// receiveSAML completes a SAML request and gets user logged in
|
||||||
func (ah *APIHandler) receiveSAML(w http.ResponseWriter, r *http.Request) {
|
func (ah *APIHandler) receiveSAML(w http.ResponseWriter, r *http.Request) {
|
||||||
// this is the source url that initiated the login request
|
// this is the source url that initiated the login request
|
||||||
redirectUri := constants.GetDefaultSiteURL()
|
redirectUri := constants.GetDefaultSiteURL()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
|
||||||
if !ah.CheckFeature(model.SSO) {
|
if !ah.CheckFeature(model.SSO) {
|
||||||
zap.S().Errorf("[receiveSAML] sso requested but feature unavailable %s in org domain %s", model.SSO)
|
zap.S().Errorf("[receiveSAML] sso requested but feature unavailable %s in org domain %s", model.SSO)
|
||||||
http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "feature unavailable, please upgrade your billing plan to access this feature"), http.StatusMovedPermanently)
|
http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "feature unavailable, please upgrade your billing plan to access this feature"), http.StatusMovedPermanently)
|
||||||
|
@ -153,6 +153,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
type: UPDATE_CURRENT_VERSION,
|
type: UPDATE_CURRENT_VERSION,
|
||||||
payload: {
|
payload: {
|
||||||
currentVersion: getUserVersionResponse.data.payload.version,
|
currentVersion: getUserVersionResponse.data.payload.version,
|
||||||
|
ee: getUserVersionResponse.data.payload.ee,
|
||||||
|
setupCompleted: getUserVersionResponse.data.payload.setupCompleted,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Button, Input, Space, Tooltip, Typography } from 'antd';
|
import { Button, Input, Space, Tooltip, Typography } from 'antd';
|
||||||
|
import getUserVersion from 'api/user/getVersion';
|
||||||
import loginApi from 'api/user/login';
|
import loginApi from 'api/user/login';
|
||||||
import loginPrecheckApi from 'api/user/loginPrecheck';
|
import loginPrecheckApi from 'api/user/loginPrecheck';
|
||||||
import afterLogin from 'AppRoutes/utils';
|
import afterLogin from 'AppRoutes/utils';
|
||||||
@ -7,6 +8,7 @@ import { useNotifications } from 'hooks/useNotifications';
|
|||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
import { PayloadProps as PrecheckResultType } from 'types/api/user/loginPrecheck';
|
import { PayloadProps as PrecheckResultType } from 'types/api/user/loginPrecheck';
|
||||||
|
|
||||||
import { FormContainer, FormWrapper, Label, ParentContainer } from './styles';
|
import { FormContainer, FormWrapper, Label, ParentContainer } from './styles';
|
||||||
@ -45,6 +47,26 @@ function Login({
|
|||||||
|
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
|
|
||||||
|
const getUserVersionResponse = useQuery({
|
||||||
|
queryFn: getUserVersion,
|
||||||
|
queryKey: 'getUserVersion',
|
||||||
|
enabled: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
getUserVersionResponse.isFetched &&
|
||||||
|
getUserVersionResponse.data &&
|
||||||
|
getUserVersionResponse.data.payload
|
||||||
|
) {
|
||||||
|
const { setupCompleted } = getUserVersionResponse.data.payload;
|
||||||
|
if (!setupCompleted) {
|
||||||
|
// no org account registered yet, re-route user to sign up first
|
||||||
|
history.push(ROUTES.SIGN_UP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [getUserVersionResponse]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (withPassword === 'Y') {
|
if (withPassword === 'Y') {
|
||||||
setPrecheckComplete(true);
|
setPrecheckComplete(true);
|
||||||
@ -255,20 +277,6 @@ function Login({
|
|||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!canSelfRegister && (
|
|
||||||
<Typography.Paragraph italic style={{ color: '#ACACAC' }}>
|
|
||||||
{t('prompt_create_account')}{' '}
|
|
||||||
<Typography.Link
|
|
||||||
onClick={(): void => {
|
|
||||||
history.push(ROUTES.SIGN_UP);
|
|
||||||
}}
|
|
||||||
style={{ fontWeight: 700 }}
|
|
||||||
>
|
|
||||||
{t('create_an_account')}
|
|
||||||
</Typography.Link>
|
|
||||||
</Typography.Paragraph>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{canSelfRegister && (
|
{canSelfRegister && (
|
||||||
<Typography.Paragraph italic style={{ color: '#ACACAC' }}>
|
<Typography.Paragraph italic style={{ color: '#ACACAC' }}>
|
||||||
{t('prompt_if_admin')}{' '}
|
{t('prompt_if_admin')}{' '}
|
||||||
|
@ -57,6 +57,8 @@ const InitialValue: InitialValueTypes = {
|
|||||||
role: null,
|
role: null,
|
||||||
configs: {},
|
configs: {},
|
||||||
userFlags: {},
|
userFlags: {},
|
||||||
|
ee: 'Y',
|
||||||
|
setupCompleted: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const appReducer = (
|
const appReducer = (
|
||||||
@ -89,6 +91,8 @@ const appReducer = (
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentVersion: action.payload.currentVersion,
|
currentVersion: action.payload.currentVersion,
|
||||||
|
ee: action.payload.ee,
|
||||||
|
setupCompleted: action.payload.setupCompleted,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,8 @@ export interface UpdateAppVersion {
|
|||||||
type: typeof UPDATE_CURRENT_VERSION;
|
type: typeof UPDATE_CURRENT_VERSION;
|
||||||
payload: {
|
payload: {
|
||||||
currentVersion: AppReducer['currentVersion'];
|
currentVersion: AppReducer['currentVersion'];
|
||||||
|
ee: AppReducer['ee'];
|
||||||
|
setupCompleted: AppReducer['setupCompleted'];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
export interface PayloadProps {
|
export interface PayloadProps {
|
||||||
version: string;
|
version: string;
|
||||||
ee: string;
|
ee: 'Y' | 'N';
|
||||||
|
setupCompleted: boolean;
|
||||||
}
|
}
|
||||||
|
@ -29,4 +29,6 @@ export default interface AppReducer {
|
|||||||
featureFlags: null | FeatureFlagPayload;
|
featureFlags: null | FeatureFlagPayload;
|
||||||
configs: ConfigPayload;
|
configs: ConfigPayload;
|
||||||
userFlags: null | UserFlags;
|
userFlags: null | UserFlags;
|
||||||
|
ee: 'Y' | 'N';
|
||||||
|
setupCompleted: boolean;
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,11 @@ type APIHandler struct {
|
|||||||
ruleManager *rules.Manager
|
ruleManager *rules.Manager
|
||||||
featureFlags interfaces.FeatureLookup
|
featureFlags interfaces.FeatureLookup
|
||||||
ready func(http.HandlerFunc) http.HandlerFunc
|
ready func(http.HandlerFunc) http.HandlerFunc
|
||||||
|
|
||||||
|
// SetupCompleted indicates if SigNoz is ready for general use.
|
||||||
|
// at the moment, we mark the app ready when the first user
|
||||||
|
// is registers.
|
||||||
|
SetupCompleted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type APIHandlerOpts struct {
|
type APIHandlerOpts struct {
|
||||||
@ -100,6 +105,19 @@ func NewAPIHandler(opts APIHandlerOpts) (*APIHandler, error) {
|
|||||||
// if errReadingDashboards != nil {
|
// if errReadingDashboards != nil {
|
||||||
// return nil, errReadingDashboards
|
// return nil, errReadingDashboards
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// check if at least one user is created
|
||||||
|
hasUsers, err := aH.appDao.GetUsersWithOpts(context.Background(), 1)
|
||||||
|
if err.Error() != "" {
|
||||||
|
// raise warning but no panic as this is a recoverable condition
|
||||||
|
zap.S().Warnf("unexpected error while fetch user count while initializing base api handler", err.Error())
|
||||||
|
}
|
||||||
|
if len(hasUsers) != 0 {
|
||||||
|
// first user is already created, we can mark the app ready for general use.
|
||||||
|
// this means, we disable self-registration and expect new users
|
||||||
|
// to signup signoz through invite link only.
|
||||||
|
aH.SetupCompleted = true
|
||||||
|
}
|
||||||
return aH, nil
|
return aH, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1645,7 +1663,13 @@ func (aH *APIHandler) getDisks(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (aH *APIHandler) getVersion(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) getVersion(w http.ResponseWriter, r *http.Request) {
|
||||||
version := version.GetVersion()
|
version := version.GetVersion()
|
||||||
aH.WriteJSON(w, r, map[string]string{"version": version, "ee": "N"})
|
versionResponse := model.GetVersionResponse{
|
||||||
|
Version: version,
|
||||||
|
EE: "Y",
|
||||||
|
SetupCompleted: aH.SetupCompleted,
|
||||||
|
}
|
||||||
|
|
||||||
|
aH.WriteJSON(w, r, versionResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -1777,6 +1801,12 @@ func (aH *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !aH.SetupCompleted {
|
||||||
|
// since the first user is now created, we can disable self-registration as
|
||||||
|
// from here onwards, we expect admin (owner) to invite other users.
|
||||||
|
aH.SetupCompleted = true
|
||||||
|
}
|
||||||
|
|
||||||
aH.Respond(w, nil)
|
aH.Respond(w, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*model.User,
|
|||||||
func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword bool) (*model.User, *model.ApiError) {
|
func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword bool) (*model.User, *model.ApiError) {
|
||||||
|
|
||||||
if req.InviteToken == "" {
|
if req.InviteToken == "" {
|
||||||
return nil, model.BadRequest(fmt.Errorf("invite token is required"))
|
return nil, model.BadRequest(ErrorAskAdmin)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !nopassword && req.Password == "" {
|
if !nopassword && req.Password == "" {
|
||||||
|
@ -15,7 +15,7 @@ var (
|
|||||||
ErrorInvalidRole = errors.New("Invalid role")
|
ErrorInvalidRole = errors.New("Invalid role")
|
||||||
|
|
||||||
ErrorInvalidInviteToken = errors.New("Invalid invite token")
|
ErrorInvalidInviteToken = errors.New("Invalid invite token")
|
||||||
ErrorAskAdmin = errors.New("You are not allowed to create an account. Please ask your admin to send an invite link")
|
ErrorAskAdmin = errors.New("An invitation is needed to create an account. Please ask your admin (the person who has first installed SIgNoz) to send an invite.")
|
||||||
)
|
)
|
||||||
|
|
||||||
func randomHex(sz int) (string, error) {
|
func randomHex(sz int) (string, error) {
|
||||||
|
@ -19,6 +19,7 @@ type Queries interface {
|
|||||||
GetUser(ctx context.Context, id string) (*model.UserPayload, *model.ApiError)
|
GetUser(ctx context.Context, id string) (*model.UserPayload, *model.ApiError)
|
||||||
GetUserByEmail(ctx context.Context, email string) (*model.UserPayload, *model.ApiError)
|
GetUserByEmail(ctx context.Context, email string) (*model.UserPayload, *model.ApiError)
|
||||||
GetUsers(ctx context.Context) ([]model.UserPayload, *model.ApiError)
|
GetUsers(ctx context.Context) ([]model.UserPayload, *model.ApiError)
|
||||||
|
GetUsersWithOpts(ctx context.Context, limit int) ([]model.UserPayload, *model.ApiError)
|
||||||
|
|
||||||
GetGroup(ctx context.Context, id string) (*model.Group, *model.ApiError)
|
GetGroup(ctx context.Context, id string) (*model.Group, *model.ApiError)
|
||||||
GetGroupByName(ctx context.Context, name string) (*model.Group, *model.ApiError)
|
GetGroupByName(ctx context.Context, name string) (*model.Group, *model.ApiError)
|
||||||
|
@ -345,7 +345,13 @@ func (mds *ModelDaoSqlite) GetUserByEmail(ctx context.Context,
|
|||||||
return &users[0], nil
|
return &users[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUsers fetches total user count
|
||||||
func (mds *ModelDaoSqlite) GetUsers(ctx context.Context) ([]model.UserPayload, *model.ApiError) {
|
func (mds *ModelDaoSqlite) GetUsers(ctx context.Context) ([]model.UserPayload, *model.ApiError) {
|
||||||
|
return mds.GetUsersWithOpts(ctx, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsersWithOpts fetches users and supports additional search options
|
||||||
|
func (mds *ModelDaoSqlite) GetUsersWithOpts(ctx context.Context, limit int) ([]model.UserPayload, *model.ApiError) {
|
||||||
users := []model.UserPayload{}
|
users := []model.UserPayload{}
|
||||||
|
|
||||||
query := `select
|
query := `select
|
||||||
@ -364,6 +370,9 @@ func (mds *ModelDaoSqlite) GetUsers(ctx context.Context) ([]model.UserPayload, *
|
|||||||
g.id = u.group_id and
|
g.id = u.group_id and
|
||||||
o.id = u.org_id`
|
o.id = u.org_id`
|
||||||
|
|
||||||
|
if limit > 0 {
|
||||||
|
query = fmt.Sprintf("%s LIMIT %d", query, limit)
|
||||||
|
}
|
||||||
err := mds.db.Select(&users, query)
|
err := mds.db.Select(&users, query)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -585,3 +585,9 @@ func (ci *ClusterInfo) GetMapFromStruct() map[string]interface{} {
|
|||||||
json.Unmarshal(data, &clusterInfoMap)
|
json.Unmarshal(data, &clusterInfoMap)
|
||||||
return clusterInfoMap
|
return clusterInfoMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetVersionResponse struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
EE string `json:"ee"`
|
||||||
|
SetupCompleted bool `json:"setupCompleted"`
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user