Ansprüche Transformation Regeln Sprache

Gilt für: Windows Server 2022, Windows Server 2019, Windows Server 2016, Windows Server 2012 R2, Windows Server 2012

Mit dem Feature für die Gesamtstruktur-Forderungstransformation können Sie Ansprüche für dynamische Zugriffssteuerung über Gesamtstrukturgrenzen hinweg überbrücken, indem Sie Anspruchstransformationsrichtlinien für gesamtstrukturübergreifende Vertrauensstellungen festlegen. Die primäre Komponente aller Richtlinien ist Regeln, die in der Sprache der Anspruchstransformationsregeln geschrieben werden. Dieses Thema enthält Details zu dieser Sprache und enthält Anleitungen zum Erstellen von Anspruchstransformationsregeln.

Die Windows PowerShell-Cmdlets für Transformationsrichtlinien für gesamtstrukturübergreifende Vertrauensstellungen verfügen über Optionen zum Festlegen einfacher Richtlinien, die in allgemeinen Szenarien erforderlich sind. Diese Cmdlets übersetzen die Benutzereingabe in Richtlinien und Regeln in der Sprache der Anspruchstransformationsregeln und speichern sie dann im vorgeschriebenen Format in Active Directory. Weitere Informationen zu Cmdlets für die Anspruchstransformation finden Sie in den AD DS-Cmdlets für dynamische Zugriffssteuerung.

Abhängig von der Anspruchskonfiguration und den Anforderungen, die auf der Gesamtstruktur vertrauen, in Ihren Active Directory-Gesamtstrukturen müssen Ihre Anspruchstransformationsrichtlinien möglicherweise komplexer sein als die richtlinien, die von den Windows PowerShell Cmdlets für Active Directory unterstützt werden. Um solche Richtlinien effektiv zu erstellen, ist es wichtig, die Syntax und Semantik der Anspruchstransformationsregeln zu verstehen. Diese Anspruchstransformationsregelnsprache ("die Sprache") in Active Directory ist eine Teilmenge der Sprache, die von Active Directory-Verbunddienste (AD FS) für ähnliche Zwecke verwendet wird und eine sehr ähnliche Syntax und Semantik aufweist. Es sind jedoch weniger Vorgänge zulässig, und zusätzliche Syntaxeinschränkungen werden in der Active Directory-Version der Sprache platziert.

In diesem Thema werden kurz die Syntax und Semantik der Sprache für die Anspruchstransformationsregeln in Active Directory erläutert und überlegungen erläutert, die beim Erstellen von Richtlinien getroffen werden sollen. Es stellt mehrere Beispielregeln bereit, die Sie starten können, und Beispiele für falsche Syntax und die von ihnen generierten Nachrichten, um Sie beim Erstellen der Regeln bei der Entschlüsselung von Fehlermeldungen zu unterstützen.

Tools zum Erstellen von Anspruchstransformationsrichtlinien

Windows PowerShell Cmdlets für Active Directory: Dies ist die bevorzugte und empfohlene Methode zum Erstellen und Festlegen von Anspruchstransformationsrichtlinien. Diese Cmdlets bieten Schalter für einfache Richtlinien und überprüfen Regeln, die für komplexere Richtlinien festgelegt sind.

LDAP: Anspruchstransformationsrichtlinien können in Active Directory über das Lightweight Directory Access Protocol (LDAP) bearbeitet werden. Dies wird jedoch nicht empfohlen, da die Richtlinien mehrere komplexe Komponenten haben und die tools, die Sie verwenden, die Richtlinie möglicherweise nicht überprüfen, bevor Sie sie in Active Directory schreiben. Dies kann später eine erhebliche Zeit erfordern, um Probleme zu diagnostizieren.

Sprache der Active Directory-Anspruchstransformationsregeln

Syntaxübersicht

