diff --git a/src/languages/definitions/cedar/cedar.ts b/src/languages/definitions/cedar/cedar.ts new file mode 100644 index 0000000000..c9729bc9a9 --- /dev/null +++ b/src/languages/definitions/cedar/cedar.ts @@ -0,0 +1,161 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { languages } from '../../../editor'; + +export const conf: languages.LanguageConfiguration = { + comments: { + lineComment: '//' + }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'] + ], + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"', notIn: ['string'] } + ], + surroundingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' } + ] +}; + +export const language = { + defaultToken: '', + tokenPostfix: '.cedar', + + // sections + keywords from TextMate grammar + keywords: ['permit', 'forbid', 'when', 'unless', 'in', 'has', 'like', 'if', 'then', 'else', 'is'], + + // principal, action, resource, context + constants: ['principal', 'action', 'resource', 'context'], + + // true, false + booleans: ['true', 'false'], + + // extension functions + functions: ['ip', 'decimal', 'datetime', 'duration'], + + // all methods (methods + ipmethods + decimalmethods + datetimemethods + durationmethods) + methods: [ + 'contains', + 'containsAll', + 'containsAny', + 'isEmpty', + 'getTag', + 'hasTag', + 'isIpv4', + 'isIpv6', + 'isLoopback', + 'isMulticast', + 'isInRange', + 'lessThan', + 'lessThanOrEqual', + 'greaterThan', + 'greaterThanOrEqual', + 'offset', + 'durationSince', + 'toDate', + 'toTime', + 'toMilliseconds', + 'toSeconds', + 'toMinutes', + 'toHours', + 'toDays' + ], + + symbols: /[=!<>|&+\-*\/]+/, + + tokenizer: { + root: [ + // comments + [/\/\/.*$/, 'comment'], + + // annotations: @name( + [/@[_a-zA-Z][_a-zA-Z0-9]*(?=\()/, 'annotation'], + + // ?principal, ?resource + [/\?(?:principal|resource)\b/, 'variable'], + + // method calls: .methodName( + [ + /\.([a-zA-Z][a-zA-Z0-9]*)(?=\()/, + { + cases: { + '$1@methods': 'keyword.function', + '@default': 'identifier' + } + } + ], + + // extension functions: ip(, decimal(, datetime(, duration( + [ + /[a-z][a-zA-Z0-9]*(?=\()/, + { + cases: { + '@functions': 'keyword.function', + '@default': 'identifier' + } + } + ], + + // entity type references with :: before a string: Namespace::Type:: + [ + /[_a-zA-Z][_a-zA-Z0-9]*(?:::[_a-zA-Z][_a-zA-Z0-9]*)*(?=::(?="))/, + 'type.identifier' + ], + + // namespaced types: Namespace::Type + [ + /[_a-zA-Z][_a-zA-Z0-9]*(?:::[_a-zA-Z][_a-zA-Z0-9]*)+/, + 'type.identifier' + ], + + // identifiers, keywords, constants, booleans + [ + /[_a-zA-Z][_a-zA-Z0-9]*/, + { + cases: { + '@keywords': 'keyword', + '@constants': 'variable', + '@booleans': 'constant', + '@default': 'identifier' + } + } + ], + + // integers + [/[1-9][0-9]*|0/, 'number'], + + // whitespace + [/[ \t\r\n]+/, ''], + + // brackets + [/[{}()\[\]]/, '@brackets'], + + // operators + [/@symbols/, 'operator'], + + // strings + [/"([^"\\]|\\.)*$/, 'string.invalid'], + [/"/, { token: 'string.quote', bracket: '@open', next: '@string' }], + + // delimiters + [/[;,.]/, 'delimiter'] + ], + + string: [ + [/[^\\"]+/, 'string'], + [/\\./, 'string.escape'], + [/"/, { token: 'string.quote', bracket: '@close', next: '@pop' }] + ] + } +}; diff --git a/src/languages/definitions/cedar/register.ts b/src/languages/definitions/cedar/register.ts new file mode 100644 index 0000000000..a01cd28efd --- /dev/null +++ b/src/languages/definitions/cedar/register.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerLanguage } from '../_.contribution'; + +registerLanguage({ + id: 'cedar', + extensions: ['.cedar'], + aliases: ['Cedar', 'cedar'], + loader: () => import('./cedar') +}); diff --git a/src/languages/definitions/cedarschema/cedarschema.ts b/src/languages/definitions/cedarschema/cedarschema.ts new file mode 100644 index 0000000000..85685c12ff --- /dev/null +++ b/src/languages/definitions/cedarschema/cedarschema.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { languages } from '../../../editor'; + +export const conf: languages.LanguageConfiguration = { + comments: { + lineComment: '//' + }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'] + ], + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"', notIn: ['string'] } + ], + surroundingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' } + ] +}; + +export const language = { + defaultToken: '', + tokenPostfix: '.cedarschema', + + keywords: ['namespace', 'type', 'entity', 'action', 'appliesTo', 'in', 'enum', 'tags'], + + typeKeywords: ['Long', 'String', 'Bool', 'Set', 'Record', 'Entity', 'Extension'], + + symbols: /[=:?<>]+/, + + tokenizer: { + root: [ + // comments + [/\/\/.*$/, 'comment'], + + // namespace path (e.g. My::Namespace::Path) + [ + /[A-Z_a-z][A-Za-z0-9_]*(?:::[A-Z_a-z][A-Za-z0-9_]*)+/, + 'entity.name.namespace' + ], + + // properties (identifier followed by optional ? then : but not ::) + [/[_a-zA-Z][_a-zA-Z0-9]*(?=\??\s*:(?!:))/, 'variable.property'], + + // identifiers and keywords + [ + /[a-z_][A-Za-z0-9_]*/, + { + cases: { + '@keywords': 'keyword', + '@default': 'identifier' + } + } + ], + + // type identifiers (capitalized) + [ + /[A-Z][A-Za-z0-9_]*/, + { + cases: { + '@typeKeywords': 'type.identifier', + '@default': 'type.identifier' + } + } + ], + + // whitespace + [/[ \t\r\n]+/, ''], + + // brackets + [/[{}()\[\]]/, '@brackets'], + + // operators / symbols + [/@symbols/, 'operator'], + + // optional marker + [/\?/, 'operator'], + + // strings + [/"([^"\\]|\\.)*$/, 'string.invalid'], + [/"/, { token: 'string.quote', bracket: '@open', next: '@string' }], + + // delimiter + [/[;,.]/, 'delimiter'] + ], + + string: [ + [/[^\\"]+/, 'string'], + [/\\./, 'string.escape'], + [/"/, { token: 'string.quote', bracket: '@close', next: '@pop' }] + ] + } +}; diff --git a/src/languages/definitions/cedarschema/register.ts b/src/languages/definitions/cedarschema/register.ts new file mode 100644 index 0000000000..a84d2e9f41 --- /dev/null +++ b/src/languages/definitions/cedarschema/register.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerLanguage } from '../_.contribution'; + +registerLanguage({ + id: 'cedarschema', + extensions: ['.cedarschema'], + aliases: ['Cedar Schema', 'cedarschema'], + loader: () => import('./cedarschema') +}); diff --git a/src/languages/definitions/register.all.ts b/src/languages/definitions/register.all.ts index ff00612225..6fb3523c2d 100644 --- a/src/languages/definitions/register.all.ts +++ b/src/languages/definitions/register.all.ts @@ -9,6 +9,8 @@ import './azcli/register'; import './bat/register'; import './bicep/register'; import './cameligo/register'; +import './cedar/register'; +import './cedarschema/register'; import './clojure/register'; import './coffee/register'; import './cpp/register'; diff --git a/website/src/website/data/home-samples/sample.cedar.txt b/website/src/website/data/home-samples/sample.cedar.txt new file mode 100644 index 0000000000..e7961e2ba6 --- /dev/null +++ b/website/src/website/data/home-samples/sample.cedar.txt @@ -0,0 +1,19 @@ +@id("policy1") +permit ( + principal in Group::"admins", + action == Action::"viewDocument", + resource +) +when { + resource has owner && + resource.owner == principal +}; + +forbid ( + principal, + action == Action::"deleteDocument", + resource +) +unless { + principal in Group::"superadmins" +}; diff --git a/website/src/website/data/home-samples/sample.cedarschema.txt b/website/src/website/data/home-samples/sample.cedarschema.txt new file mode 100644 index 0000000000..706f9a54e1 --- /dev/null +++ b/website/src/website/data/home-samples/sample.cedarschema.txt @@ -0,0 +1,23 @@ +namespace MyApp { + entity User in [Group] { + name: String, + email?: String, + }; + + entity Group in [Group]; + + entity Document { + owner: User, + isPublic: Bool, + }; + + action viewDocument appliesTo { + principal: [User], + resource: [Document], + }; + + action deleteDocument appliesTo { + principal: [User], + resource: [Document], + }; +}