mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Showing censored values as a grey cell
Test Plan: Block read access to column A based on the condition rec.B == 1. Then setting B = 1 in a row makes the cell under A grey. Reviewers: dsagal Reviewed By: dsagal Subscribers: paulfitz, dsagal Differential Revision: https://phab.getgrist.com/D2828
This commit is contained in:
parent
2feef7f780
commit
2f3a0e0c7f
@ -78,6 +78,27 @@
|
|||||||
background-color: unset;
|
background-color: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.field_clip.invalid.field-error-C {
|
||||||
|
background-color: unset;
|
||||||
|
color: var(--grist-color-dark-grey);
|
||||||
|
padding-left: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field_clip.invalid.field-error-C::before {
|
||||||
|
/* based on standard icon styles */
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
left: 2px;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
background-color: var(--grist-color-dark-grey);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-position: center;
|
||||||
|
-webkit-mask-size: contain;
|
||||||
|
-webkit-mask-image: var(--icon-Lock);
|
||||||
|
}
|
||||||
|
|
||||||
.field_clip.field-error-U {
|
.field_clip.field-error-U {
|
||||||
color: #6363a2;
|
color: #6363a2;
|
||||||
background-color: unset;
|
background-color: unset;
|
||||||
|
@ -152,7 +152,19 @@ export class FieldEditor extends Disposable {
|
|||||||
|
|
||||||
const column = this._field.column();
|
const column = this._field.column();
|
||||||
const cellCurrentValue = this._editRow.cells[this._field.colId()].peek();
|
const cellCurrentValue = this._editRow.cells[this._field.colId()].peek();
|
||||||
const cellValue = column.isFormula() ? column.formula() : cellCurrentValue;
|
let cellValue: CellValue;
|
||||||
|
if (column.isFormula()) {
|
||||||
|
cellValue = column.formula();
|
||||||
|
} else if (Array.isArray(cellCurrentValue) && cellCurrentValue[0] === 'C') {
|
||||||
|
// This cell value is censored by access control rules
|
||||||
|
// Really the rules should also block editing, but in case they don't, show a blank value
|
||||||
|
// rather than a 'C'. However if the user tries to edit the cell and then clicks away
|
||||||
|
// without typing anything the empty string is saved, deleting what was there.
|
||||||
|
// We should probably just automatically block updates where reading is not allowed.
|
||||||
|
cellValue = '';
|
||||||
|
} else {
|
||||||
|
cellValue = cellCurrentValue;
|
||||||
|
}
|
||||||
|
|
||||||
// Enter formula-editing mode (e.g. click-on-column inserts its ID) only if we are opening the
|
// Enter formula-editing mode (e.g. click-on-column inserts its ID) only if we are opening the
|
||||||
// editor by typing into it (and overriding previous formula). In other cases (e.g. double-click),
|
// editor by typing into it (and overriding previous formula). In other cases (e.g. double-click),
|
||||||
|
@ -109,6 +109,19 @@ export class SkipValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A placeholder for a value hidden by access control rules.
|
||||||
|
* Depending on the types of the columns involved, copying
|
||||||
|
* a censored value and pasting elsewhere will either use
|
||||||
|
* CensoredValue.__repr__ (python) or CensoredValue.toString (typescript)
|
||||||
|
* so they should match
|
||||||
|
*/
|
||||||
|
export class CensoredValue {
|
||||||
|
public toString() {
|
||||||
|
return 'CENSORED';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces a Grist-encoded version of the value, e.g. turning a Date into ['d', timestamp].
|
* Produces a Grist-encoded version of the value, e.g. turning a Date into ['d', timestamp].
|
||||||
* Returns ['U', repr(value)] if it fails to encode otherwise.
|
* Returns ['U', repr(value)] if it fails to encode otherwise.
|
||||||
@ -135,6 +148,8 @@ export function encodeObject(value: unknown): CellValue {
|
|||||||
// TODO Depending on how it's used, may want to return ['d', timestamp] for UTC midnight.
|
// TODO Depending on how it's used, may want to return ['d', timestamp] for UTC midnight.
|
||||||
return ['D', timestamp, 'UTC'];
|
return ['D', timestamp, 'UTC'];
|
||||||
}
|
}
|
||||||
|
} else if (value instanceof CensoredValue) {
|
||||||
|
return ['C'];
|
||||||
} else if (value instanceof RaisedException) {
|
} else if (value instanceof RaisedException) {
|
||||||
return ['E', value.name, value.message, value.details];
|
return ['E', value.name, value.message, value.details];
|
||||||
} else if (Array.isArray(value)) {
|
} else if (Array.isArray(value)) {
|
||||||
@ -172,6 +187,7 @@ export function decodeObject(value: CellValue): unknown {
|
|||||||
case 'P': return new PendingValue();
|
case 'P': return new PendingValue();
|
||||||
case 'R': return new Reference(String(args[0]), args[1]);
|
case 'R': return new Reference(String(args[0]), args[1]);
|
||||||
case 'S': return new SkipValue();
|
case 'S': return new SkipValue();
|
||||||
|
case 'C': return new CensoredValue();
|
||||||
case 'U': return new UnknownValue(args[0]);
|
case 'U': return new UnknownValue(args[0]);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -888,9 +888,9 @@ export class GranularAccess implements GranularAccessForBundle {
|
|||||||
if (colValues === undefined) {
|
if (colValues === undefined) {
|
||||||
censorAt = () => 1;
|
censorAt = () => 1;
|
||||||
} else if (Array.isArray(action[2])) {
|
} else if (Array.isArray(action[2])) {
|
||||||
censorAt = (colId, idx) => (colValues as BulkColValues)[colId][idx] = 'CENSORED'; // TODO Pick a suitable value
|
censorAt = (colId, idx) => (colValues as BulkColValues)[colId][idx] = ['C']; // censored
|
||||||
} else {
|
} else {
|
||||||
censorAt = (colId) => (colValues as ColValues)[colId] = 'CENSORED'; // TODO Pick a suitable value
|
censorAt = (colId) => (colValues as ColValues)[colId] = ['C']; // censored
|
||||||
}
|
}
|
||||||
|
|
||||||
// These map an index of a row in the action to its index in rowsBefore and in rowsAfter.
|
// These map an index of a row in the action to its index in rowsBefore and in rowsAfter.
|
||||||
|
@ -105,6 +105,17 @@ class UnmarshallableValue(object):
|
|||||||
# document was just migrated.
|
# document was just migrated.
|
||||||
_pending_sentinel = object()
|
_pending_sentinel = object()
|
||||||
|
|
||||||
|
# A placeholder for a value hidden by access control rules.
|
||||||
|
# Depending on the types of the columns involved, copying
|
||||||
|
# a censored value and pasting elsewhere will either use
|
||||||
|
# CensoredValue.__repr__ (python) or CensoredValue.toString (typescript)
|
||||||
|
# so they should match
|
||||||
|
class CensoredValue(object):
|
||||||
|
def __repr__(self):
|
||||||
|
return 'CENSORED'
|
||||||
|
|
||||||
|
_censored_sentinel = CensoredValue()
|
||||||
|
|
||||||
|
|
||||||
_max_js_int = 1<<31
|
_max_js_int = 1<<31
|
||||||
|
|
||||||
@ -178,6 +189,8 @@ def encode_object(value):
|
|||||||
return ['O', {key: encode_object(val) for key, val in value.iteritems()}]
|
return ['O', {key: encode_object(val) for key, val in value.iteritems()}]
|
||||||
elif value == _pending_sentinel:
|
elif value == _pending_sentinel:
|
||||||
return ['P']
|
return ['P']
|
||||||
|
elif value == _censored_sentinel:
|
||||||
|
return ['C']
|
||||||
elif isinstance(value, UnmarshallableValue):
|
elif isinstance(value, UnmarshallableValue):
|
||||||
return ['U', value.value_repr]
|
return ['U', value.value_repr]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -218,6 +231,8 @@ def decode_object(value):
|
|||||||
return {decode_object(key): decode_object(val) for key, val in args[0].iteritems()}
|
return {decode_object(key): decode_object(val) for key, val in args[0].iteritems()}
|
||||||
elif code == 'P':
|
elif code == 'P':
|
||||||
return _pending_sentinel
|
return _pending_sentinel
|
||||||
|
elif code == 'C':
|
||||||
|
return _censored_sentinel
|
||||||
elif code == 'U':
|
elif code == 'U':
|
||||||
return UnmarshallableValue(args[0])
|
return UnmarshallableValue(args[0])
|
||||||
raise KeyError("Unknown object type code %r" % code)
|
raise KeyError("Unknown object type code %r" % code)
|
||||||
|
Loading…
Reference in New Issue
Block a user