Hier ist eine kurze Übersicht über die Syntax und Semantik der Sprache:

  • Der Anspruchstransformationsregelsatz besteht aus null oder mehr Regeln. Jede Regel verfügt über zwei aktive Teile: Bedingungsliste und Regelaktion auswählen. Wenn die Auswahlbedingungsliste auf TRUE ausgewertet wird, wird die entsprechende Regelaktion ausgeführt.

  • Die Bedingungsliste hat null oder mehr Auswahlbedingungen. Alle Auswahlbedingungen müssen für die Auswahlbedingungsliste als TRUE ausgewertet werden.

  • Jede Auswahlbedingung weist einen Satz von null oder mehr übereinstimmenden Bedingungen auf. Alle Übereinstimmenden Bedingungen müssen für die Auswahlbedingung als TRUE ausgewertet werden. Alle diese Bedingungen werden gegen einen einzelnen Anspruch ausgewertet. Ein Anspruch, der einer Auswahlbedingung entspricht, kann durch einen Bezeichner gekennzeichnet und in der Regelaktion bezeichnet werden.

  • Jede übereinstimmende Bedingung gibt die Bedingung an, die dem Typ oder ValueType eines Anspruchs entspricht, indem verschiedene Bedingungsoperatoren und Zeichenfolgenliterale verwendet werden.

    • Wenn Sie eine Übereinstimmende Bedingung für einen Wert angeben, müssen Sie auch eine übereinstimmende Bedingung für einen bestimmten ValueType angeben und umgekehrt. Diese Bedingungen müssen sich in der Syntax nebeneinander unterscheiden.

    • ValueType-Übereinstimmungsbedingungen müssen nur bestimmte ValueType-Literale verwenden.

  • Eine Regelaktion kann einen Anspruch kopieren, der mit einem Bezeichner markiert ist oder einen Anspruch basierend auf einem Anspruch ausgibt, der mit einem Bezeichner und/oder bestimmten Zeichenfolgenliteralen markiert ist.

Beispielregel

In diesem Beispiel wird eine Regel gezeigt, die verwendet werden kann, um den Anspruchstyp zwischen zwei Gesamtstrukturen zu übersetzen, sofern sie dieselben Anspruchswerttypen verwenden und dieselben Interpretationen für Anspruchswerte für diesen Typ haben. Die Regel verfügt über eine übereinstimmende Bedingung und eine Issue-Anweisung, die String Literals und einen übereinstimmenden Anspruchsverweis verwendet.

C1: [TYPE=="EmployeeType"]
                 => ISSUE (TYPE= "EmpType", VALUE = C1.VALUE, VALUETYPE = C1.VALUETYPE);
[TYPE=="EmployeeType"] == Select Condition List with one Matching Condition for claims Type.
ISSUE (TYPE= "EmpType", VALUE = C1.VALUE, VALUETYPE = C1.VALUETYPE) == Rule Action that issues a claims using string literal and matching claim referred with the Identifier.

Laufzeitvorgang

Es ist wichtig, den Laufzeitvorgang von Anspruchstransformationen zu verstehen, um die Regeln effektiv zu erstellen. Der Laufzeitvorgang verwendet drei Anspruchsgruppen:

  1. Eingabeanspruchssatz: Der Eingabesatz von Ansprüchen, die dem Anspruchstransformationsvorgang zugewiesen werden.

  2. Arbeitsanspruchssatz: Zwischenansprüche, die während der Anspruchstransformation gelesen und geschrieben werden.

  3. Ausgabeanspruchssatz: Ausgabe des Anspruchstransformationsvorgangs.

Hier ist eine kurze Übersicht über den Laufzeitanspruchstransformationsvorgang:

  1. Eingabeansprüche für die Anspruchstransformation werden verwendet, um den Arbeitsanspruchssatz zu initialisieren.

    1. Bei der Verarbeitung jeder Regel wird der Arbeitsanspruchssatz für die Eingabeansprüche verwendet.

    2. Die Auswahlbedingungsliste in einer Regel wird mit allen möglichen Ansprüchen aus dem Arbeitsanspruchssatz abgeglichen.

    3. Jeder Satz übereinstimmende Ansprüche wird verwendet, um die Aktion in dieser Regel auszuführen.

    4. Das Ausführen einer Regelaktion führt zu einem Anspruch, der an den Ausgabeanspruchssatz und den Arbeitsanspruchssatz angefügt wird. Daher wird die Ausgabe einer Regel als Eingabe für nachfolgende Regeln im Regelsatz verwendet.

  2. Die Regeln im Regelsatz werden ab der ersten Regel in sequenzieller Reihenfolge verarbeitet.

  3. Wenn der gesamte Regelsatz verarbeitet wird, wird der Ausgabeanspruchssatz verarbeitet, um doppelte Ansprüche und für andere Sicherheitsprobleme zu entfernen. Die resultierenden Ansprüche sind die Ausgabe des Anspruchstransformationsprozesses.

