Merge branch 'next' into version-2021.5
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> # Conflicts: # outpost/pkg/version.go
This commit is contained in:
commit
e91ff4566d
|
@ -27,7 +27,7 @@ jobs:
|
|||
-f Dockerfile .
|
||||
docker-compose up --no-start
|
||||
docker-compose start postgresql redis
|
||||
docker-compose run -u root --entrypoint /bin/bash server -c "pip install --no-cache -r requirements-dev.txt && ./manage.py test authentik"
|
||||
docker-compose run -u root --entrypoint /bin/bash server -c "apt-get update && apt-get install -y --no-install-recommends git && pip install --no-cache -r requirements-dev.txt && ./manage.py test authentik"
|
||||
- name: Extract version number
|
||||
id: get_version
|
||||
uses: actions/github-script@0.2.0
|
||||
|
|
|
@ -2,22 +2,25 @@
|
|||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.groups import GroupSerializer
|
||||
from authentik.events.models import NotificationRule
|
||||
|
||||
|
||||
class NotificationRuleSerializer(ModelSerializer):
|
||||
"""NotificationRule Serializer"""
|
||||
|
||||
group_obj = GroupSerializer(read_only=True, source="group")
|
||||
|
||||
class Meta:
|
||||
|
||||
model = NotificationRule
|
||||
depth = 2
|
||||
fields = [
|
||||
"pk",
|
||||
"name",
|
||||
"transports",
|
||||
"severity",
|
||||
"group",
|
||||
"group_obj",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ outposts:
|
|||
# Placeholders:
|
||||
# %(type)s: Outpost type; proxy, ldap, etc
|
||||
# %(version)s: Current version; 2021.4.1
|
||||
# %(build_hash)s: Build hash if you're running a beta version
|
||||
docker_image_base: "beryju/authentik-%(type)s:%(version)s"
|
||||
|
||||
authentik:
|
||||
|
|
|
@ -18,8 +18,6 @@ class OutpostSerializer(ModelSerializer):
|
|||
"""Outpost Serializer"""
|
||||
|
||||
config = JSONField(validators=[is_dict], source="_config")
|
||||
# TODO: Remove _config again, this is only here for legacy with older outposts
|
||||
_config = JSONField(validators=[is_dict], read_only=True)
|
||||
providers_obj = ProviderSerializer(source="providers", many=True, read_only=True)
|
||||
|
||||
def validate_config(self, config) -> dict:
|
||||
|
@ -42,7 +40,6 @@ class OutpostSerializer(ModelSerializer):
|
|||
"service_connection",
|
||||
"token_identifier",
|
||||
"config",
|
||||
"_config",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ class OutpostConsumer(AuthJsonConsumer):
|
|||
)
|
||||
if msg.instruction == WebsocketMessageInstruction.HELLO:
|
||||
state.version = msg.args.get("version", None)
|
||||
state.build_hash = msg.args.get("buildHash", "")
|
||||
elif msg.instruction == WebsocketMessageInstruction.ACK:
|
||||
return
|
||||
state.save(timeout=OUTPOST_HELLO_INTERVAL * 1.5)
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
"""Base Controller"""
|
||||
from dataclasses import dataclass
|
||||
from os import environ
|
||||
from typing import Optional
|
||||
|
||||
from structlog.stdlib import get_logger
|
||||
from structlog.testing import capture_logs
|
||||
|
||||
from authentik import __version__
|
||||
from authentik import ENV_GIT_HASH_KEY, __version__
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.sentry import SentryIgnoredException
|
||||
from authentik.outposts.models import Outpost, OutpostServiceConnection
|
||||
|
@ -69,4 +70,8 @@ class BaseController:
|
|||
def get_container_image(self) -> str:
|
||||
"""Get container image to use for this outpost"""
|
||||
image_name_template: str = CONFIG.y("outposts.docker_image_base")
|
||||
return image_name_template % {"type": self.outpost.type, "version": __version__}
|
||||
return image_name_template % {
|
||||
"type": self.outpost.type,
|
||||
"version": __version__,
|
||||
"build_hash": environ.get(ENV_GIT_HASH_KEY, ""),
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"""Outpost models"""
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from datetime import datetime
|
||||
from os import environ
|
||||
from typing import Iterable, Optional, Union
|
||||
from uuid import uuid4
|
||||
|
||||
|
@ -26,7 +27,7 @@ from packaging.version import LegacyVersion, Version, parse
|
|||
from structlog.stdlib import get_logger
|
||||
from urllib3.exceptions import HTTPError
|
||||
|
||||
from authentik import __version__
|
||||
from authentik import ENV_GIT_HASH_KEY, __version__
|
||||
from authentik.core.models import USER_ATTRIBUTE_SA, Provider, Token, TokenIntents, User
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.lib.config import CONFIG
|
||||
|
@ -411,6 +412,7 @@ class OutpostState:
|
|||
last_seen: Optional[datetime] = field(default=None)
|
||||
version: Optional[str] = field(default=None)
|
||||
version_should: Union[Version, LegacyVersion] = field(default=OUR_VERSION)
|
||||
build_hash: str = field(default="")
|
||||
|
||||
_outpost: Optional[Outpost] = field(default=None)
|
||||
|
||||
|
@ -419,6 +421,8 @@ class OutpostState:
|
|||
"""Check if outpost version matches our version"""
|
||||
if not self.version:
|
||||
return False
|
||||
if self.build_hash != environ.get(ENV_GIT_HASH_KEY, ""):
|
||||
return False
|
||||
return parse(self.version) < OUR_VERSION
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -320,6 +320,7 @@ CELERY_RESULT_BACKEND = (
|
|||
# Database backup
|
||||
DBBACKUP_STORAGE = "django.core.files.storage.FileSystemStorage"
|
||||
DBBACKUP_STORAGE_OPTIONS = {"location": "./backups" if DEBUG else "/backups"}
|
||||
DBBACKUP_FILENAME_TEMPLATE = "authentik-backup-{datetime}.sql"
|
||||
if CONFIG.y("postgresql.s3_backup"):
|
||||
DBBACKUP_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
||||
DBBACKUP_STORAGE_OPTIONS = {
|
||||
|
|
|
@ -116,7 +116,10 @@ stages:
|
|||
command: 'buildAndPush'
|
||||
Dockerfile: 'outpost/proxy.Dockerfile'
|
||||
buildContext: 'outpost/'
|
||||
tags: "gh-$(branchName)"
|
||||
tags: |
|
||||
gh-$(branchName)
|
||||
gh-$(Build.SourceVersion)
|
||||
arguments: '--build-arg GIT_BUILD_HASH=$(Build.SourceVersion)'
|
||||
- job: ldap_build_docker
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
@ -141,4 +144,7 @@ stages:
|
|||
command: 'buildAndPush'
|
||||
Dockerfile: 'outpost/ldap.Dockerfile'
|
||||
buildContext: 'outpost/'
|
||||
tags: "gh-$(branchName)"
|
||||
tags: |
|
||||
gh-$(branchName)
|
||||
gh-$(Build.SourceVersion)
|
||||
arguments: '--build-arg GIT_BUILD_HASH=$(Build.SourceVersion)'
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
FROM golang:1.16.4 AS builder
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package ak
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -43,7 +42,7 @@ type APIController struct {
|
|||
// NewAPIController initialise new API Controller instance from URL and API token
|
||||
func NewAPIController(akURL url.URL, token string) *APIController {
|
||||
transport := httptransport.New(akURL.Host, client.DefaultBasePath, []string{akURL.Scheme})
|
||||
transport.Transport = SetUserAgent(getTLSTransport(), fmt.Sprintf("authentik-proxy@%s", pkg.VERSION))
|
||||
transport.Transport = SetUserAgent(getTLSTransport(), pkg.UserAgent())
|
||||
|
||||
// create the transport
|
||||
auth := httptransport.BearerToken(token)
|
||||
|
|
|
@ -23,7 +23,7 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) {
|
|||
|
||||
header := http.Header{
|
||||
"Authorization": []string{authHeader},
|
||||
"User-Agent": []string{fmt.Sprintf("authentik-proxy@%s", pkg.VERSION)},
|
||||
"User-Agent": []string{pkg.UserAgent()},
|
||||
}
|
||||
|
||||
value, set := os.LookupEnv("AUTHENTIK_INSECURE")
|
||||
|
@ -47,6 +47,7 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) {
|
|||
Instruction: WebsocketInstructionHello,
|
||||
Args: map[string]interface{}{
|
||||
"version": pkg.VERSION,
|
||||
"buildHash": pkg.BUILD(),
|
||||
"uuid": ac.instanceUUID.String(),
|
||||
},
|
||||
}
|
||||
|
@ -76,7 +77,7 @@ func (ac *APIController) startWSHandler() {
|
|||
var wsMsg websocketMessage
|
||||
err := ac.wsConn.ReadJSON(&wsMsg)
|
||||
if err != nil {
|
||||
logger.Println("read:", err)
|
||||
logger.WithError(err).Warning("ws write error, reconnecting")
|
||||
ac.wsConn.CloseAndReconnect()
|
||||
continue
|
||||
}
|
||||
|
@ -101,13 +102,14 @@ func (ac *APIController) startWSHealth() {
|
|||
Instruction: WebsocketInstructionHello,
|
||||
Args: map[string]interface{}{
|
||||
"version": pkg.VERSION,
|
||||
"buildHash": pkg.BUILD(),
|
||||
"uuid": ac.instanceUUID.String(),
|
||||
},
|
||||
}
|
||||
err := ac.wsConn.WriteJSON(aliveMsg)
|
||||
ac.logger.WithField("loop", "ws-health").Trace("hello'd")
|
||||
if err != nil {
|
||||
ac.logger.WithField("loop", "ws-health").Println("write:", err)
|
||||
ac.logger.WithField("loop", "ws-health").WithError(err).Warning("ws write error, reconnecting")
|
||||
ac.wsConn.CloseAndReconnect()
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ func doGlobalSetup(config map[string]interface{}) {
|
|||
default:
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
log.WithField("version", pkg.VERSION).Info("Starting authentik outpost")
|
||||
log.WithField("buildHash", pkg.BUILD()).WithField("version", pkg.VERSION).Info("Starting authentik outpost")
|
||||
|
||||
var dsn string
|
||||
if config[ConfigErrorReportingEnabled].(bool) {
|
||||
|
|
|
@ -2,20 +2,22 @@ package ldap
|
|||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/nmcclain/ldap"
|
||||
)
|
||||
|
||||
func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) {
|
||||
ls.log.WithField("boundDN", bindDN).Info("bind")
|
||||
ls.log.WithField("bindDN", bindDN).Info("bind")
|
||||
bindDN = strings.ToLower(bindDN)
|
||||
for _, instance := range ls.providers {
|
||||
username, err := instance.getUsername(bindDN)
|
||||
if err == nil {
|
||||
return instance.Bind(username, bindPW, conn)
|
||||
return instance.Bind(username, bindDN, bindPW, conn)
|
||||
} else {
|
||||
ls.log.WithError(err).Debug("Username not for instance")
|
||||
}
|
||||
}
|
||||
ls.log.WithField("boundDN", bindDN).WithField("request", "bind").Warning("No provider found for request")
|
||||
ls.log.WithField("bindDN", bindDN).WithField("request", "bind").Warning("No provider found for request")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ func (pi *ProviderInstance) getUsername(dn string) (string, error) {
|
|||
return "", errors.New("failed to find cn")
|
||||
}
|
||||
|
||||
func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) {
|
||||
func (pi *ProviderInstance) Bind(username string, bindDN, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) {
|
||||
jar, err := cookiejar.New(nil)
|
||||
if err != nil {
|
||||
pi.log.WithError(err).Warning("Failed to create cookiejar")
|
||||
|
@ -67,9 +67,9 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn)
|
|||
}
|
||||
params := url.Values{}
|
||||
params.Add("goauthentik.io/outpost/ldap", "true")
|
||||
passed, err := pi.solveFlowChallenge(username, bindPW, client, params.Encode())
|
||||
passed, err := pi.solveFlowChallenge(username, bindPW, client, params.Encode(), 1)
|
||||
if err != nil {
|
||||
pi.log.WithField("boundDN", username).WithError(err).Warning("failed to solve challenge")
|
||||
pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to solve challenge")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
if !passed {
|
||||
|
@ -82,25 +82,25 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn)
|
|||
}, httptransport.PassThroughAuth)
|
||||
if err != nil {
|
||||
if _, denied := err.(*core.CoreApplicationsCheckAccessForbidden); denied {
|
||||
pi.log.WithField("boundDN", username).Info("Access denied for user")
|
||||
pi.log.WithField("bindDN", bindDN).Info("Access denied for user")
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
pi.log.WithField("boundDN", username).WithError(err).Warning("failed to check access")
|
||||
pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to check access")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
pi.log.WithField("boundDN", username).Info("User has access")
|
||||
pi.log.WithField("bindDN", bindDN).Info("User has access")
|
||||
// Get user info to store in context
|
||||
userInfo, err := pi.s.ac.Client.Core.CoreUsersMe(&core.CoreUsersMeParams{
|
||||
Context: context.Background(),
|
||||
HTTPClient: client,
|
||||
}, httptransport.PassThroughAuth)
|
||||
if err != nil {
|
||||
pi.log.WithField("boundDN", username).WithError(err).Warning("failed to get user info")
|
||||
pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to get user info")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
pi.boundUsersMutex.Lock()
|
||||
pi.boundUsers[username] = UserFlags{
|
||||
UserInfo: userInfo.Payload.User,
|
||||
pi.boundUsers[bindDN] = UserFlags{
|
||||
UserInfo: *userInfo.Payload.User,
|
||||
CanSearch: pi.SearchAccessCheck(userInfo.Payload.User),
|
||||
}
|
||||
defer pi.boundUsersMutex.Unlock()
|
||||
|
@ -112,7 +112,8 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn)
|
|||
func (pi *ProviderInstance) SearchAccessCheck(user *models.User) bool {
|
||||
for _, group := range user.Groups {
|
||||
for _, allowedGroup := range pi.searchAllowedGroups {
|
||||
if &group.Pk == allowedGroup {
|
||||
pi.log.WithField("userGroup", group.Pk).WithField("allowedGroup", allowedGroup).Trace("Checking search access")
|
||||
if group.Pk.String() == allowedGroup.String() {
|
||||
pi.log.WithField("group", group.Name).Info("Allowed access to search")
|
||||
return true
|
||||
}
|
||||
|
@ -139,7 +140,7 @@ func (pi *ProviderInstance) delayDeleteUserInfo(dn string) {
|
|||
}()
|
||||
}
|
||||
|
||||
func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, client *http.Client, urlParams string) (bool, error) {
|
||||
func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, client *http.Client, urlParams string, depth int) (bool, error) {
|
||||
challenge, err := pi.s.ac.Client.Flows.FlowsExecutorGet(&flows.FlowsExecutorGetParams{
|
||||
FlowSlug: pi.flowSlug,
|
||||
Query: urlParams,
|
||||
|
@ -169,6 +170,10 @@ func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, c
|
|||
}
|
||||
response, err := pi.s.ac.Client.Flows.FlowsExecutorSolve(responseParams, pi.s.ac.Auth)
|
||||
pi.log.WithField("component", response.Payload.Component).WithField("type", *response.Payload.Type).Debug("Got response")
|
||||
switch response.Payload.Component {
|
||||
case "ak-stage-access-denied":
|
||||
return false, errors.New("got ak-stage-access-denied")
|
||||
}
|
||||
if *response.Payload.Type == "redirect" {
|
||||
return true, nil
|
||||
}
|
||||
|
@ -184,5 +189,8 @@ func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, c
|
|||
}
|
||||
}
|
||||
}
|
||||
return pi.solveFlowChallenge(bindDN, password, client, urlParams)
|
||||
if depth >= 10 {
|
||||
return false, errors.New("exceeded stage recursion depth")
|
||||
}
|
||||
return pi.solveFlowChallenge(bindDN, password, client, urlParams, depth+1)
|
||||
}
|
||||
|
|
|
@ -29,10 +29,13 @@ func (pi *ProviderInstance) Search(bindDN string, searchReq ldap.SearchRequest,
|
|||
pi.boundUsersMutex.RLock()
|
||||
defer pi.boundUsersMutex.RUnlock()
|
||||
flags, ok := pi.boundUsers[bindDN]
|
||||
pi.log.WithField("bindDN", bindDN).WithField("ok", ok).Debugf("%+v\n", flags)
|
||||
if !ok {
|
||||
pi.log.Debug("User info not cached")
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
|
||||
}
|
||||
if !flags.CanSearch {
|
||||
pi.log.Debug("User can't search")
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ type ProviderInstance struct {
|
|||
}
|
||||
|
||||
type UserFlags struct {
|
||||
UserInfo *models.User
|
||||
UserInfo models.User
|
||||
CanSearch bool
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
"github.com/nmcclain/ldap"
|
||||
)
|
||||
|
||||
func (ls *LDAPServer) Search(boundDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) {
|
||||
ls.log.WithField("boundDN", boundDN).WithField("baseDN", searchReq.BaseDN).Info("search")
|
||||
func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) {
|
||||
ls.log.WithField("bindDN", bindDN).WithField("baseDN", searchReq.BaseDN).Info("search")
|
||||
if searchReq.BaseDN == "" {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultSuccess}, nil
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ func (ls *LDAPServer) Search(boundDN string, searchReq ldap.SearchRequest, conn
|
|||
for _, provider := range ls.providers {
|
||||
providerBase, _ := goldap.ParseDN(provider.BaseDN)
|
||||
if providerBase.AncestorOf(bd) {
|
||||
return provider.Search(boundDN, searchReq, conn)
|
||||
return provider.Search(bindDN, searchReq, conn)
|
||||
}
|
||||
}
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, errors.New("no provider could handle request")
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
package pkg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
const VERSION = "2021.5.1-rc8"
|
||||
|
||||
func BUILD() string {
|
||||
return os.Getenv("GIT_BUILD_HASH")
|
||||
}
|
||||
|
||||
func UserAgent() string {
|
||||
return fmt.Sprintf("authentik-outpost@%s (%s)", VERSION, BUILD())
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
FROM golang:1.16.4 AS builder
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
|
|
89
swagger.yaml
89
swagger.yaml
|
@ -15694,6 +15694,7 @@ definitions:
|
|||
NotificationRule:
|
||||
required:
|
||||
- name
|
||||
- transports
|
||||
type: object
|
||||
properties:
|
||||
pk:
|
||||
|
@ -15706,38 +15707,17 @@ definitions:
|
|||
type: string
|
||||
minLength: 1
|
||||
transports:
|
||||
description: Select which transports should be used to notify the user. If
|
||||
none are selected, the notification will only be shown in the authentik
|
||||
UI.
|
||||
type: array
|
||||
items:
|
||||
required:
|
||||
- name
|
||||
- mode
|
||||
type: object
|
||||
properties:
|
||||
uuid:
|
||||
title: Uuid
|
||||
description: Select which transports should be used to notify the user.
|
||||
If none are selected, the notification will only be shown in the authentik
|
||||
UI.
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
name:
|
||||
title: Name
|
||||
type: string
|
||||
minLength: 1
|
||||
mode:
|
||||
title: Mode
|
||||
type: string
|
||||
enum:
|
||||
- webhook
|
||||
- webhook_slack
|
||||
- email
|
||||
webhook_url:
|
||||
title: Webhook url
|
||||
type: string
|
||||
send_once:
|
||||
title: Send once
|
||||
description: Only send notification once, for example when sending a
|
||||
webhook into a chat channel.
|
||||
type: boolean
|
||||
readOnly: true
|
||||
uniqueItems: true
|
||||
severity:
|
||||
title: Severity
|
||||
description: Controls which severity level the created notifications will
|
||||
|
@ -15748,57 +15728,14 @@ definitions:
|
|||
- warning
|
||||
- alert
|
||||
group:
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
properties:
|
||||
group_uuid:
|
||||
title: Group uuid
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
name:
|
||||
title: Name
|
||||
type: string
|
||||
maxLength: 80
|
||||
minLength: 1
|
||||
is_superuser:
|
||||
title: Is superuser
|
||||
description: Users added to this group will be superusers.
|
||||
type: boolean
|
||||
attributes:
|
||||
title: Attributes
|
||||
type: object
|
||||
parent:
|
||||
required:
|
||||
- name
|
||||
- parent
|
||||
type: object
|
||||
properties:
|
||||
group_uuid:
|
||||
title: Group uuid
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
name:
|
||||
title: Name
|
||||
type: string
|
||||
maxLength: 80
|
||||
minLength: 1
|
||||
is_superuser:
|
||||
title: Is superuser
|
||||
description: Users added to this group will be superusers.
|
||||
type: boolean
|
||||
attributes:
|
||||
title: Attributes
|
||||
type: object
|
||||
parent:
|
||||
title: Parent
|
||||
title: Group
|
||||
description: Define which group of users this notification should be sent
|
||||
and shown to. If left empty, Notification won't ben sent.
|
||||
type: string
|
||||
format: uuid
|
||||
x-nullable: true
|
||||
readOnly: true
|
||||
readOnly: true
|
||||
group_obj:
|
||||
$ref: '#/definitions/Group'
|
||||
NotificationTransport:
|
||||
required:
|
||||
- name
|
||||
|
|
|
@ -67,7 +67,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
|
|||
<option value="" ?selected=${this.instance?.group === undefined}>---------</option>
|
||||
${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then(groups => {
|
||||
return groups.results.map(group => {
|
||||
return html`<option value=${ifDefined(group.pk)} ?selected=${this.instance?.group?.groupUuid === group.pk}>${group.name}</option>`;
|
||||
return html`<option value=${ifDefined(group.pk)} ?selected=${this.instance?.group === group.pk}>${group.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
</select>
|
||||
|
@ -80,7 +80,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
|
|||
${until(new EventsApi(DEFAULT_CONFIG).eventsTransportsList({}).then(transports => {
|
||||
return transports.results.map(transport => {
|
||||
const selected = Array.from(this.instance?.transports || []).some(su => {
|
||||
return su.uuid == transport.pk;
|
||||
return su == transport.pk;
|
||||
});
|
||||
return html`<option value=${ifDefined(transport.pk)} ?selected=${selected}>${transport.name}</option>`;
|
||||
});
|
||||
|
|
|
@ -55,7 +55,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
|
|||
return [
|
||||
html`${item.name}`,
|
||||
html`${item.severity}`,
|
||||
html`${item.group?.name || t`None (rule disabled)`}`,
|
||||
html`${item.groupObj?.name || t`None (rule disabled)`}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
|
|
|
@ -42,13 +42,12 @@ export class OutpostHealthElement extends LitElement {
|
|||
return html`<ak-spinner></ak-spinner>`;
|
||||
}
|
||||
if (this.outpostHealth.length === 0) {
|
||||
return html`<li>
|
||||
return html`
|
||||
<ul>
|
||||
<li role="cell">
|
||||
<ak-label color=${PFColor.Grey} text=${t`Not available`}></ak-label>
|
||||
</li>
|
||||
</ul>
|
||||
</li>`;
|
||||
</ul>`;
|
||||
}
|
||||
return html`<ul>${this.outpostHealth.map((h) => {
|
||||
return html`<li>
|
||||
|
|
|
@ -70,7 +70,7 @@ export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValid
|
|||
<ak-form-element-horizontal
|
||||
label=${t`Not configured action`}
|
||||
?required=${true}
|
||||
name="mode">
|
||||
name="notConfiguredAction">
|
||||
<select class="pf-c-form-control" @change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLSelectElement;
|
||||
if (target.selectedOptions[0].value === AuthenticatorValidateStageNotConfiguredActionEnum.Configure) {
|
||||
|
|
Reference in New Issue