chromium/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/CompromisedCredential.java

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.password_check;

import android.os.Parcel;
import android.os.Parcelable;

import org.jni_zero.CalledByNative;

import org.chromium.url.GURL;

import java.util.Objects;

/**
 * This class holds the data used to represent a compromised credential in the Password Check
 * settings screen.
 */
public class CompromisedCredential implements Parcelable {
    /** This static member is required to automagically deserialize credential parcels . */
    public static final Parcelable.Creator<CompromisedCredential> CREATOR =
            new Parcelable.Creator<CompromisedCredential>() {
                @Override
                public CompromisedCredential createFromParcel(Parcel in) {
                    final String signonRealm = in.readString();
                    final GURL associatedUrl = GURL.deserialize(in.readString());
                    final String username = in.readString();
                    final String displayOrigin = in.readString();
                    final String displayUsername = in.readString();
                    final String password = in.readString();
                    final String passwordChangeUrl = in.readString();
                    final String associatedApp = in.readString();
                    final long creationTime = in.readLong();
                    final long lastUsedTime = in.readLong();
                    boolean[] boolArguments = new boolean[4];
                    in.readBooleanArray(boolArguments);
                    final boolean leaked = boolArguments[0];
                    final boolean phished = boolArguments[1];

                    return new CompromisedCredential(
                            signonRealm,
                            associatedUrl,
                            username,
                            displayOrigin,
                            displayUsername,
                            password,
                            passwordChangeUrl,
                            associatedApp,
                            creationTime,
                            lastUsedTime,
                            leaked,
                            phished);
                }

                @Override
                public CompromisedCredential[] newArray(int size) {
                    return new CompromisedCredential[size];
                }
            };

    private final String mSignonRealm;
    private final GURL mAssociatedUrl;
    private final String mUsername;
    private final String mDisplayOrigin;
    private final String mDisplayUsername;
    private final String mPassword;
    private final String mPasswordChangeUrl;
    private final String mAssociatedApp;
    private final long mCreationTime;
    private final long mLastUsedTime;
    private final boolean mOnlyLeaked;
    private final boolean mOnlyPhished;

    /**
     * @param signonRealm The URL leading to the sign-on page.
     * @param associatedUrl The associated URL to this credential, for android
     *         credentials without affiliation information it will be empty.
     * @param username The name used to identify this credential (may be empty).
     * @param displayOrigin The origin displayed to the user. Not necessarily a valid URL (e.g.
     *         missing scheme).
     * @param displayUsername The username displayed to the user (substituted if empty).
     * @param password The compromised password.
     * @param passwordChangeUrl A URL that links to the password change form of the affected site.
     * @param associatedApp The associated app if the password originates from it.
     * @param creationTime The time at which the compromised credential was created, which is the
     *        time at which the compromised credential was first found to be compromised during
     *        a check.
     * @param lastUsedTime The time when compromised credential was last time used.
     * @param isOnlyLeaked True if the credential was (only) discovered to be in a leak.
     * @param isOnlyPhished True if the credential was (only) entered on an unsafe site.
     */
    public CompromisedCredential(
            String signonRealm,
            GURL associatedUrl,
            String username,
            String displayOrigin,
            String displayUsername,
            String password,
            String passwordChangeUrl,
            String associatedApp,
            long creationTime,
            long lastUsedTime,
            boolean isOnlyLeaked,
            boolean isOnlyPhished) {
        assert associatedUrl != null
                : "Credential associated URL is null! Pass an empty one instead.";
        assert signonRealm != null;
        assert passwordChangeUrl != null : "Change URL may be empty but not null!";
        assert associatedApp != null : "App package name may be empty but not null!";
        assert !passwordChangeUrl.isEmpty() || !associatedApp.isEmpty()
                : "Change URL and app name may not be empty at the same time!";
        mSignonRealm = signonRealm;
        mAssociatedUrl = associatedUrl;
        mUsername = username;
        mDisplayOrigin = displayOrigin;
        mDisplayUsername = displayUsername;
        mPassword = password;
        mPasswordChangeUrl = passwordChangeUrl;
        mAssociatedApp = associatedApp;
        mCreationTime = creationTime;
        mLastUsedTime = lastUsedTime;
        mOnlyLeaked = isOnlyLeaked;
        mOnlyPhished = isOnlyPhished;
    }