Es ist möglich, komplexe Anspruchstransformationen basierend auf dem vorherigen Laufzeitverhalten zu schreiben.

Beispiel: Laufzeitvorgang

In diesem Beispiel wird der Laufzeitvorgang einer Anspruchstransformation veranschaulicht, die zwei Regeln verwendet.


     C1:[Type=="EmpType", Value=="FullTime",ValueType=="string"] =>
                Issue(Type=="EmployeeType", Value=="FullTime",ValueType=="string");
     [Type=="EmployeeType"] =>
               Issue(Type=="AccessType", Value=="Privileged", ValueType=="string");
Input claims and Initial Evaluation Context:
  {(Type= "EmpType"),(Value="FullTime"),(ValueType="String")}
{(Type= "Organization"),(Value="Marketing"),(ValueType="String")}
After Processing Rule 1:
 Evaluation Context:
  {(Type= "EmpType"),(Value="FullTime"),(ValueType="String")}
{(Type= "Organization"), (Value="Marketing"),(ValueType="String")}
  {(Type= "EmployeeType"),(Value="FullTime"),(ValueType="String")}
Output Context:
  {(Type= "EmployeeType"),(Value="FullTime"),(ValueType="String")}

After Processing Rule 2:
Evaluation Context:
  {(Type= "EmpType"),(Value="FullTime"),(ValueType="String")}
{(Type= "Organization"),(Value="Marketing"),(ValueType="String")}
  {(Type= "EmployeeType"),(Value="FullTime"),(ValueType="String")}
  {(Type= "AccessType"),(Value="Privileged"),(ValueType="String")}
Output Context:
  {(Type= "EmployeeType"),(Value="FullTime"),(ValueType="String")}
  {(Type= "AccessType"),(Value="Privileged"),(ValueType="String")}

Final Output:
  {(Type= "EmployeeType"),(Value="FullTime"),(ValueType="String")}
  {(Type= "AccessType"),(Value="Privileged"),(ValueType="String")}

Sonderregelnsemantik

Nachfolgend sind spezielle Syntax für Regeln aufgeführt:

  1. Leere Regelsatz == Keine Ausgabeansprüche

  2. Leere Bedingungsliste == Jeder Anspruch entspricht der Auswahlbedingungsliste

    Beispiel: Leere Bedingungsliste

    Die folgende Regel entspricht jedem Anspruch im Arbeitssatz.

    => Issue (Type = "UserType", Value = "External", ValueType = "string")
    
  3. Leere Auswahlabgleichsliste == Jeder Anspruch entspricht der Auswahlbedingungsliste

    Beispiel: Leere Übereinstimmungsbedingungen

    Die folgende Regel entspricht jedem Anspruch im Arbeitssatz. Dies ist die grundlegende Regel "Alle zulassen", wenn sie allein verwendet wird.

    C1:[] => Issule (claim = C1);
    

Sicherheitshinweise

Ansprüche, die eine Gesamtstruktur eingeben

Die Ansprüche, die von Prinzipalen vorgelegt werden, die in eine Gesamtstruktur eingehen, müssen gründlich überprüft werden, um sicherzustellen, dass wir nur die richtigen Ansprüche zulassen oder ausstellen. Falsche Ansprüche können die Gesamtstruktursicherheit gefährden, und dies sollte bei der Erstellung von Transformationsrichtlinien für Ansprüche, die in eine Gesamtstruktur eingegeben werden, eine oberste Berücksichtigung finden.

Active Directory verfügt über die folgenden Features, um die Falschkonfiguration von Ansprüchen zu verhindern, die eine Gesamtstruktur eingeben:

  • Wenn eine Gesamtstrukturvertrauensstellung keine Anspruchstransformationsrichtlinie für die Ansprüche enthält, die eine Gesamtstruktur eingeben, für Sicherheitszwecke legt Active Directory alle Hauptansprüche ab, die in die Gesamtstruktur eingegeben werden.

  • Wenn das Ausführen der Regel für Ansprüche, die eine Gesamtstruktur eingeben, zu Ansprüchen führt, die nicht in der Gesamtstruktur definiert sind, werden die nicht definierten Ansprüche aus den Ausgabeansprüchen gelöscht.

