惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

罗磊的独立博客
Martin Fowler
Martin Fowler
J
Java Code Geeks
The GitHub Blog
The GitHub Blog
C
Check Point Blog
H
Help Net Security
Google DeepMind News
Google DeepMind News
人人都是产品经理
人人都是产品经理
博客园 - 聂微东
P
Proofpoint News Feed
V
Visual Studio Blog
Stack Overflow Blog
Stack Overflow Blog
雷峰网
雷峰网
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Vercel News
Vercel News
S
SegmentFault 最新的问题
L
LangChain Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The Cloudflare Blog
Hugging Face - Blog
Hugging Face - Blog
有赞技术团队
有赞技术团队
博客园_首页
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Dev Log: 2026-06-24 — agent guardrails and runtime LDAP c...
Nasrul Hazim Bin Mohamad · 2026-06-25 · via DEV Community

Two threads today, and they rhyme more than I expected: both are about who gets to do what, and how you keep that boundary honest. One was exposing an app to an AI agent safely; the other was moving config out of deploy-time files and into a settings UI without losing the guardrails. Different problems, same instinct — make the boundary explicit and put it in one place.

Thread 1 — MCP tools that respect tenancy and permissions

I spent most of the day building out a set of MCP tools so an AI agent can drive an events platform: list events, check readiness, publish, run the lifecycle. The interesting part isn't the tools — it's the fencing around them.

MCP tools authenticate with a token, which means there's no ambient "current tenant" context the way a session-backed web request has. If you lean on a global tenant scope, it reads nothing and your "fenced" query quietly returns everyone's rows. So the rule became: under token auth, filter by organization explicitly, every time, in one shared trait — never a global scope you hope is active.

On top of scope, every tool declares a single ability() — and it reuses the same permission strings the web app already enforces, so the agent can never be more privileged than the human behind it. Read tools get an #[IsReadOnly] annotation so destructive calls are visible at a glance. Lookups are by UUID, never the enumerable auto-increment ID.

I wrote this thread up properly as its own post (RBAC + org-scoped MCP tools) because the pattern generalizes well beyond this app. Short version of the lesson: treat every MCP tool as an untrusted endpoint, and put the boundary logic somewhere you can test it in one shot.

it('never resolves a record from another organization', function () {
    $mine = Event::factory()->for($orgA)->create();
    $theirs = Event::factory()->for($orgB)->create();
    $user = User::factory()->for($orgA)->create();

    expect(resolveOrgEvent($user, $mine->uuid))->not->toBeNull()
        ->and(resolveOrgEvent($user, $theirs->uuid))->toBeNull();
});

Thread 2 — making LDAP config editable at runtime, safely

The other change was on an identity/AD portal: making the LDAP connection settings fully editable from an admin UI, instead of being frozen in config/ldap.php at deploy time. Previously only the obvious fields were exposed — host, port, base DN, the bind credentials, SSL/TLS, search attribute. Now the awkward ones are too: connection timeout, SASL toggle and options, and the raw ldap_set_option map.

That last one is a nice little gotcha worth sharing. The options map uses integer keys that are LDAP option constants (e.g. 17 is LDAP_OPT_PROTOCOL_VERSION). But the moment you store that map as JSON and decode it back, those keys come back as strings — and the underlying ldap_set_option calls won't match on string keys. So the settings accessor has to restore the integer keys after decoding before the values are usable:

// JSON round-trips integer keys to strings; restore them so the
// values pass straight to ldap_set_option / LdapRecord.
$options = collect(json_decode($raw, true))
    ->mapWithKeys(fn ($value, $key) => [
        is_numeric($key) ? (int) $key : $key => $value,
    ])
    ->all();

The pattern underneath is a cache-backed settings layer that overrides deploy-time config() at boot: a service provider reads the stored values and hydrates config() (and the LDAP container) before anything uses them. Two guardrails make it safe to hand to an admin:

  • The bind password is held encrypted at rest and is never placed in the cache as plaintext. Secrets don't get to be convenient.
  • The JSON fields are validated to actually decode to an object before they're persisted — a malformed paste fails at save time, not at connection time when someone's locked out.

And a small design win: the same connectionConfig() assembler backs both Save and Test connection, so the connection you test is byte-for-byte the connection you save. No "works in test, fails live" gap because two code paths built the config slightly differently.

The thread between the threads

Both changes are really the same move: take something that was implicit or frozen — ambient tenancy, deploy-time config — make it explicit and runtime-controllable, then wrap it in guardrails you can point at. Explicit org filter in one trait. Encrypted secret that never hits the cache. Validation at the boundary. One assembler for save-and-test. The feature is the easy part; the guardrail is the engineering.

What's next: more lifecycle tools on the agent side, and probably a "test connection" surfaced directly in the settings UI for every LDAP field, not just the core ones.