DEV Community

Rahul
Rahul

Posted on Fully Autonomous

CCIL Test Studio source 12: src/access.js

// Public sample credentials for this browser-local prototype only.
export const demoPasswords = { user: 'team-demo', administrator: 'admin-demo' };
export const defaultTeamAccess = {
  userHash: '5ad5c1934a4602677ed173d3bf0d69aab489aba11309b165f0b68725220fcf47',
  administratorHash: '198352b6a8078a827be267c847d39506629d3af446a3cc0bce670dd3a6b5d753',
};
export async function hashPassword(password) {
  const bytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(password));
  return [...new Uint8Array(bytes)].map(byte => byte.toString(16).padStart(2, '0')).join('');
}
export async function authenticateTeam(state, team, password) {
  if (!state.teams.includes(team) || !password) throw new Error('Choose a team and enter its password.');
  const access = state.teamAccess[team];
  const digest = await hashPassword(password);
  const role = digest === access?.administratorHash ? 'administrator' : digest === access?.userHash ? 'user' : '';
  if (!role) throw new Error('The password does not match the selected team.');
  return { team, role, name: `${team} ${role === 'administrator' ? 'Administrator' : 'User'}`, authenticated: true };
}
export function normalizeAccessModel(state) {
  state.teamAccess ||= {};
  for (const team of state.teams) state.teamAccess[team] ||= { ...defaultTeamAccess };
  const identity = name => typeof name === 'string' ? name.replace(/ (Normal User|Contributor|Reviewer|Baseline Approver|Developer|Business User|BA)$/, ' User') : name;
  state.session.name = `${state.session.team} ${state.session.role === 'administrator' ? 'Administrator' : 'User'}`;
  state.questions.forEach(q => { q.author = identity(q.author); q.resolvedBy = identity(q.resolvedBy); q.validatedBy = identity(q.validatedBy); });
  state.audit.forEach(a => { a.actor = identity(a.actor); a.action = a.action.replace(/ as (Normal User|Contributor|Reviewer|Baseline Approver|Developer|Business User|BA)$/, ' as User'); });
  Object.values(state.threads).forEach(thread => thread.messages?.forEach(m => { m.actor = identity(m.actor); }));
  state.candidates.forEach(c => c.decisions?.forEach(d => { d.actor = identity(d.actor); }));
  if (state.accessModelVersion === 1) return state;
  state.session.role = state.session.role === 'administrator' ? 'administrator' : 'user';
  state.session.authenticated = false;
  state.workspaces.forEach(w => { w.visibility = 'team'; w.members = [w.team]; });
  state.baselines.forEach(b => { if (b.status === 'Approved' && !b.parentId) b.status = 'Active'; });
  state.questions.forEach(q => {
    if (q.status === 'validated' || (q.status === 'submitted' && q.answer && q.sourceReference && (q.answerSourceId || q.sourceId))) q.status = 'resolved';
    else if (q.status === 'submitted') q.status = 'open';
    q.author = identity(q.author); q.resolvedBy = identity(q.validatedBy || q.author);
  });
  state.templates.forEach(t => { t.fields = t.fields.map(f => f === 'Reviewer status' ? 'Review status' : f); });
  state.suites.forEach(s => { if (s.templateSnapshot) s.templateSnapshot.fields = s.templateSnapshot.fields.map(f => f === 'Reviewer status' ? 'Review status' : f); });
  state.accessModelVersion = 1;
  return state;
}
export function documentInputIssues(documents) {
  return documents.length ? [] : ['Upload at least one Business, Technical, or Functional / Bug Information document.'];
}
export function addTeam(state, name, userHash, administratorHash) {
  if (!state.session.authenticated || state.session.role !== 'administrator') throw new Error('Only an Administrator can add a team.');
  if (!name || state.teams.some(team => team.toLowerCase() === name.toLowerCase())) throw new Error('Enter a unique team name.');
  if (![userHash, administratorHash].every(hash => /^[a-f0-9]{64}$/.test(hash)) || userHash === administratorHash) throw new Error('Provide different team and administrator passwords.');
  state.teams.push(name); state.teamAccess[name] = { userHash, administratorHash };
}
export function baselineInputIssues(state, name, documents) {
  return [...(!name.trim() ? ['Enter a System Baseline name.'] : []),
    ...(state.baselines.some(b => b.team === state.session.team && b.ref === name.trim()) ? ['This System Baseline name already exists.'] : []),
    ...documentInputIssues(documents)];
}
export function saveClarification(state, workspace, questionId, answer, sourceId, reference, documents) {
  const q = state.questions.find(q => q.id === questionId && q.workspaceId === workspace.id && q.version === workspace.version);
  if (!q || !answer.trim() || !reference.trim() || !documents.some(d => d.id === sourceId && ['Ready', 'Reviewed locally'].includes(d.status))) throw new Error('Provide an answer, an accessible source, and its revision / location.');
  Object.assign(q, { answer: answer.trim(), answerSourceId: sourceId, sourceReference: reference.trim(), status: 'resolved', author: state.session.name, resolvedBy: state.session.name, answeredAt: new Date().toISOString() });
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)