Ansprüche, die eine Gesamtstruktur verlassen

Ansprüche, die eine Gesamtstruktur verlassen, stellen eine geringere Sicherheitssorge für die Gesamtstruktur dar als die Ansprüche, die in die Gesamtstruktur gelangen. Ansprüche dürfen die Gesamtstruktur selbst dann verlassen, wenn keine entsprechende Anspruchstransformationsrichtlinie vorhanden ist. Es ist auch möglich, Ansprüche ausstellen, die nicht in der Gesamtstruktur definiert sind, als Teil der Transformation von Ansprüchen, die die Gesamtstruktur verlassen. Dies ist das einfache Einrichten von Gesamtstrukturvertrauensstellungen mit Ansprüchen. Ein Administrator kann ermitteln, ob Ansprüche, die die Gesamtstruktur eingeben, transformiert werden müssen, und die entsprechende Richtlinie einrichten. Beispielsweise könnte ein Administrator eine Richtlinie festlegen, wenn ein Anspruch ausgeblendet werden muss, um die Offenlegung von Informationen zu verhindern.

Syntaxfehler in Anspruchstransformationsregeln

Wenn eine bestimmte Anspruchstransformationsrichtlinie einen Regelsatz aufweist, der syntaktisch falsch ist oder andere Syntax- oder Speicherprobleme auftreten, wird die Richtlinie als ungültig betrachtet. Dies wird anders behandelt als die zuvor erwähnten Standardbedingungen.

Active Directory kann die Absicht in diesem Fall nicht ermitteln und in einen fehlersicheren Modus wechselt, in dem keine Ausgabeansprüche für diese vertrauenswürdige+Richtung der Traversal generiert werden. Die Administratorinteraktion ist erforderlich, um das Problem zu beheben. Dies kann passieren, wenn LDAP zum Bearbeiten der Anspruchstransformationsrichtlinie verwendet wird. Windows PowerShell Cmdlets für Active Directory haben Überprüfung, um das Schreiben einer Richtlinie mit Syntaxproblemen zu verhindern.

Weitere Sprachüberlegungen

  1. Es gibt mehrere Schlüsselwörter oder Zeichen, die in dieser Sprache speziell sind (als Terminals bezeichnet). Diese werden in der Tabelle " Sprachterminals " weiter unten in diesem Thema vorgestellt. Die Fehlermeldungen verwenden die Tags für diese Terminals zur Undeutigkeit.

  2. Terminals können manchmal als Zeichenfolgenliterale verwendet werden. Diese Verwendung kann jedoch mit der Sprachdefinition in Konflikt stehen oder unbeabsichtigte Folgen haben. Diese Art der Verwendung wird nicht empfohlen.

  3. Die Regelaktion kann keine Typkonvertierungen für Anspruchswerte ausführen, und ein Regelsatz, der eine solche Regelaktion enthält, gilt als ungültig. Dies würde zu einem Laufzeitfehler führen, und es werden keine Ausgabeansprüche erstellt.

  4. Wenn eine Regelaktion auf einen Bezeichner verweist, der nicht im Abschnitt "Bedingungsliste auswählen" der Regel verwendet wurde, handelt es sich um eine ungültige Verwendung. Dies würde zu einem Syntaxfehler führen.

    Beispiel: Falscher Bezeichnerverweis In der folgenden Regel wird ein falscher Bezeichner veranschaulicht, der in Regelaktionen verwendet wird.

    C1:[] => Issue (claim = C2);
    

Beispieltransformationsregeln

  • Alle Ansprüche eines bestimmten Typs zulassen

    Exakter Typ

    C1:[type=="XYZ"] => Issue (claim = C1);
    

    Verwenden von Regex

    C1: [type =~ "XYZ*"] => Issue (claim = C1);
    
  • Zulassen eines bestimmten Anspruchstyps Exakter Typ

    C1:[type != "XYZ"] => Issue (claim=C1);
    

    Verwenden von Regex

    C1:[Type !~ "XYZ?"] => Issue (claim=C1);
    

Beispiele für Regeln parserfehler