    @CalledByNative
    public String getSignonRealm() {
        return mSignonRealm;
    }

    @CalledByNative
    public String getUsername() {
        return mUsername;
    }

    @CalledByNative
    public GURL getAssociatedUrl() {
        return mAssociatedUrl;
    }

    @CalledByNative
    public String getPassword() {
        return mPassword;
    }

    public String getDisplayUsername() {
        return mDisplayUsername;
    }

    public String getDisplayOrigin() {
        return mDisplayOrigin;
    }

    public String getAssociatedApp() {
        return mAssociatedApp;
    }

    public long getCreationTime() {
        return mCreationTime;
    }

    @CalledByNative
    public long getLastUsedTime() {
        return mLastUsedTime;
    }

    public String getPasswordChangeUrl() {
        return mPasswordChangeUrl;
    }

    public boolean isOnlyLeaked() {
        return mOnlyLeaked;
    }

    public boolean isOnlyPhished() {
        return mOnlyPhished;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        CompromisedCredential that = (CompromisedCredential) o;
        return mSignonRealm.equals(that.mSignonRealm)
                && mAssociatedUrl.equals(that.mAssociatedUrl)
                && mUsername.equals(that.mUsername)
                && mDisplayOrigin.equals(that.mDisplayOrigin)
                && mDisplayUsername.equals(that.mDisplayUsername)
                && mPassword.equals(that.mPassword)
                && mPasswordChangeUrl.equals(that.mPasswordChangeUrl)
                && mAssociatedApp.equals(that.mAssociatedApp)
                && mCreationTime == that.mCreationTime
                && mLastUsedTime == that.mLastUsedTime
                && mOnlyLeaked == that.mOnlyLeaked
                && mOnlyPhished == that.mOnlyPhished;
    }

    @Override
    public String toString() {
        return "CompromisedCredential{"
                + "signonRealm='"
                + mSignonRealm
                + ", associatedUrl='"
                + mAssociatedUrl
                + '\''
                + '\''
                + ", username='"
                + mUsername
                + '\''
                + ", displayOrigin='"
                + mDisplayOrigin
                + '\''
                + ", displayUsername='"
                + mDisplayUsername
                + '\''
                + ", password='"
                + mPassword
                + '\''
                + ", passwordChangeUrl='"
                + mPasswordChangeUrl
                + '\''
                + ", associatedApp='"
                + mAssociatedApp
                + '\''
                + ", creationTime="
                + mCreationTime
                + ". lastUsedTime="
                + mLastUsedTime
                + ", leaked="
                + mOnlyLeaked
                + ", phished="
                + mOnlyPhished
                + '}';
    }

    @Override
    public int hashCode() {
        return Objects.hash(
                mSignonRealm,
                mAssociatedUrl.getPossiblyInvalidSpec(),
                mUsername,
                mDisplayOrigin,
                mDisplayUsername,
                mPassword,
                mPasswordChangeUrl,
                mAssociatedApp,
                mCreationTime,
                mLastUsedTime,
                mOnlyLeaked,
                mOnlyPhished);
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeString(mSignonRealm);
        parcel.writeString(mAssociatedUrl.serialize());
        parcel.writeString(mUsername);
        parcel.writeString(mDisplayOrigin);
        parcel.writeString(mDisplayUsername);
        parcel.writeString(mPassword);
        parcel.writeString(mPasswordChangeUrl);
        parcel.writeString(mAssociatedApp);
        parcel.writeLong(mCreationTime);
        parcel.writeLong(mLastUsedTime);
        parcel.writeBooleanArray(new boolean[] {mOnlyLeaked, mOnlyPhished});
    }

    @Override
    public int describeContents() {
        return 0; // No file descriptor necessary.
    }
}