IdUCB1ValidationGeneral.java
/*
* Copyright © 2023 Mark Raynsford <code@io7m.com> https://www.io7m.com
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package com.io7m.idstore.protocol.user.cb.internal;
import com.io7m.cedarbridge.runtime.api.CBIntegerUnsigned32;
import com.io7m.cedarbridge.runtime.api.CBIntegerUnsigned8;
import com.io7m.cedarbridge.runtime.api.CBList;
import com.io7m.cedarbridge.runtime.api.CBOptionType;
import com.io7m.cedarbridge.runtime.api.CBString;
import com.io7m.cedarbridge.runtime.api.CBUUID;
import com.io7m.cedarbridge.runtime.convenience.CBLists;
import com.io7m.cedarbridge.runtime.time.CBOffsetDateTime;
import com.io7m.idstore.model.IdEmail;
import com.io7m.idstore.model.IdName;
import com.io7m.idstore.model.IdNonEmptyList;
import com.io7m.idstore.model.IdPassword;
import com.io7m.idstore.model.IdPasswordAlgorithms;
import com.io7m.idstore.model.IdPasswordException;
import com.io7m.idstore.model.IdRealName;
import com.io7m.idstore.model.IdUser;
import com.io7m.idstore.protocol.api.IdProtocolException;
import com.io7m.idstore.protocol.user.cb.IdU1Password;
import com.io7m.idstore.protocol.user.cb.IdU1TimestampUTC;
import com.io7m.idstore.protocol.user.cb.IdU1User;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Map;
import java.util.Optional;
import static com.io7m.idstore.error_codes.IdStandardErrorCodes.PROTOCOL_ERROR;
/**
* Functions to translate between the core command set and the User v1
* Cedarbridge encoding command set.
*/
public final class IdUCB1ValidationGeneral
{
private IdUCB1ValidationGeneral()
{
}
public static IdNonEmptyList<IdEmail> fromWireEmails(
final CBList<CBString> fieldEmails)
throws IdProtocolException
{
final var es = fieldEmails.values();
if (es.isEmpty()) {
throw new IdProtocolException(
"Admin emails list is empty!",
PROTOCOL_ERROR,
Map.of(),
Optional.of("Provide at least one admin email address.")
);
}
final var emails = new ArrayList<>(fieldEmails.values());
final var email0 = new IdEmail(emails.remove(0).value());
return new IdNonEmptyList<>(
email0,
emails.stream().map(CBString::value).map(IdEmail::new).toList()
);
}
public static IdPassword fromWirePassword(
final IdU1Password fieldPassword)
throws IdPasswordException
{
return new IdPassword(
IdPasswordAlgorithms.parse(fieldPassword.fieldAlgorithm().value()),
fieldPassword.fieldHash().value(),
fieldPassword.fieldSalt().value(),
fieldPassword.fieldExpires().asOptional().map(CBOffsetDateTime::value)
);
}
public static OffsetDateTime fromWireTimestamp(
final IdU1TimestampUTC t)
{
return OffsetDateTime.of(
(int) (t.fieldYear().value() & 0xffffffffL),
t.fieldMonth().value(),
t.fieldDay().value(),
t.fieldHour().value(),
t.fieldMinute().value(),
t.fieldSecond().value(),
(int) (t.fieldMillisecond().value() * 1000L),
ZoneOffset.UTC
);
}
public static IdU1TimestampUTC toWireTimestamp(
final OffsetDateTime t)
{
return new IdU1TimestampUTC(
new CBIntegerUnsigned32(Integer.toUnsignedLong(t.getYear())),
new CBIntegerUnsigned8(t.getMonthValue()),
new CBIntegerUnsigned8(t.getDayOfMonth()),
new CBIntegerUnsigned8(t.getHour()),
new CBIntegerUnsigned8(t.getMinute()),
new CBIntegerUnsigned8(t.getSecond()),
new CBIntegerUnsigned32(Integer.toUnsignedLong(t.getNano() / 1000))
);
}
public static IdU1Password toWirePassword(
final IdPassword password)
{
return new IdU1Password(
new CBString(password.algorithm().identifier()),
new CBString(password.hash()),
new CBString(password.salt()),
CBOptionType.fromOptional(password.expires().map(CBOffsetDateTime::new))
);
}
public static IdU1User toWireUser(
final IdUser user)
{
return new IdU1User(
new CBUUID(user.id()),
new CBString(user.idName().value()),
new CBString(user.realName().value()),
toWireEmails(user.emails()),
toWireTimestamp(user.timeCreated()),
toWireTimestamp(user.timeUpdated()),
toWirePassword(user.password())
);
}
private static CBList<CBString> toWireEmails(
final IdNonEmptyList<IdEmail> emails)
{
return CBLists.ofCollection(emails.toList(), e -> new CBString(e.value()));
}
public static IdUser fromWireUser(
final IdU1User user)
throws IdProtocolException, IdPasswordException
{
return new IdUser(
user.fieldId().value(),
new IdName(user.fieldIdName().value()),
new IdRealName(user.fieldRealName().value()),
fromWireEmails(user.fieldEmails()),
fromWireTimestamp(user.fieldTimeCreated()),
fromWireTimestamp(user.fieldTimeUpdated()),
fromWirePassword(user.fieldPassword())
);
}
}