Anspruchstransformationsregeln werden von einem benutzerdefinierten Parser analysiert, um nach Syntaxfehlern zu überprüfen. Dieser Parser wird durch verwandte Windows PowerShell Cmdlets ausgeführt, bevor Regeln in Active Directory gespeichert werden. Alle Fehler beim Analysieren der Regeln, einschließlich Syntaxfehlern, werden auf der Konsole gedruckt. Domänencontroller führen auch den Parser aus, bevor sie die Regeln zum Transformieren von Ansprüchen verwenden, und sie melden Fehler im Ereignisprotokoll an (Hinzufügen von Ereignisprotokollnummern).

In diesem Abschnitt werden einige Beispiele für Regeln veranschaulicht, die mit falscher Syntax und den entsprechenden Syntaxfehlern geschrieben werden, die vom Parser generiert werden.

  1. Beispiel:

    c1;[]=>Issue(claim=c1);
    

    In diesem Beispiel wird ein falsch verwendetes Semikolon anstelle eines Doppelpunkts verwendet. Fehlermeldung:POLICY0002: Richtliniendaten konnten nicht analysiert werden.Zeilennummer: 1, Spaltennummer: 2, Fehlertoken: ;. Zeile: 'c1; []=Issue(claim=>c1);'.Parserfehler: 'POLICY0030: Syntaxfehler, unerwartet ';', erwartet eine der folgenden: ':' .'

  2. Beispiel:

    c1:[]=>Issue(claim=c2);
    

    In diesem Beispiel ist das Id-Tag in der Kopiesausstellungsrichtlinie nicht definiert. Fehlermeldung: POLICY0011: Keine Bedingungen in der Anspruchsregel entsprechen dem Bedingungstag, das im CopyIssuanceStatement angegeben ist: "c2".

  3. Beispiel:

    c1:[type=="x1", value=="1", valuetype=="bool"]=>Issue(claim=c1)
    

    "bool" ist kein Terminal in der Sprache, und es ist kein gültiger ValueType. Gültige Terminals werden in der folgenden Fehlermeldung aufgeführt. Fehlermeldung:POLICY0002: Richtliniendaten konnten nicht analysiert werden. Zeilennummer: 1, Spaltennummer: 39, Fehlertoken: "bool". Zeile: 'c1:[type=="x1", wert=="1",valuetype=="bool"]=Issue(claim=>c1);'. Parserfehler: 'POLICY0030: Syntaxfehler, unerwartete 'STRING', erwartet eine der folgenden: 'INT64_TYPE' 'UINT64_TYPE' 'STRING_TYPE' 'BOOLEAN_TYPE' 'ID'

  4. Beispiel:

    c1:[type=="x1", value==1, valuetype=="boolean"]=>Issue(claim=c1);
    

    Die Zahl 1 in diesem Beispiel ist kein gültiges Token in der Sprache, und diese Verwendung ist in einer übereinstimmenden Bedingung nicht zulässig. Es muss in doppelte Anführungszeichen eingeschlossen werden, um es zu einer Zeichenfolge zu machen. Fehlermeldung:POLICY0002: Richtliniendaten konnten nicht analysiert werden.Zeilennummer: 1, Spaltennummer: 23, Fehlertoken: 1. Zeile: 'c1:[type=="x1", Wert==1, Valuetype=="bool"]=Issue(claim=>c1);'.Parserfehler: 'POLICY0029: Unerwartete Eingabe.

  5. Beispiel:

    c1:[type == "x1", value == "1", valuetype == "boolean"] =>
    
         Issue(type = c1.type, value="0", valuetype == "boolean");
    

    In diesem Beispiel wird anstelle eines einzelnen Gleichheitszeichens (=) ein doppeltes Gleichheitszeichen (=) verwendet. Fehlermeldung:POLICY0002: Richtliniendaten konnten nicht analysiert werden.Zeilennummer: 1, Spaltennummer: 91, Fehlertoken: ==. Zeile: 'c1:[type=="x1", wert=="1",valuetype=="boolean"]>=Issue(type=c1.type, value="0", valuetype=="boolean");''.Parserfehler: 'POLICY0030: Syntaxfehler, unerwartet '==', erwartet eine der folgenden: '='.

  6. Beispiel:

    c1:[type=="x1", value=="boolean", valuetype=="string"] =>
    
          Issue(type=c1.type, value=c1.value, valuetype = "string");
    

    Dieses Beispiel ist syntactisch und semantisch korrekt. Die Verwendung von "boolean" als Zeichenfolgenwert ist jedoch gebunden, um Verwirrung zu verursachen, und es sollte vermieden werden. Wie zuvor erwähnt, sollte die Verwendung von Sprachterminals als Anspruchswerte möglichst vermieden werden.

