80 lines
3.6 KiB
Python
80 lines
3.6 KiB
Python
"""Public source preparation only. No I/O, native tools, credentials or dispatch.
|
|
|
|
The actual three-group case is refused on Forgejo 11.0.3. A single-group test
|
|
control proves the real chart secret-reference seam, never case admission.
|
|
"""
|
|
from copy import deepcopy
|
|
import hashlib
|
|
import re
|
|
import yaml
|
|
|
|
APPS = '944b30ad63ab054cc24617e374e177d34cd572da'
|
|
BASE_SHA256 = 'cf87a3dccd666bf8870619755afac4af64fc0bee77b0f0ea54718d6384b70d4f'
|
|
CASE_GROUPS = ('forge-admins', 'forge-coulomb-owners', 'forge-coulomb-readers')
|
|
|
|
class Refused(ValueError):
|
|
pass
|
|
|
|
|
|
def policy_only(base):
|
|
"""Incomplete preparatory policy delta; does not add an OAuth source."""
|
|
result = deepcopy(base)
|
|
config = result['gitea']['config']
|
|
if config.get('service', {}).get('DISABLE_REGISTRATION') is not True:
|
|
raise Refused('existing registration policy differs')
|
|
# No supported local-form switch in 11.0.3: retain existing settings.
|
|
oauth = config.setdefault('oauth2_client', {})
|
|
oauth.update(ENABLE_AUTO_REGISTRATION=False, ACCOUNT_LINKING='login')
|
|
return result
|
|
|
|
|
|
def chart_projection(base, groups):
|
|
"""Chart-compatible PUBLIC values for one literal gate, or fail closed.
|
|
|
|
groups must be the authoritative reviewed case set, never a selected subset.
|
|
This function authenticates neither case/source acceptance nor permission.
|
|
It is not suitable for rendering arbitrary live/private Helm values.
|
|
"""
|
|
if not isinstance(groups, tuple) or not groups or len(set(groups)) != len(groups):
|
|
raise Refused('explicit unique group tuple required')
|
|
if any(not isinstance(g, str) or not re.fullmatch(r'[a-z][a-z0-9-]{1,62}', g) for g in groups):
|
|
raise Refused('group literal invalid')
|
|
if len(groups) != 1:
|
|
raise Refused('Forgejo 11.0.3 supports one literal required claim; any-of unavailable')
|
|
result = policy_only(base)
|
|
sources = result['gitea'].setdefault('oauth', [])
|
|
if not isinstance(sources, list) or any(not isinstance(s, dict) for s in sources):
|
|
raise Refused('OAuth source list required')
|
|
names = [s.get('name') for s in sources]
|
|
if len(names) != len(set(names)) or 'netkingdom' in names:
|
|
raise Refused('existing target requires exact owner reconciliation')
|
|
idx = len(sources)
|
|
env = result.setdefault('deployment', {}).setdefault('env', [])
|
|
if not isinstance(env, list):
|
|
raise Refused('deployment env list required')
|
|
var = f'GITEA_OAUTH_SECRET_{idx}'
|
|
if any(e.get('name') == var for e in env):
|
|
raise Refused('indexed secret environment collision')
|
|
sources.append({
|
|
'name': 'netkingdom', 'provider': 'openidConnect', 'key': 'forgejo',
|
|
'autoDiscoverUrl': 'https://kc.coulomb.social/.well-known/openid-configuration',
|
|
'scopes': 'openid,profile,email,groups', 'groupClaimName': 'groups',
|
|
'requiredClaimName': 'groups', 'requiredClaimValue': groups[0],
|
|
})
|
|
env.append({'name': var, 'valueFrom': {'secretKeyRef': {
|
|
'name': 'forgejo-netkingdom-oidc-client', 'key': 'CLIENT_SECRET'}}})
|
|
return result
|
|
|
|
|
|
def prepare_first_case(base_bytes, base):
|
|
"""Pin the public baseline and keep exact case any-of semantics."""
|
|
if hashlib.sha256(base_bytes).hexdigest() != BASE_SHA256:
|
|
raise Refused('frozen Apps public values bytes differ')
|
|
if base != yaml.safe_load(base_bytes):
|
|
raise Refused('decoded public baseline differs from frozen bytes')
|
|
# This entry never returns while the actual case has an unsupported gate.
|
|
return chart_projection(base, CASE_GROUPS)
|
|
|
|
|
|
def activate(*args, **kwargs):
|
|
raise Refused('source preparation has no operational backend or admission')
|