mirror of https://github.com/portainer/portainer
feat(waiting-room): show and filter by check in [EE-5186] (#8701)
parent
b5771df6a8
commit
4b9c857d85
|
@ -34,7 +34,7 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
|||
idxEdgeID: make(map[string]portainer.EndpointID),
|
||||
}
|
||||
|
||||
es, err := s.Endpoints()
|
||||
es, err := s.endpoints()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -89,8 +89,7 @@ func (service *Service) DeleteEndpoint(ID portainer.EndpointID) error {
|
|||
})
|
||||
}
|
||||
|
||||
// Endpoints return an array containing all the environments(endpoints).
|
||||
func (service *Service) Endpoints() ([]portainer.Endpoint, error) {
|
||||
func (service *Service) endpoints() ([]portainer.Endpoint, error) {
|
||||
var endpoints []portainer.Endpoint
|
||||
var err error
|
||||
|
||||
|
@ -99,8 +98,14 @@ func (service *Service) Endpoints() ([]portainer.Endpoint, error) {
|
|||
return err
|
||||
})
|
||||
|
||||
return endpoints, err
|
||||
}
|
||||
|
||||
// Endpoints return an array containing all the environments(endpoints).
|
||||
func (service *Service) Endpoints() ([]portainer.Endpoint, error) {
|
||||
endpoints, err := service.endpoints()
|
||||
if err != nil {
|
||||
return endpoints, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, e := range endpoints {
|
||||
|
|
|
@ -117,6 +117,11 @@ func (handler *Handler) endpointEdgeStatusInspect(w http.ResponseWriter, r *http
|
|||
return httperror.InternalServerError("Unable to Unable to persist environment changes inside the database", err)
|
||||
}
|
||||
|
||||
err = handler.requestBouncer.TrustedEdgeEnvironmentAccess(endpoint)
|
||||
if err != nil {
|
||||
return httperror.Forbidden("Permission denied to access environment", err)
|
||||
}
|
||||
|
||||
checkinInterval := endpoint.EdgeCheckinInterval
|
||||
if endpoint.EdgeCheckinInterval == 0 {
|
||||
settings, err := handler.DataStore.Settings().Settings()
|
||||
|
|
|
@ -45,6 +45,7 @@ const (
|
|||
// @param agentVersions query []string false "will return only environments with on of these agent versions"
|
||||
// @param edgeAsync query bool false "if exists true show only edge async agents, false show only standard edge agents. if missing, will show both types (relevant only for edge agents)"
|
||||
// @param edgeDeviceUntrusted query bool false "if true, show only untrusted edge agents, if false show only trusted edge agents (relevant only for edge agents)"
|
||||
// @param edgeCheckInPassedSeconds query number false "if bigger then zero, show only edge agents that checked-in in the last provided seconds (relevant only for edge agents)"
|
||||
// @param name query string false "will return only environments(endpoints) with this name"
|
||||
// @success 200 {array} portainer.Endpoint "Endpoints"
|
||||
// @failure 500 "Server error"
|
||||
|
|
|
@ -23,11 +23,12 @@ type EnvironmentsQuery struct {
|
|||
groupIds []portainer.EndpointGroupID
|
||||
status []portainer.EndpointStatus
|
||||
// if edgeAsync not nil, will filter edge endpoints based on this value
|
||||
edgeAsync *bool
|
||||
edgeDeviceUntrusted bool
|
||||
excludeSnapshots bool
|
||||
name string
|
||||
agentVersions []string
|
||||
edgeAsync *bool
|
||||
edgeDeviceUntrusted bool
|
||||
excludeSnapshots bool
|
||||
name string
|
||||
agentVersions []string
|
||||
edgeCheckInPassedSeconds int
|
||||
}
|
||||
|
||||
func parseQuery(r *http.Request) (EnvironmentsQuery, error) {
|
||||
|
@ -77,19 +78,22 @@ func parseQuery(r *http.Request) (EnvironmentsQuery, error) {
|
|||
|
||||
excludeSnapshots, _ := request.RetrieveBooleanQueryParameter(r, "excludeSnapshots", true)
|
||||
|
||||
edgeCheckInPassedSeconds, _ := request.RetrieveNumericQueryParameter(r, "edgeCheckInPassedSeconds", true)
|
||||
|
||||
return EnvironmentsQuery{
|
||||
search: search,
|
||||
types: endpointTypes,
|
||||
tagIds: tagIDs,
|
||||
endpointIds: endpointIDs,
|
||||
tagsPartialMatch: tagsPartialMatch,
|
||||
groupIds: groupIDs,
|
||||
status: status,
|
||||
edgeAsync: edgeAsync,
|
||||
edgeDeviceUntrusted: edgeDeviceUntrusted,
|
||||
excludeSnapshots: excludeSnapshots,
|
||||
name: name,
|
||||
agentVersions: agentVersions,
|
||||
search: search,
|
||||
types: endpointTypes,
|
||||
tagIds: tagIDs,
|
||||
endpointIds: endpointIDs,
|
||||
tagsPartialMatch: tagsPartialMatch,
|
||||
groupIds: groupIDs,
|
||||
status: status,
|
||||
edgeAsync: edgeAsync,
|
||||
edgeDeviceUntrusted: edgeDeviceUntrusted,
|
||||
excludeSnapshots: excludeSnapshots,
|
||||
name: name,
|
||||
agentVersions: agentVersions,
|
||||
edgeCheckInPassedSeconds: edgeCheckInPassedSeconds,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -128,6 +132,22 @@ func (handler *Handler) filterEndpointsByQuery(filteredEndpoints []portainer.End
|
|||
return endpoint.UserTrusted == !query.edgeDeviceUntrusted
|
||||
})
|
||||
|
||||
if query.edgeCheckInPassedSeconds > 0 {
|
||||
filteredEndpoints = filter(filteredEndpoints, func(endpoint portainer.Endpoint) bool {
|
||||
// ignore non-edge endpoints
|
||||
if !endpointutils.IsEdgeEndpoint(&endpoint) {
|
||||
return true
|
||||
}
|
||||
|
||||
// filter out endpoints that have never checked in
|
||||
if endpoint.LastCheckInDate == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return time.Now().Unix()-endpoint.LastCheckInDate < int64(query.edgeCheckInPassedSeconds)
|
||||
})
|
||||
}
|
||||
|
||||
if len(query.status) > 0 {
|
||||
filteredEndpoints = filterEndpointsByStatuses(filteredEndpoints, query.status, settings)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package security
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
httperror "github.com/portainer/libhttp/error"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/apikey"
|
||||
|
@ -147,13 +146,19 @@ func (bouncer *RequestBouncer) AuthorizedEdgeEndpointOperation(r *http.Request,
|
|||
return errors.New("invalid Edge identifier")
|
||||
}
|
||||
|
||||
if endpoint.LastCheckInDate > 0 || endpoint.UserTrusted {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TrustedEdgeEnvironmentAccess defines a security check for Edge environments, checks if
|
||||
// the request is coming from a trusted Edge environment
|
||||
func (bouncer *RequestBouncer) TrustedEdgeEnvironmentAccess(endpoint *portainer.Endpoint) error {
|
||||
if endpoint.UserTrusted {
|
||||
return nil
|
||||
}
|
||||
|
||||
settings, err := bouncer.dataStore.Settings().Settings()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not retrieve the settings: %w", err)
|
||||
return errors.WithMessage(err, "could not retrieve the settings")
|
||||
}
|
||||
|
||||
if !settings.TrustOnFirstConnect {
|
||||
|
|
|
@ -3,8 +3,18 @@ import { useGroups } from '@/react/portainer/environments/environment-groups/que
|
|||
import { useEdgeGroups } from '@/react/edge/edge-groups/queries/useEdgeGroups';
|
||||
import { useTags } from '@/portainer/tags/queries';
|
||||
|
||||
import { PortainerSelect } from '@@/form-components/PortainerSelect';
|
||||
|
||||
import { useFilterStore } from './filter-store';
|
||||
|
||||
const checkInOptions = [
|
||||
{ value: 0, label: 'Show all time' },
|
||||
{ value: 60 * 60, label: 'Show past hour' },
|
||||
{ value: 60 * 60 * 24, label: 'Show past day' },
|
||||
{ value: 60 * 60 * 24 * 7, label: 'Show past week' },
|
||||
{ value: 60 * 60 * 24 * 14, label: 'Show past 14 days' },
|
||||
];
|
||||
|
||||
export function Filter() {
|
||||
const edgeGroupsQuery = useEdgeGroups();
|
||||
const groupsQuery = useGroups();
|
||||
|
@ -45,6 +55,14 @@ export function Filter() {
|
|||
value: g.ID,
|
||||
}))}
|
||||
/>
|
||||
|
||||
<div className="ml-auto" />
|
||||
<PortainerSelect
|
||||
onChange={(f) => filterStore.setCheckIn(f || 0)}
|
||||
value={filterStore.checkIn}
|
||||
options={checkInOptions}
|
||||
bindToBody
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import moment from 'moment';
|
||||
import { CellProps, Column } from 'react-table';
|
||||
|
||||
import { WaitingRoomEnvironment } from '../types';
|
||||
|
@ -52,4 +53,24 @@ export const columns: readonly Column<WaitingRoomEnvironment>[] = [
|
|||
canHide: false,
|
||||
sortType: 'string',
|
||||
},
|
||||
{
|
||||
Header: 'Last Check-in',
|
||||
accessor: 'LastCheckInDate',
|
||||
Cell: LastCheckinDateCell,
|
||||
id: 'last-check-in',
|
||||
disableFilters: true,
|
||||
Filter: () => null,
|
||||
canHide: false,
|
||||
sortType: 'string',
|
||||
},
|
||||
] as const;
|
||||
|
||||
function LastCheckinDateCell({
|
||||
value,
|
||||
}: CellProps<WaitingRoomEnvironment, number>) {
|
||||
if (!value) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return moment(value * 1000).fromNow();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ interface TableFiltersStore {
|
|||
setEdgeGroups(value: number[]): void;
|
||||
tags: number[];
|
||||
setTags(value: number[]): void;
|
||||
checkIn: number;
|
||||
setCheckIn(value: number): void;
|
||||
}
|
||||
|
||||
export const useFilterStore = createStore<TableFiltersStore>()(
|
||||
|
@ -27,6 +29,10 @@ export const useFilterStore = createStore<TableFiltersStore>()(
|
|||
setTags(tags: number[]) {
|
||||
set({ tags });
|
||||
},
|
||||
checkIn: 0,
|
||||
setCheckIn(checkIn: number) {
|
||||
set({ checkIn });
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: keyBuilder('edge-devices-meta-filters'),
|
||||
|
|
|
@ -30,6 +30,7 @@ export function useEnvironments() {
|
|||
tagIds: filterStore.tags.length ? filterStore.tags : undefined,
|
||||
groupIds: filterStore.groups.length ? filterStore.groups : undefined,
|
||||
endpointIds: filterByEnvironmentsIds,
|
||||
edgeCheckInPassedSeconds: filterStore.checkIn,
|
||||
});
|
||||
|
||||
const groupsQuery = useGroups({
|
||||
|
|
|
@ -29,6 +29,7 @@ export interface EnvironmentsQueryParams {
|
|||
name?: string;
|
||||
agentVersions?: string[];
|
||||
updateInformation?: boolean;
|
||||
edgeCheckInPassedSeconds?: number;
|
||||
}
|
||||
|
||||
export interface GetEnvironmentsOptions {
|
||||
|
|
Loading…
Reference in New Issue