Sprachterminals

In der folgenden Tabelle sind die vollständigen Zeichenfolgen und die zugeordneten Sprachterminals aufgeführt, die in der Sprache der Anspruchstransformationsregeln verwendet werden. Diese Definitionen verwenden groß-insensitive UTF-16-Zeichenfolgen.

Zeichenfolge Terminal
"=>" BEDEUTEN
";" SEMIKOLON
":" DOPPELPUNKT
"," KOMMA
"." PUNKT
"[" O_SQ_BRACKET
"]" C_SQ_BRACKET
"(" O_BRACKET
")" C_BRACKET
"==" EQ
"!=" NEQ
"=~" REGEXP_MATCH
"!~" REGEXP_NOT_MATCH
"=" ZUWEISEN
"&&" AND
"Problem" PROBLEM
"type" TYPE
"Wert" WERT
"Valuetype" VALUE_TYPE
"Anspruch" ANSPRUCH
"[_A-Za-z][_A-Za-z0-9]*" IDENTIFIER
"\"[^\"\n]*\"" STRING
"uint64" UINT64_TYPE
"int64" INT64_TYPE
„String“ STRING_TYPE
"boolescher" BOOLEAN_TYPE

Sprachsyntax

Die folgende Anspruchstransformationsregelnsprache wird in ABNF-Formular angegeben. Diese Definition verwendet die Terminals, die in der vorherigen Tabelle angegeben werden, zusätzlich zu den hier definierten ABNF-Produktionen. Die Regeln müssen in UTF-16 codiert werden, und die Zeichenfolgenvergleiche müssen als Groß-/Kleinschreibung behandelt werden.

Rule_set        = ;/*Empty*/
             / Rules
Rules         = Rule
             / Rule Rules
Rule          = Rule_body
Rule_body       = (Conditions IMPLY Rule_action SEMICOLON)
Conditions       = ;/*Empty*/
             / Sel_condition_list
Sel_condition_list   = Sel_condition
             / (Sel_condition_list AND Sel_condition)
Sel_condition     = Sel_condition_body
             / (IDENTIFIER COLON Sel_condition_body)
Sel_condition_body   = O_SQ_BRACKET Opt_cond_list C_SQ_BRACKET
Opt_cond_list     = /*Empty*/
             / Cond_list
Cond_list       = Cond
             / (Cond_list COMMA Cond)
Cond          = Value_cond
             / Type_cond
Type_cond       = TYPE Cond_oper Literal_expr
Value_cond       = (Val_cond COMMA Val_type_cond)
             /(Val_type_cond COMMA Val_cond)
Val_cond        = VALUE Cond_oper Literal_expr
Val_type_cond     = VALUE_TYPE Cond_oper Value_type_literal
claim_prop       = TYPE
             / VALUE
Cond_oper       = EQ
             / NEQ
             / REGEXP_MATCH
             / REGEXP_NOT_MATCH
Literal_expr      = Literal
             / Value_type_literal

Expr          = Literal
             / Value_type_expr
             / (IDENTIFIER DOT claim_prop)
Value_type_expr    = Value_type_literal
             /(IDENTIFIER DOT VALUE_TYPE)
Value_type_literal   = INT64_TYPE
             / UINT64_TYPE
             / STRING_TYPE
             / BOOLEAN_TYPE
Literal        = STRING
Rule_action      = ISSUE O_BRACKET Issue_params C_BRACKET
Issue_params      = claim_copy
             / claim_new
claim_copy       = CLAIM ASSIGN IDENTIFIER
claim_new       = claim_prop_assign_list
claim_prop_assign_list = (claim_value_assign COMMA claim_type_assign)
             /(claim_type_assign COMMA claim_value_assign)
claim_value_assign   = (claim_val_assign COMMA claim_val_type_assign)
             /(claim_val_type_assign COMMA claim_val_assign)
claim_val_assign    = VALUE ASSIGN Expr
claim_val_type_assign = VALUE_TYPE ASSIGN Value_type_expr
Claim_type_assign   = TYPE ASSIGN Expr