mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 18:09:07 +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"
|
||||
baseapp "go.signoz.io/signoz/pkg/query-service/app"
|
||||
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"
|
||||
"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) {
|
||||
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"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"go.signoz.io/signoz/ee/query-service/constants"
|
||||
"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
|
||||
invite, err := baseauth.ValidateInvite(ctx, req)
|
||||
if err != nil || invite == nil {
|
||||
if err != nil {
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// receiveSAML completes a SAML request and gets user logged in
|
||||
func (ah *APIHandler) receiveSAML(w http.ResponseWriter, r *http.Request) {
|
||||
// this is the source url that initiated the login request
|
||||
redirectUri := constants.GetDefaultSiteURL()
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
if !ah.CheckFeature(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)
|
||||
|
@ -153,6 +153,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
type: UPDATE_CURRENT_VERSION,
|
||||
payload: {
|
||||
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 getUserVersion from 'api/user/getVersion';
|
||||
import loginApi from 'api/user/login';
|
||||
import loginPrecheckApi from 'api/user/loginPrecheck';
|
||||
import afterLogin from 'AppRoutes/utils';
|
||||
@ -7,6 +8,7 @@ import { useNotifications } from 'hooks/useNotifications';
|
||||
import history from 'lib/history';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQuery } from 'react-query';
|
||||
import { PayloadProps as PrecheckResultType } from 'types/api/user/loginPrecheck';
|
||||
|
||||
import { FormContainer, FormWrapper, Label, ParentContainer } from './styles';
|
||||
@ -45,6 +47,26 @@ function Login({
|
||||
|
||||
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(() => {
|
||||
if (withPassword === 'Y') {
|
||||
setPrecheckComplete(true);
|
||||
@ -255,20 +277,6 @@ function Login({
|
||||
</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 && (
|
||||
<Typography.Paragraph italic style={{ color: '#ACACAC' }}>
|
||||
{t('prompt_if_admin')}{' '}
|
||||
|
@ -57,6 +57,8 @@ const InitialValue: InitialValueTypes = {
|
||||
role: null,
|
||||
configs: {},
|
||||
userFlags: {},
|
||||
ee: 'Y',
|
||||
setupCompleted: true,
|
||||
};
|
||||
|
||||
const appReducer = (
|
||||
@ -89,6 +91,8 @@ const appReducer = (
|
||||
return {
|
||||
...state,
|
||||
currentVersion: action.payload.currentVersion,
|
||||
ee: action.payload.ee,
|
||||
setupCompleted: action.payload.setupCompleted,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,8 @@ export interface UpdateAppVersion {
|
||||
type: typeof UPDATE_CURRENT_VERSION;
|
||||
payload: {
|
||||
currentVersion: AppReducer['currentVersion'];
|
||||
ee: AppReducer['ee'];
|
||||
setupCompleted: AppReducer['setupCompleted'];
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
export interface PayloadProps {
|
||||
version: string;
|
||||
ee: string;
|
||||
ee: 'Y' | 'N';
|
||||
setupCompleted: boolean;
|
||||
}
|
||||
|
@ -29,4 +29,6 @@ export default interface AppReducer {
|
||||
featureFlags: null | FeatureFlagPayload;
|
||||
configs: ConfigPayload;
|
||||
userFlags: null | UserFlags;
|
||||
ee: 'Y' | 'N';
|
||||
setupCompleted: boolean;
|
||||
}
|
||||
|
@ -61,6 +61,11 @@ type APIHandler struct {
|
||||
ruleManager *rules.Manager
|
||||
featureFlags interfaces.FeatureLookup
|
||||
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 {
|
||||
@ -100,6 +105,19 @@ func NewAPIHandler(opts APIHandlerOpts) (*APIHandler, error) {
|
||||
// if errReadingDashboards != nil {
|
||||
// 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
|
||||
}
|
||||
|
||||
@ -1645,7 +1663,13 @@ func (aH *APIHandler) getDisks(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (aH *APIHandler) getVersion(w http.ResponseWriter, r *http.Request) {
|
||||
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) {
|
||||
@ -1777,6 +1801,12 @@ func (aH *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
||||
if req.InviteToken == "" {
|
||||
return nil, model.BadRequest(fmt.Errorf("invite token is required"))
|
||||
return nil, model.BadRequest(ErrorAskAdmin)
|
||||
}
|
||||
|
||||
if !nopassword && req.Password == "" {
|
||||
|
@ -15,7 +15,7 @@ var (
|
||||
ErrorInvalidRole = errors.New("Invalid role")
|
||||
|
||||
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) {
|
||||
|
@ -19,6 +19,7 @@ type Queries interface {
|
||||
GetUser(ctx context.Context, id string) (*model.UserPayload, *model.ApiError)
|
||||
GetUserByEmail(ctx context.Context, email string) (*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)
|
||||
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
|
||||
}
|
||||
|
||||
// GetUsers fetches total user count
|
||||
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{}
|
||||
|
||||
query := `select
|
||||
@ -364,6 +370,9 @@ func (mds *ModelDaoSqlite) GetUsers(ctx context.Context) ([]model.UserPayload, *
|
||||
g.id = u.group_id and
|
||||
o.id = u.org_id`
|
||||
|
||||
if limit > 0 {
|
||||
query = fmt.Sprintf("%s LIMIT %d", query, limit)
|
||||
}
|
||||
err := mds.db.Select(&users, query)
|
||||
|
||||
if err != nil {
|
||||
|
@ -585,3 +585,9 @@ func (ci *ClusterInfo) GetMapFromStruct() map[string]interface{} {
|
||||
json.Unmarshal(data, &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