Skip to content

Commit

Permalink
Add 'visibility' field to Places and Domains.
Browse files Browse the repository at this point in the history
    Currently default to "OPEN" but allow "PRIVATE"
  • Loading branch information
Misterblue committed Mar 22, 2021
1 parent 6ad6fde commit cd62e04
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 19 deletions.
2 changes: 2 additions & 0 deletions docs/API-Domains.md
Expand Up @@ -23,6 +23,7 @@ A request returns an array of domain descriptions:
"domainId": stringDomainId,
"id": stringDomainId,
"name": stringName,
"visibility": string, // one of 'open', 'friends', 'connections', 'private'
"label": stringName,
"public_key": stringPublicKey,
"sponsor_account_id": stringAccountIdAssociated,
Expand Down Expand Up @@ -136,6 +137,7 @@ The domain fields that can be fetched:
| --------- | -------- | -------- | ---- |
| domainId | all | noone |string |
| name | all | domain sponsor admin | string |
| visibility | all | domain sponsor admin | string |
| public_key | all | domain | string |
| sponsor_account_id | all | domain sponsor admin | string |
| version | all | domain | string |
Expand Down
45 changes: 36 additions & 9 deletions docs/API-Explore.md
Expand Up @@ -6,6 +6,12 @@ This request returns Place information that is compatible with this legacy dialo
There are some additional fields providing new information that could be used
by future scripts.

Since this mimics the legacy 'Explore' request, the returned Place data gives
the name of the listed Place as "Domain name".
The "Domain name" field will have the same value as "Place.name".
The "DomainId", though, is the ID of the domain that Place is in and additional
Domain information is given under "Place.

## GET /explore.json

This request takes a number of parameters to control the list of places returned.
Expand All @@ -16,28 +22,49 @@ This request takes a number of parameters to control the list of places returned
| tag | get places that have specified tags (comma separated list. Any tag matches) |
| per_page | number of entries to return per request |
| page_num | which "page" of entries to return |
| order | comma separated list of 'ascending', 'decending', 'num_users', 'name' |
| search | search on the place name. ( "search=fred" will return all Places whos name includes "fred") |
| status | comma separated list of 'online' (domain is heartbeating), 'active' (has attendees) |

So, a legal request could be:

```
GET /explore.json?per_page=20&page_num=4
GET /explore.json?maturity=adult
GET /explore.json?order=ascending,num_users&maturity=unrated
GET /explore.json?tag=friendly,kids,sandbox
The response is a JSON array of Place descriptions.
```
[
{
"Domain name": "place name",
"Address": "domain-network-address/float,float,float/float,float,float,float",
"Visit": "hifi://domain-network-address/float,float,float/float,float,float,float",
"DomainId": "domainId",
"Network Address": "domain network address",
"Network Port": "domain network port",
"Owner": "account name of domain owner",
"People": number,
"Place": { place information as defined in [Places](./API-Places.md) }
"Domain name": "place name",
"Address": "domain-network-address/float,float,float/float,float,float,float",
"Visit": "hifi://domain-network-address/float,float,float/float,float,float,float",
"DomainId": "domainId",
"Network Address": "domain network address",
"Network Port": "domain network port",
"Owner": "account name of domain owner",
"People": number,
"Place": {
"placeId': string,
"id': string,
"name': "place name",
"visibility': string, // one of "open", "friends", "connections", "private"
"path": "/float,float,float/float,float,float,float",
"address": "domain-network-address/float,float,float/float,float,float,float",
"description': "place description",
"maturity': string, // one of "everyone", "teen", "mature", "adult", "unrated"
"tags': string[],
"thumbnail': stringURL,
"images': stringURLS[],
// The following fields can be updated in realtime by a script at the Place
"current_attendance': number, // reported attendance at place
"current_images': stringURLS[],
"current_info': string,
"current_last_update_time': "ISODateString"
},
},
...
]
Expand Down
2 changes: 2 additions & 0 deletions docs/API-Places.md
Expand Up @@ -89,6 +89,7 @@ This request return JSON formatted as:
{
"placeId": string,
"name": string,
"visibility": string, // one of 'open', 'friends', 'connections', 'private'
"path": string,
"address": string,
"description": string,
Expand Down Expand Up @@ -226,6 +227,7 @@ The place fields that can be fetched:
| placeId | all | none | string |
| name | all | domainOwner, admin | string |
| description | all | domainOwner, admin | string |
| visibility | all | domainOwner, admin | string |
| domainId | all | none | string |
| maturity | all | domainOwner, admin | string |
| tags | all | domainOwner, admin | stringArray |
Expand Down
1 change: 1 addition & 0 deletions src/Entities/DomainEntity.ts
Expand Up @@ -21,6 +21,7 @@ import { Entity } from '@Entities/Entity';
export class DomainEntity implements Entity {
public id: string; // globally unique domain identifier
public name: string; // domain name/label
public visibility: string; // visibility of this entry in general domain lists
public publicKey: string; // DomainServers's public key in multi-line PEM format
public apiKey: string; // Access key if a temp domain
public sponsorAccountId: string; // The account that gave this domain an access key
Expand Down
14 changes: 14 additions & 0 deletions src/Entities/DomainFields.ts
Expand Up @@ -80,6 +80,20 @@ export const DomainFields: { [key: string]: FieldDefn } = {
setter: noOverwriteSetter,
getter: simpleGetter
},
'visiblity': {
entity_field: 'visiblity',
request_field_name: 'visiblity',
get_permissions: [ Perm.ALL ],
set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ],
validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise<ValidateResponse> => {
if(typeof(pVal) === 'string' && Visibility.KnownVisibility(pVal)) {
return { valid: true };
}
return { valid: false, reason: 'not accepted visibility value'};
},
setter: simpleSetter,
getter: simpleGetter
},
// An alternate way of setting domain name
'world_name': {
entity_field: 'name',
Expand Down
1 change: 0 additions & 1 deletion src/Entities/EntityFilters/AccountScopeFilter.ts
Expand Up @@ -18,7 +18,6 @@ import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter';
import { Accounts } from '@Entities/Accounts';
import { AccountEntity } from '@Entities/AccountEntity';
import { Logger } from '@Tools/Logging';
import { ParseQueryString } from '@Tools/Misc';

// AccountScopeFilter filters a query stream to the accounts the requestor
// can look at. That is, a person can normally see only the domains
Expand Down
12 changes: 8 additions & 4 deletions src/Entities/EntityFilters/PlaceFilterInfo.ts
Expand Up @@ -19,6 +19,7 @@ import { PlaceEntity } from '@Entities/PlaceEntity';

import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter';
import { Maturity } from '@Entities/Sets/Maturity';
import { Visibility } from '@Entities/Sets/Visibility';

import { VKeyedCollection } from '@Tools/vTypes';
import { Logger } from '@Tools/Logging';
Expand Down Expand Up @@ -179,14 +180,17 @@ export class PlaceFilterInfo extends CriteriaFilter {
this._doingQuery = true;
const criteria:VKeyedCollection = {};
if (this._maturity) {
criteria.maturity = { '$in': this._maturity }
criteria['maturity'] = { '$in': this._maturity }
};
if (this._tags) {
criteria.tags = { '$in': this._tags }
criteria['tags'] = { '$in': this._tags }
};
if (this._search) {
criteria.name = { '$regex': this._search, '$options': 'i' }
}
criteria['name'] = { '$regex': this._search, '$options': 'i' }
};
criteria['visibility'] = { "$or": [ { "visibility": { "$exists": false }},
{ "visibility": Visibility.OPEN },
] };
return criteria;
};

Expand Down
1 change: 1 addition & 0 deletions src/Entities/PlaceEntity.ts
Expand Up @@ -22,6 +22,7 @@ export class PlaceEntity implements Entity {
public id: string; // globally unique place identifier
public name: string; // Human friendly name of the place
public description: string; // Human friendly description of the place
public visibility: string; // visibility of this Place in general Place lists
public maturity: string; // maturity level of the place (see Sets/Maturity.ts)
public tags: string[]; // tags defining the string content
public domainId: string; // domain the place is in
Expand Down
20 changes: 17 additions & 3 deletions src/Entities/PlaceFields.ts
Expand Up @@ -22,6 +22,7 @@ import { Domains } from '@Entities/Domains';
import { Places } from '@Entities/Places';
import { AuthToken } from '@Entities/AuthToken';
import { Maturity } from '@Entities/Sets/Maturity';
import { Visibility } from '@Entities/Sets/Visibility';

import { Perm } from '@Route-Tools/Perm';
import { checkAccessToEntity } from '@Route-Tools/Permissions';
Expand All @@ -33,7 +34,6 @@ import { simpleGetter, simpleSetter, noSetter, sArraySetter, dateStringGetter }
import { IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc';

import { Logger } from '@Tools/Logging';
import { Tokens } from './Tokens';

// Naming and access for the fields in a PlaceEntity.
// Indexed by request_field_name.
Expand Down Expand Up @@ -77,16 +77,30 @@ export const placeFields: { [key: string]: FieldDefn } = {
entity_field: 'description',
request_field_name: 'description',
get_permissions: [ Perm.ALL ],
set_permissions: [ Perm.DOMAIN, Perm.OWNER, Perm.ADMIN ],
set_permissions: [ Perm.DOMAINACCESS, Perm.ADMIN ],
validate: isStringValidator,
setter: simpleSetter,
getter: simpleGetter
},
'visiblity': {
entity_field: 'visiblity',
request_field_name: 'visiblity',
get_permissions: [ Perm.ALL ],
set_permissions: [ Perm.DOMAINACCESS, Perm.ADMIN ],
validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise<ValidateResponse> => {
if(typeof(pVal) === 'string' && Visibility.KnownVisibility(pVal)) {
return { valid: true };
}
return { valid: false, reason: 'not accepted visibility value'};
},
setter: simpleSetter,
getter: simpleGetter
},
'domainId': {
entity_field: 'domainId',
request_field_name: 'domainId',
get_permissions: [ Perm.ALL ],
set_permissions: [ Perm.OWNER, Perm.ADMIN ],
set_permissions: [ Perm.DOMAINACCESS, Perm.ADMIN ],
validate: async (pField: FieldDefn, pEntity: Entity, pVal: any, pAuth: AuthToken): Promise<ValidateResponse> => {
// This is setting a place to a new domainId. Make sure the domain exists
// and requestor has access to that domain.
Expand Down
2 changes: 0 additions & 2 deletions src/Entities/Sets/Restriction.ts
Expand Up @@ -34,5 +34,3 @@ export class Restriction {
};

};


34 changes: 34 additions & 0 deletions src/Entities/Sets/Visibility.ts
@@ -0,0 +1,34 @@
// Copyright 2021 Vircadia Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict'

export class Visibility {
public static OPEN: string = 'open';
public static FRIENDS: string = 'friends';
public static CONNECTIONS: string = 'connections';
public static PRIVATE: string = 'private';

static VisibilityCategories = [
Visibility.OPEN,
Visibility.FRIENDS,
Visibility.CONNECTIONS,
Visibility.PRIVATE
];

static KnownVisibility(pVisibility: string): boolean {
return this.VisibilityCategories.includes(pVisibility);
};
};

5 changes: 5 additions & 0 deletions src/route-tools/Util.ts
Expand Up @@ -31,7 +31,9 @@ import { createPublicKey } from 'crypto';
import { VKeyedCollection, VKeyValue } from '@Tools/vTypes';
import { IsNotNullOrEmpty } from '@Tools/Misc';
import { Logger } from '@Tools/Logging';

import { Maturity } from '@Entities/Sets/Maturity';
import { Visibility } from '@Entities/Sets/Visibility';

// The public_key is sent as a binary (DER) form of a PKCS1 key.
// To keep backward compatibility, we convert the PKCS1 key into a SPKI key in PEM format
Expand Down Expand Up @@ -126,6 +128,7 @@ export async function buildDomainInfo(pDomain: DomainEntity): Promise<any> {
'id': pDomain.id,
'domainId': pDomain.id,
'name': pDomain.name,
'visibility': pDomain.visibility ?? Visibility.OPEN,
'sponsorAccountId': pDomain.sponsorAccountId,
'label': pDomain.name,
'network_address': pDomain.networkAddr,
Expand All @@ -142,6 +145,7 @@ export async function buildDomainInfoV1(pDomain: DomainEntity): Promise<any> {
'domainId': pDomain.id,
'id': pDomain.id, // legacy
'name': pDomain.name,
'visibility': pDomain.visibility ?? Visibility.OPEN,
'world_name': pDomain.name, // legacy
'label': pDomain.name, // legacy
'public_key': pDomain.publicKey ? createSimplifiedPublicKey(pDomain.publicKey) : undefined,
Expand Down Expand Up @@ -269,6 +273,7 @@ export async function buildPlaceInfoSmall(pPlace: PlaceEntity, pDomain?: DomainE
'placeId': pPlace.id,
'id': pPlace.id,
'name': pPlace.name,
'visibility': pPlace.visibility ?? Visibility.OPEN,
'address': await Places.getAddressString(pPlace),
'path': pPlace.path,
'description': pPlace.description,
Expand Down

0 comments on commit cd62e04

Please sign in to comment.